dfu: Introduction of the "dfu_checksum_method" env variable for checksum method setting
[kernel/u-boot.git] / drivers / dfu / dfu.c
1 /*
2  * dfu.c -- DFU back-end routines
3  *
4  * Copyright (C) 2012 Samsung Electronics
5  * author: Lukasz Majewski <l.majewski@samsung.com>
6  *
7  * SPDX-License-Identifier:     GPL-2.0+
8  */
9
10 #include <common.h>
11 #include <errno.h>
12 #include <malloc.h>
13 #include <mmc.h>
14 #include <fat.h>
15 #include <dfu.h>
16 #include <linux/list.h>
17 #include <linux/compiler.h>
18
19 static bool dfu_reset_request;
20 static LIST_HEAD(dfu_list);
21 static int dfu_alt_num;
22 static int alt_num_count;
23 static int dfu_checksum_method;
24
25 bool dfu_reset(void)
26 {
27         return dfu_reset_request;
28 }
29
30 void dfu_trigger_reset()
31 {
32         dfu_reset_request = true;
33 }
34
35 static int dfu_find_alt_num(const char *s)
36 {
37         int i = 0;
38
39         for (; *s; s++)
40                 if (*s == ';')
41                         i++;
42
43         return ++i;
44 }
45
46 int dfu_init_env_entities(char *interface, int dev)
47 {
48         const char *str_env;
49         char *env_bkp;
50         int ret;
51
52         str_env = getenv("dfu_alt_info");
53         if (!str_env) {
54                 error("\"dfu_alt_info\" env variable not defined!\n");
55                 return -EINVAL;
56         }
57
58         env_bkp = strdup(str_env);
59         ret = dfu_config_entities(env_bkp, interface, dev);
60         if (ret) {
61                 error("DFU entities configuration failed!\n");
62                 return ret;
63         }
64
65         free(env_bkp);
66         return 0;
67 }
68
69 static unsigned char *dfu_buf;
70 static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE;
71
72 unsigned char *dfu_free_buf(void)
73 {
74         free(dfu_buf);
75         dfu_buf = NULL;
76         return dfu_buf;
77 }
78
79 unsigned long dfu_get_buf_size(void)
80 {
81         return dfu_buf_size;
82 }
83
84 unsigned char *dfu_get_buf(void)
85 {
86         char *s;
87
88         if (dfu_buf != NULL)
89                 return dfu_buf;
90
91         s = getenv("dfu_bufsiz");
92         dfu_buf_size = s ? (unsigned long)simple_strtol(s, NULL, 16) :
93                         CONFIG_SYS_DFU_DATA_BUF_SIZE;
94
95         dfu_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, dfu_buf_size);
96         if (dfu_buf == NULL)
97                 printf("%s: Could not memalign 0x%lx bytes\n",
98                        __func__, dfu_buf_size);
99
100         return dfu_buf;
101 }
102
103 static int dfu_get_checksum_method(void)
104 {
105         char *s;
106
107         s = getenv("dfu_checksum_method");
108         if (!s)
109                 return DFU_NO_CHECKSUM;
110
111         if (!strcmp(s, "crc32")) {
112                 debug("%s: DFU checksum method: %s\n", __func__, s);
113                 return DFU_CRC32;
114         } else {
115                 error("DFU checksum method: %s not supported!\n", s);
116                 return -EINVAL;
117         }
118 }
119
120 static int dfu_write_buffer_drain(struct dfu_entity *dfu)
121 {
122         long w_size;
123         int ret;
124
125         /* flush size? */
126         w_size = dfu->i_buf - dfu->i_buf_start;
127         if (w_size == 0)
128                 return 0;
129
130         if (dfu_checksum_method == DFU_CRC32)
131                 dfu->crc = crc32(dfu->crc, dfu->i_buf_start, w_size);
132
133         ret = dfu->write_medium(dfu, dfu->offset, dfu->i_buf_start, &w_size);
134         if (ret)
135                 debug("%s: Write error!\n", __func__);
136
137         /* point back */
138         dfu->i_buf = dfu->i_buf_start;
139
140         /* update offset */
141         dfu->offset += w_size;
142
143         puts("#");
144
145         return ret;
146 }
147
148 int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
149 {
150         int ret = 0;
151         int tret;
152
153         debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x offset: 0x%llx bufoffset: 0x%x\n",
154               __func__, dfu->name, buf, size, blk_seq_num, dfu->offset,
155               dfu->i_buf - dfu->i_buf_start);
156
157         if (!dfu->inited) {
158                 /* initial state */
159                 dfu->crc = 0;
160                 dfu->offset = 0;
161                 dfu->bad_skip = 0;
162                 dfu->i_blk_seq_num = 0;
163                 dfu->i_buf_start = dfu_get_buf();
164                 if (dfu->i_buf_start == NULL)
165                         return -ENOMEM;
166                 dfu->i_buf_end = dfu_get_buf() + dfu_buf_size;
167                 dfu->i_buf = dfu->i_buf_start;
168
169                 dfu->inited = 1;
170         }
171
172         if (dfu->i_blk_seq_num != blk_seq_num) {
173                 printf("%s: Wrong sequence number! [%d] [%d]\n",
174                        __func__, dfu->i_blk_seq_num, blk_seq_num);
175                 return -1;
176         }
177
178         /* DFU 1.1 standard says:
179          * The wBlockNum field is a block sequence number. It increments each
180          * time a block is transferred, wrapping to zero from 65,535. It is used
181          * to provide useful context to the DFU loader in the device."
182          *
183          * This means that it's a 16 bit counter that roll-overs at
184          * 0xffff -> 0x0000. By having a typical 4K transfer block
185          * we roll-over at exactly 256MB. Not very fun to debug.
186          *
187          * Handling rollover, and having an inited variable,
188          * makes things work.
189          */
190
191         /* handle rollover */
192         dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
193
194         /* flush buffer if overflow */
195         if ((dfu->i_buf + size) > dfu->i_buf_end) {
196                 tret = dfu_write_buffer_drain(dfu);
197                 if (ret == 0)
198                         ret = tret;
199         }
200
201         /* we should be in buffer now (if not then size too large) */
202         if ((dfu->i_buf + size) > dfu->i_buf_end) {
203                 error("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf,
204                       size, dfu->i_buf_end);
205                 return -1;
206         }
207
208         memcpy(dfu->i_buf, buf, size);
209         dfu->i_buf += size;
210
211         /* if end or if buffer full flush */
212         if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) {
213                 tret = dfu_write_buffer_drain(dfu);
214                 if (ret == 0)
215                         ret = tret;
216         }
217
218         /* end? */
219         if (size == 0) {
220                 /* Now try and flush to the medium if needed. */
221                 if (dfu->flush_medium)
222                         ret = dfu->flush_medium(dfu);
223
224                 if (dfu_checksum_method == DFU_CRC32)
225                         printf("\nDFU complete CRC32: 0x%08x\n", dfu->crc);
226                 /* clear everything */
227                 dfu_free_buf();
228                 dfu->crc = 0;
229                 dfu->offset = 0;
230                 dfu->i_blk_seq_num = 0;
231                 dfu->i_buf_start = dfu_buf;
232                 dfu->i_buf_end = dfu_buf;
233                 dfu->i_buf = dfu->i_buf_start;
234
235                 dfu->inited = 0;
236
237         }
238
239         return ret = 0 ? size : ret;
240 }
241
242 static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size)
243 {
244         long chunk;
245         int ret, readn;
246
247         readn = 0;
248         while (size > 0) {
249                 /* get chunk that can be read */
250                 chunk = min(size, dfu->b_left);
251                 /* consume */
252                 if (chunk > 0) {
253                         memcpy(buf, dfu->i_buf, chunk);
254                         if (dfu_checksum_method == DFU_CRC32)
255                                 dfu->crc = crc32(dfu->crc, buf, chunk);
256                         dfu->i_buf += chunk;
257                         dfu->b_left -= chunk;
258                         dfu->r_left -= chunk;
259                         size -= chunk;
260                         buf += chunk;
261                         readn += chunk;
262                 }
263
264                 /* all done */
265                 if (size > 0) {
266                         /* no more to read */
267                         if (dfu->r_left == 0)
268                                 break;
269
270                         dfu->i_buf = dfu->i_buf_start;
271                         dfu->b_left = dfu->i_buf_end - dfu->i_buf_start;
272
273                         /* got to read, but buffer is empty */
274                         if (dfu->b_left > dfu->r_left)
275                                 dfu->b_left = dfu->r_left;
276                         ret = dfu->read_medium(dfu, dfu->offset, dfu->i_buf,
277                                         &dfu->b_left);
278                         if (ret != 0) {
279                                 debug("%s: Read error!\n", __func__);
280                                 return ret;
281                         }
282                         dfu->offset += dfu->b_left;
283                         dfu->r_left -= dfu->b_left;
284
285                         puts("#");
286                 }
287         }
288
289         return readn;
290 }
291
292 int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
293 {
294         int ret = 0;
295
296         debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
297                __func__, dfu->name, buf, size, blk_seq_num, dfu->i_buf);
298
299         if (!dfu->inited) {
300                 dfu->i_buf_start = dfu_get_buf();
301                 if (dfu->i_buf_start == NULL)
302                         return -ENOMEM;
303
304                 ret = dfu->read_medium(dfu, 0, dfu->i_buf_start, &dfu->r_left);
305                 if (ret != 0) {
306                         debug("%s: failed to get r_left\n", __func__);
307                         return ret;
308                 }
309
310                 debug("%s: %s %ld [B]\n", __func__, dfu->name, dfu->r_left);
311
312                 dfu->i_blk_seq_num = 0;
313                 dfu->crc = 0;
314                 dfu->offset = 0;
315                 dfu->i_buf_end = dfu_get_buf() + dfu_buf_size;
316                 dfu->i_buf = dfu->i_buf_start;
317                 dfu->b_left = min(dfu_buf_size, dfu->r_left);
318
319                 dfu->bad_skip = 0;
320
321                 dfu->inited = 1;
322         }
323
324         if (dfu->i_blk_seq_num != blk_seq_num) {
325                 printf("%s: Wrong sequence number! [%d] [%d]\n",
326                        __func__, dfu->i_blk_seq_num, blk_seq_num);
327                 return -1;
328         }
329         /* handle rollover */
330         dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
331
332         ret = dfu_read_buffer_fill(dfu, buf, size);
333         if (ret < 0) {
334                 printf("%s: Failed to fill buffer\n", __func__);
335                 return -1;
336         }
337
338         if (ret < size) {
339                 if (dfu_checksum_method == DFU_CRC32)
340                         debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name,
341                               dfu->crc);
342                 puts("\nUPLOAD ... done\nCtrl+C to exit ...\n");
343
344                 dfu_free_buf();
345                 dfu->i_blk_seq_num = 0;
346                 dfu->crc = 0;
347                 dfu->offset = 0;
348                 dfu->i_buf_start = dfu_buf;
349                 dfu->i_buf_end = dfu_buf;
350                 dfu->i_buf = dfu->i_buf_start;
351                 dfu->b_left = 0;
352
353                 dfu->bad_skip = 0;
354
355                 dfu->inited = 0;
356         }
357
358         return ret;
359 }
360
361 static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
362                            char *interface, int num)
363 {
364         char *st;
365
366         debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num);
367         st = strsep(&s, " ");
368         strcpy(dfu->name, st);
369
370         dfu->dev_num = num;
371         dfu->alt = alt;
372
373         /* Specific for mmc device */
374         if (strcmp(interface, "mmc") == 0) {
375                 if (dfu_fill_entity_mmc(dfu, s))
376                         return -1;
377         } else if (strcmp(interface, "nand") == 0) {
378                 if (dfu_fill_entity_nand(dfu, s))
379                         return -1;
380         } else if (strcmp(interface, "ram") == 0) {
381                 if (dfu_fill_entity_ram(dfu, s))
382                         return -1;
383         } else {
384                 printf("%s: Device %s not (yet) supported!\n",
385                        __func__,  interface);
386                 return -1;
387         }
388
389         return 0;
390 }
391
392 void dfu_free_entities(void)
393 {
394         struct dfu_entity *dfu, *p, *t = NULL;
395
396         list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) {
397                 list_del(&dfu->list);
398                 t = dfu;
399         }
400         if (t)
401                 free(t);
402         INIT_LIST_HEAD(&dfu_list);
403
404         alt_num_count = 0;
405 }
406
407 int dfu_config_entities(char *env, char *interface, int num)
408 {
409         struct dfu_entity *dfu;
410         int i, ret;
411         char *s;
412
413         dfu_alt_num = dfu_find_alt_num(env);
414         debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
415
416         ret = dfu_get_checksum_method();
417         if (ret < 0)
418                 return ret;
419         dfu_checksum_method = ret;
420
421         dfu = calloc(sizeof(*dfu), dfu_alt_num);
422         if (!dfu)
423                 return -1;
424         for (i = 0; i < dfu_alt_num; i++) {
425
426                 s = strsep(&env, ";");
427                 ret = dfu_fill_entity(&dfu[i], s, alt_num_count, interface, num);
428                 if (ret)
429                         return -1;
430
431                 list_add_tail(&dfu[i].list, &dfu_list);
432                 alt_num_count++;
433         }
434
435         return 0;
436 }
437
438 const char *dfu_get_dev_type(enum dfu_device_type t)
439 {
440         const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM" };
441         return dev_t[t];
442 }
443
444 const char *dfu_get_layout(enum dfu_layout l)
445 {
446         const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
447                                            "EXT3", "EXT4", "RAM_ADDR" };
448         return dfu_layout[l];
449 }
450
451 void dfu_show_entities(void)
452 {
453         struct dfu_entity *dfu;
454
455         puts("DFU alt settings list:\n");
456
457         list_for_each_entry(dfu, &dfu_list, list) {
458                 printf("dev: %s alt: %d name: %s layout: %s\n",
459                        dfu_get_dev_type(dfu->dev_type), dfu->alt,
460                        dfu->name, dfu_get_layout(dfu->layout));
461         }
462 }
463
464 int dfu_get_alt_number(void)
465 {
466         return dfu_alt_num;
467 }
468
469 struct dfu_entity *dfu_get_entity(int alt)
470 {
471         struct dfu_entity *dfu;
472
473         list_for_each_entry(dfu, &dfu_list, list) {
474                 if (dfu->alt == alt)
475                         return dfu;
476         }
477
478         return NULL;
479 }
480
481 int dfu_get_alt(char *name)
482 {
483         struct dfu_entity *dfu;
484
485         list_for_each_entry(dfu, &dfu_list, list) {
486                 if (!strncmp(dfu->name, name, strlen(dfu->name)))
487                         return dfu->alt;
488         }
489
490         return -ENODEV;
491 }