xref: /rk3399_rockchip-uboot/drivers/dfu/dfu.c (revision ea2453d56b8860dbd18a3c517531ffc8dcb5c839)
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  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 
22 #include <common.h>
23 #include <malloc.h>
24 #include <mmc.h>
25 #include <fat.h>
26 #include <dfu.h>
27 #include <linux/list.h>
28 #include <linux/compiler.h>
29 
30 static LIST_HEAD(dfu_list);
31 static int dfu_alt_num;
32 
33 static int dfu_find_alt_num(const char *s)
34 {
35 	int i = 0;
36 
37 	for (; *s; s++)
38 		if (*s == ';')
39 			i++;
40 
41 	return ++i;
42 }
43 
44 static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
45 				     dfu_buf[DFU_DATA_BUF_SIZE];
46 
47 static int dfu_write_buffer_drain(struct dfu_entity *dfu)
48 {
49 	long w_size;
50 	int ret;
51 
52 	/* flush size? */
53 	w_size = dfu->i_buf - dfu->i_buf_start;
54 	if (w_size == 0)
55 		return 0;
56 
57 	/* update CRC32 */
58 	dfu->crc = crc32(dfu->crc, dfu->i_buf_start, w_size);
59 
60 	ret = dfu->write_medium(dfu, dfu->offset, dfu->i_buf_start, &w_size);
61 	if (ret)
62 		debug("%s: Write error!\n", __func__);
63 
64 	/* point back */
65 	dfu->i_buf = dfu->i_buf_start;
66 
67 	/* update offset */
68 	dfu->offset += w_size;
69 
70 	puts("#");
71 
72 	return ret;
73 }
74 
75 int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
76 {
77 	int ret = 0;
78 	int tret;
79 
80 	debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x offset: 0x%llx bufoffset: 0x%x\n",
81 	      __func__, dfu->name, buf, size, blk_seq_num, dfu->offset,
82 	      dfu->i_buf - dfu->i_buf_start);
83 
84 	if (!dfu->inited) {
85 		/* initial state */
86 		dfu->crc = 0;
87 		dfu->offset = 0;
88 		dfu->i_blk_seq_num = 0;
89 		dfu->i_buf_start = dfu_buf;
90 		dfu->i_buf_end = dfu_buf + sizeof(dfu_buf);
91 		dfu->i_buf = dfu->i_buf_start;
92 
93 		dfu->inited = 1;
94 	}
95 
96 	if (dfu->i_blk_seq_num != blk_seq_num) {
97 		printf("%s: Wrong sequence number! [%d] [%d]\n",
98 		       __func__, dfu->i_blk_seq_num, blk_seq_num);
99 		return -1;
100 	}
101 
102 	/* DFU 1.1 standard says:
103 	 * The wBlockNum field is a block sequence number. It increments each
104 	 * time a block is transferred, wrapping to zero from 65,535. It is used
105 	 * to provide useful context to the DFU loader in the device."
106 	 *
107 	 * This means that it's a 16 bit counter that roll-overs at
108 	 * 0xffff -> 0x0000. By having a typical 4K transfer block
109 	 * we roll-over at exactly 256MB. Not very fun to debug.
110 	 *
111 	 * Handling rollover, and having an inited variable,
112 	 * makes things work.
113 	 */
114 
115 	/* handle rollover */
116 	dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
117 
118 	/* flush buffer if overflow */
119 	if ((dfu->i_buf + size) > dfu->i_buf_end) {
120 		tret = dfu_write_buffer_drain(dfu);
121 		if (ret == 0)
122 			ret = tret;
123 	}
124 
125 	/* we should be in buffer now (if not then size too large) */
126 	if ((dfu->i_buf + size) > dfu->i_buf_end) {
127 		printf("%s: Wrong size! [%d] [%d] - %d\n",
128 		       __func__, dfu->i_blk_seq_num, blk_seq_num, size);
129 		return -1;
130 	}
131 
132 	memcpy(dfu->i_buf, buf, size);
133 	dfu->i_buf += size;
134 
135 	/* if end or if buffer full flush */
136 	if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) {
137 		tret = dfu_write_buffer_drain(dfu);
138 		if (ret == 0)
139 			ret = tret;
140 	}
141 
142 	/* end? */
143 	if (size == 0) {
144 		/* Now try and flush to the medium if needed. */
145 		if (dfu->flush_medium)
146 			ret = dfu->flush_medium(dfu);
147 		printf("\nDFU complete CRC32: 0x%08x\n", dfu->crc);
148 
149 		/* clear everything */
150 		dfu->crc = 0;
151 		dfu->offset = 0;
152 		dfu->i_blk_seq_num = 0;
153 		dfu->i_buf_start = dfu_buf;
154 		dfu->i_buf_end = dfu_buf + sizeof(dfu_buf);
155 		dfu->i_buf = dfu->i_buf_start;
156 
157 		dfu->inited = 0;
158 
159 	}
160 
161 	return ret = 0 ? size : ret;
162 }
163 
164 static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size)
165 {
166 	long chunk;
167 	int ret, readn;
168 
169 	readn = 0;
170 	while (size > 0) {
171 		/* get chunk that can be read */
172 		chunk = min(size, dfu->b_left);
173 		/* consume */
174 		if (chunk > 0) {
175 			memcpy(buf, dfu->i_buf, chunk);
176 			dfu->crc = crc32(dfu->crc, buf, chunk);
177 			dfu->i_buf += chunk;
178 			dfu->b_left -= chunk;
179 			size -= chunk;
180 			buf += chunk;
181 			readn += chunk;
182 		}
183 
184 		/* all done */
185 		if (size > 0) {
186 			/* no more to read */
187 			if (dfu->r_left == 0)
188 				break;
189 
190 			dfu->i_buf = dfu->i_buf_start;
191 			dfu->b_left = dfu->i_buf_end - dfu->i_buf_start;
192 
193 			/* got to read, but buffer is empty */
194 			if (dfu->b_left > dfu->r_left)
195 				dfu->b_left = dfu->r_left;
196 			ret = dfu->read_medium(dfu, dfu->offset, dfu->i_buf,
197 					&dfu->b_left);
198 			if (ret != 0) {
199 				debug("%s: Read error!\n", __func__);
200 				return ret;
201 			}
202 			dfu->offset += dfu->b_left;
203 			dfu->r_left -= dfu->b_left;
204 
205 			puts("#");
206 		}
207 	}
208 
209 	return readn;
210 }
211 
212 int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
213 {
214 	int ret = 0;
215 
216 	debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
217 	       __func__, dfu->name, buf, size, blk_seq_num, dfu->i_buf);
218 
219 	if (!dfu->inited) {
220 		ret = dfu->read_medium(dfu, 0, buf, &dfu->r_left);
221 		if (ret != 0) {
222 			debug("%s: failed to get r_left\n", __func__);
223 			return ret;
224 		}
225 
226 		debug("%s: %s %ld [B]\n", __func__, dfu->name, dfu->r_left);
227 
228 		dfu->i_blk_seq_num = 0;
229 		dfu->crc = 0;
230 		dfu->offset = 0;
231 		dfu->i_buf_start = dfu_buf;
232 		dfu->i_buf_end = dfu_buf + sizeof(dfu_buf);
233 		dfu->i_buf = dfu->i_buf_start;
234 		dfu->b_left = 0;
235 
236 		dfu->inited = 1;
237 	}
238 
239 	if (dfu->i_blk_seq_num != blk_seq_num) {
240 		printf("%s: Wrong sequence number! [%d] [%d]\n",
241 		       __func__, dfu->i_blk_seq_num, blk_seq_num);
242 		return -1;
243 	}
244 	/* handle rollover */
245 	dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
246 
247 	ret = dfu_read_buffer_fill(dfu, buf, size);
248 	if (ret < 0) {
249 		printf("%s: Failed to fill buffer\n", __func__);
250 		return -1;
251 	}
252 
253 	if (ret < size) {
254 		debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, dfu->crc);
255 		puts("\nUPLOAD ... done\nCtrl+C to exit ...\n");
256 
257 		dfu->i_blk_seq_num = 0;
258 		dfu->crc = 0;
259 		dfu->offset = 0;
260 		dfu->i_buf_start = dfu_buf;
261 		dfu->i_buf_end = dfu_buf + sizeof(dfu_buf);
262 		dfu->i_buf = dfu->i_buf_start;
263 		dfu->b_left = 0;
264 
265 		dfu->inited = 0;
266 	}
267 
268 	return ret;
269 }
270 
271 static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
272 			    char *interface, int num)
273 {
274 	char *st;
275 
276 	debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num);
277 	st = strsep(&s, " ");
278 	strcpy(dfu->name, st);
279 
280 	dfu->dev_num = num;
281 	dfu->alt = alt;
282 
283 	/* Specific for mmc device */
284 	if (strcmp(interface, "mmc") == 0) {
285 		if (dfu_fill_entity_mmc(dfu, s))
286 			return -1;
287 	} else {
288 		printf("%s: Device %s not (yet) supported!\n",
289 		       __func__,  interface);
290 		return -1;
291 	}
292 
293 	return 0;
294 }
295 
296 void dfu_free_entities(void)
297 {
298 	struct dfu_entity *dfu, *p, *t = NULL;
299 
300 	list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) {
301 		list_del(&dfu->list);
302 		t = dfu;
303 	}
304 	if (t)
305 		free(t);
306 	INIT_LIST_HEAD(&dfu_list);
307 }
308 
309 int dfu_config_entities(char *env, char *interface, int num)
310 {
311 	struct dfu_entity *dfu;
312 	int i, ret;
313 	char *s;
314 
315 	dfu_alt_num = dfu_find_alt_num(env);
316 	debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
317 
318 	dfu = calloc(sizeof(*dfu), dfu_alt_num);
319 	if (!dfu)
320 		return -1;
321 	for (i = 0; i < dfu_alt_num; i++) {
322 
323 		s = strsep(&env, ";");
324 		ret = dfu_fill_entity(&dfu[i], s, i, interface, num);
325 		if (ret)
326 			return -1;
327 
328 		list_add_tail(&dfu[i].list, &dfu_list);
329 	}
330 
331 	return 0;
332 }
333 
334 const char *dfu_get_dev_type(enum dfu_device_type t)
335 {
336 	const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND" };
337 	return dev_t[t];
338 }
339 
340 const char *dfu_get_layout(enum dfu_layout l)
341 {
342 	const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
343 					   "EXT3", "EXT4" };
344 	return dfu_layout[l];
345 }
346 
347 void dfu_show_entities(void)
348 {
349 	struct dfu_entity *dfu;
350 
351 	puts("DFU alt settings list:\n");
352 
353 	list_for_each_entry(dfu, &dfu_list, list) {
354 		printf("dev: %s alt: %d name: %s layout: %s\n",
355 		       dfu_get_dev_type(dfu->dev_type), dfu->alt,
356 		       dfu->name, dfu_get_layout(dfu->layout));
357 	}
358 }
359 
360 int dfu_get_alt_number(void)
361 {
362 	return dfu_alt_num;
363 }
364 
365 struct dfu_entity *dfu_get_entity(int alt)
366 {
367 	struct dfu_entity *dfu;
368 
369 	list_for_each_entry(dfu, &dfu_list, list) {
370 		if (dfu->alt == alt)
371 			return dfu;
372 	}
373 
374 	return NULL;
375 }
376