uboot-nand-markbad-reallybad.patch
[u-boot-openmoko/mini2440.git] / drivers / usb / usbdfu.c
blobb7ca8b6087604fd14958d0d85c1714817244492b
1 /*
2 * (C) 2007 by OpenMoko, Inc.
3 * Author: Harald Welte <laforge@openmoko.org>
5 * based on existing SAM7DFU code from OpenPCD:
6 * (C) Copyright 2006 by Harald Welte <hwelte@hmw-consulting.de>
8 * See file CREDITS for list of people who contributed to this
9 * project.
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation; either version 2 of
14 * the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
24 * MA 02111-1307 USA
26 * TODO:
27 * - make NAND support reasonably self-contained and put in apropriate
28 * ifdefs
29 * - add some means of synchronization, i.e. block commandline access
30 * while DFU transfer is in progress, and return to commandline once
31 * we're finished
32 * - add VERIFY support after writing to flash
33 * - sanely free() resources allocated during first uppload/download
34 * request when aborting
35 * - sanely free resources when another alternate interface is selected
37 * Maybe:
38 * - add something like uImage or some other header that provides CRC
39 * checking?
40 * - make 'dnstate' attached to 'struct usb_device_instance'
43 #include <config.h>
44 #if defined(CONFIG_USBD_DFU)
46 #include <common.h>
47 DECLARE_GLOBAL_DATA_PTR;
49 #include <malloc.h>
50 #include <linux/types.h>
51 #include <linux/list.h>
52 #include <asm/errno.h>
53 #include <usbdcore.h>
54 #include <usb_dfu.h>
55 #include <usb_dfu_descriptors.h>
56 #include <usb_dfu_trailer.h>
58 #include <nand.h>
59 #include <jffs2/load_kernel.h>
60 int mtdparts_init(void);
61 extern struct list_head devices;
63 #include "usbdcore_s3c2410.h"
64 #include "../serial/usbtty.h" /* for STR_* defs */
66 #define RET_NOTHING 0
67 #define RET_ZLP 1
68 #define RET_STALL 2
70 volatile enum dfu_state *system_dfu_state; /* for 3rd parties */
73 struct dnload_state {
74 nand_info_t *nand;
75 struct part_info *part;
76 unsigned int part_net_size; /* net sizee (excl. bad blocks) of part */
78 nand_erase_options_t erase_opts;
79 nand_write_options_t write_opts;
80 nand_read_options_t read_opts;
82 unsigned char *ptr; /* pointer to next empty byte in buffer */
83 unsigned int off; /* offset of current erase page in flash chip */
84 unsigned char *buf; /* pointer to allocated erase page buffer */
86 /* unless doing an atomic transfer, we use the static buffer below.
87 * This saves us from having to clean up dynamic allications in the
88 * various error paths of the code. Also, it will always work, no
89 * matter what the memory situation is. */
90 unsigned char _buf[0x20000]; /* FIXME: depends flash page size */
93 static struct dnload_state _dnstate;
95 static int dfu_trailer_matching(const struct uboot_dfu_trailer *trailer)
97 if (trailer->magic != UBOOT_DFU_TRAILER_MAGIC ||
98 trailer->version != UBOOT_DFU_TRAILER_V1 ||
99 trailer->vendor != CONFIG_USBD_VENDORID ||
100 (trailer->product != CONFIG_USBD_PRODUCTID_CDCACM &&
101 trailer->product != CONFIG_USBD_PRODUCTID_GSERIAL))
102 return 0;
103 #ifdef CONFIG_REVISION_TAG
104 if (trailer->revision != get_board_rev())
105 return 0;
106 #endif
108 return 1;
111 static struct part_info *get_partition_nand(int idx)
113 struct mtd_device *dev;
114 struct part_info *part;
115 struct list_head *pentry;
116 int i;
118 if (mtdparts_init())
119 return NULL;
120 if (list_empty(&devices))
121 return NULL;
123 dev = list_entry(devices.next, struct mtd_device, link);
124 i = 0;
125 list_for_each(pentry, &dev->parts) {
126 if (i == idx) {
127 part = list_entry(pentry, struct part_info, link);
128 return part;
130 i++;
133 return NULL;
136 #define LOAD_ADDR ((unsigned char *)0x32000000)
138 static int initialize_ds_nand(struct usb_device_instance *dev, struct dnload_state *ds)
140 ds->part = get_partition_nand(dev->alternate - 1);
141 if (!ds->part) {
142 printf("DFU: unable to find partition %u\b", dev->alternate-1);
143 dev->dfu_state = DFU_STATE_dfuERROR;
144 dev->dfu_status = DFU_STATUS_errADDRESS;
145 return RET_STALL;
147 ds->nand = &nand_info[ds->part->dev->id->num];
148 ds->off = ds->part->offset;
149 ds->part_net_size = nand_net_part_size(ds->part);
151 if (ds->nand->erasesize > sizeof(ds->_buf)) {
152 printf("*** Warning - NAND ERASESIZE bigger than static buffer\n");
153 ds->buf = malloc(ds->nand->erasesize);
154 if (!ds->buf) {
155 printf("DFU: can't allocate %u bytes\n", ds->nand->erasesize);
156 dev->dfu_state = DFU_STATE_dfuERROR;
157 dev->dfu_status = DFU_STATUS_errADDRESS;
158 return RET_STALL;
160 } else
161 ds->buf = ds->_buf;
163 ds->ptr = ds->buf;
165 memset(&ds->read_opts, 0, sizeof(ds->read_opts));
167 memset(&ds->erase_opts, 0, sizeof(ds->erase_opts));
168 ds->erase_opts.quiet = 1;
169 /* FIXME: do this more dynamic */
170 if (!strcmp(ds->part->name, "rootfs"))
171 ds->erase_opts.jffs2 = 1;
173 memset(&ds->write_opts, 0, sizeof(ds->write_opts));
174 ds->write_opts.pad = 1;
175 ds->write_opts.blockalign = 1;
176 ds->write_opts.quiet = 1;
178 debug("initialize_ds_nand(dev=%p, ds=%p): ", dev, ds);
179 debug("nand=%p, ptr=%p, buf=%p, off=0x%x\n", ds->nand, ds->ptr, ds->buf, ds->off);
181 return RET_NOTHING;
184 static int erase_flash_verify_nand(struct urb *urb, struct dnload_state *ds,
185 unsigned long erasesize, unsigned long size)
187 struct usb_device_instance *dev = urb->device;
188 int rc;
190 debug("erase_flash_verify_nand(urb=%p, ds=%p, erase=0x%x size=0x%x)\n",
191 urb, ds, erasesize, size);
193 if (erasesize == ds->nand->erasesize) {
194 /* we're only writing a single block and need to
195 * do bad block skipping / offset adjustments our own */
196 while (ds->nand->block_isbad(ds->nand, ds->off)) {
197 debug("SKIP_ONE_BLOCK(0x%08x)!!\n", ds->off);
198 ds->off += ds->nand->erasesize;
202 /* we have finished one eraseblock, flash it */
203 ds->erase_opts.offset = ds->off;
204 ds->erase_opts.length = erasesize;
205 debug("Erasing 0x%x bytes @ offset 0x%x (jffs=%u)\n",
206 ds->erase_opts.length, ds->erase_opts.offset,
207 ds->erase_opts.jffs2);
208 rc = nand_erase_opts(ds->nand, &ds->erase_opts);
209 if (rc) {
210 debug("Error erasing\n");
211 dev->dfu_state = DFU_STATE_dfuERROR;
212 dev->dfu_status = DFU_STATUS_errERASE;
213 return RET_STALL;
216 ds->write_opts.buffer = ds->buf;
217 ds->write_opts.length = size;
218 ds->write_opts.offset = ds->off;
219 debug("Writing 0x%x bytes @ offset 0x%x\n", size, ds->off);
220 rc = nand_write_opts(ds->nand, &ds->write_opts);
221 if (rc) {
222 debug("Error writing\n");
223 dev->dfu_state = DFU_STATE_dfuERROR;
224 dev->dfu_status = DFU_STATUS_errWRITE;
225 return RET_STALL;
228 ds->off += size;
229 ds->ptr = ds->buf;
231 /* FIXME: implement verify! */
232 return RET_NOTHING;
235 static int erase_tail_clean_nand(struct urb *urb, struct dnload_state *ds)
237 struct usb_device_instance *dev = urb->device;
238 int rc;
240 ds->erase_opts.offset = ds->off;
241 ds->erase_opts.length = ds->part->size - (ds->off - ds->part->offset);
242 debug("Erasing tail of 0x%x bytes @ offset 0x%x (jffs=%u)\n",
243 ds->erase_opts.length, ds->erase_opts.offset,
244 ds->erase_opts.jffs2);
245 rc = nand_erase_opts(ds->nand, &ds->erase_opts);
246 if (rc) {
247 printf("Error erasing tail\n");
248 dev->dfu_state = DFU_STATE_dfuERROR;
249 dev->dfu_status = DFU_STATUS_errERASE;
250 return RET_STALL;
253 ds->off += ds->erase_opts.length; /* for consistency */
255 return RET_NOTHING;
258 /* Read the next erase blcok from NAND into buffer */
259 static int read_next_nand(struct urb *urb, struct dnload_state *ds)
261 struct usb_device_instance *dev = urb->device;
262 int rc;
264 ds->read_opts.buffer = ds->buf;
265 ds->read_opts.length = ds->nand->erasesize;
266 ds->read_opts.offset = ds->off;
267 ds->read_opts.quiet = 1;
269 debug("Reading 0x%x@0x%x to 0x%08p\n", ds->nand->erasesize,
270 ds->off, ds->buf);
271 rc = nand_read_opts(ds->nand, &ds->read_opts);
272 if (rc) {
273 debug("Error reading\n");
274 dev->dfu_state = DFU_STATE_dfuERROR;
275 dev->dfu_status = DFU_STATUS_errWRITE;
276 return RET_STALL;
278 ds->off += ds->nand->erasesize;
279 ds->ptr = ds->buf;
281 return RET_NOTHING;
285 static int handle_dnload(struct urb *urb, u_int16_t val, u_int16_t len, int first)
287 struct usb_device_instance *dev = urb->device;
288 struct dnload_state *ds = &_dnstate;
289 unsigned int actual_len = len;
290 unsigned int remain_len;
291 unsigned long size;
292 int rc;
294 debug("download(len=%u, first=%u) ", len, first);
296 if (len > CONFIG_USBD_DFU_XFER_SIZE) {
297 /* Too big. Not that we'd really care, but it's a
298 * DFU protocol violation */
299 debug("length exceeds flash page size ");
300 dev->dfu_state = DFU_STATE_dfuERROR;
301 dev->dfu_status = DFU_STATUS_errADDRESS;
302 return RET_STALL;
305 if (first) {
306 /* Make sure that we have a valid mtd partition table */
307 char *mtdp = getenv("mtdparts");
308 if (!mtdp)
309 run_command("dynpart", 0);
312 if (len == 0) {
313 debug("zero-size write -> MANIFEST_SYNC ");
314 dev->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
316 /* cleanup */
317 switch (dev->alternate) {
318 char buf[12];
319 case 0:
320 sprintf(buf, "%lx", ds->ptr - ds->buf);
321 setenv("filesize", buf);
322 ds->ptr = ds->buf;
323 break;
324 case 1:
325 if (ds->ptr >
326 ds->buf + sizeof(struct uboot_dfu_trailer)) {
327 struct uboot_dfu_trailer trailer;
328 dfu_trailer_mirror(&trailer, ds->ptr);
329 if (!dfu_trailer_matching(&trailer)) {
330 printf("DFU TRAILER NOT OK\n");
331 dev->dfu_state = DFU_STATE_dfuERROR;
332 dev->dfu_status = DFU_STATUS_errTARGET;
333 return RET_STALL;
336 rc = erase_flash_verify_nand(urb, ds,
337 ds->part->size,
338 ds->part_net_size);
339 /* re-write dynenv marker in OOB */
340 run_command("dynenv set u-boot_env", 0);
342 ds->nand = NULL;
343 free(ds->buf);
344 ds->ptr = ds->buf = ds->_buf;
345 break;
346 default:
347 rc = 0;
348 if (ds->ptr > ds->buf)
349 rc = erase_flash_verify_nand(urb, ds,
350 ds->nand->erasesize,
351 ds->nand->erasesize);
352 /* rootfs partition */
353 if (!rc && !strcmp(ds->part->name, "rootfs"))
354 rc = erase_tail_clean_nand(urb, ds);
356 ds->nand = NULL;
357 break;
360 return RET_ZLP;
363 if (urb->actual_length != len) {
364 debug("urb->actual_length(%u) != len(%u) ?!? ",
365 urb->actual_length, len);
366 dev->dfu_state = DFU_STATE_dfuERROR;
367 dev->dfu_status = DFU_STATUS_errADDRESS;
368 return RET_STALL;
371 if (first && ds->buf && ds->buf != ds->_buf && ds->buf != LOAD_ADDR) {
372 free(ds->buf);
373 ds->buf = ds->_buf;
376 switch (dev->alternate) {
377 case 0:
378 if (first) {
379 printf("Starting DFU DOWNLOAD to RAM (0x%08p)\n",
380 LOAD_ADDR);
381 ds->buf = LOAD_ADDR;
382 ds->ptr = ds->buf;
385 memcpy(ds->ptr, urb->buffer, len);
386 ds->ptr += len;
387 break;
388 case 1:
389 if (first) {
390 rc = initialize_ds_nand(dev, ds);
391 if (rc)
392 return rc;
393 ds->buf = malloc(ds->part_net_size);
394 if (!ds->buf) {
395 printf("No memory for atomic buffer!!\n");
396 dev->dfu_state = DFU_STATE_dfuERROR;
397 dev->dfu_status = DFU_STATUS_errUNKNOWN;
398 return RET_STALL;
400 ds->ptr = ds->buf;
401 printf("Starting Atomic DFU DOWNLOAD to partition '%s'\n",
402 ds->part->name);
405 remain_len = (ds->buf + ds->part_net_size) - ds->ptr;
406 if (remain_len < len) {
407 len = remain_len;
408 printf("End of write exceeds partition end\n");
409 dev->dfu_state = DFU_STATE_dfuERROR;
410 dev->dfu_status = DFU_STATUS_errADDRESS;
411 return RET_STALL;
413 memcpy(ds->ptr, urb->buffer, len);
414 ds->ptr += len;
415 break;
416 default:
417 if (first) {
418 rc = initialize_ds_nand(dev, ds);
419 if (rc)
420 return rc;
421 printf("Starting DFU DOWNLOAD to partition '%s'\n",
422 ds->part->name);
425 size = ds->nand->erasesize;
426 remain_len = ds->buf + size - ds->ptr;
427 if (remain_len < len)
428 actual_len = remain_len;
430 memcpy(ds->ptr, urb->buffer, actual_len);
431 ds->ptr += actual_len;
433 /* check partition end */
434 if (ds->off + (ds->ptr - ds->buf) > ds->part->offset + ds->part->size) {
435 printf("End of write exceeds partition end\n");
436 dev->dfu_state = DFU_STATE_dfuERROR;
437 dev->dfu_status = DFU_STATUS_errADDRESS;
438 return RET_STALL;
441 if (ds->ptr >= ds->buf + size) {
442 rc = erase_flash_verify_nand(urb, ds,
443 ds->nand->erasesize,
444 ds->nand->erasesize);
445 if (rc)
446 return rc;
447 /* copy remainder of data into buffer */
448 memcpy(ds->ptr, urb->buffer + actual_len, len - actual_len);
449 ds->ptr += (len - actual_len);
451 break;
454 return RET_ZLP;
457 static int handle_upload(struct urb *urb, u_int16_t val, u_int16_t len, int first)
459 struct usb_device_instance *dev = urb->device;
460 struct dnload_state *ds = &_dnstate;
461 unsigned int remain;
462 int rc;
464 debug("upload(val=0x%02x, len=%u, first=%u) ", val, len, first);
466 if (len > CONFIG_USBD_DFU_XFER_SIZE) {
467 /* Too big */
468 dev->dfu_state = DFU_STATE_dfuERROR;
469 dev->dfu_status = DFU_STATUS_errADDRESS;
470 //udc_ep0_send_stall();
471 debug("Error: Transfer size > CONFIG_USBD_DFU_XFER_SIZE ");
472 return -EINVAL;
475 switch (dev->alternate) {
476 case 0:
477 if (first) {
478 printf("Starting DFU Upload of RAM (0x%08p)\n",
479 LOAD_ADDR);
480 ds->ptr = ds->buf;
483 /* FIXME: end at some more dynamic point */
484 if (ds->ptr + len > LOAD_ADDR + 0x200000)
485 len = (LOAD_ADDR + 0x200000) - ds->ptr;
487 urb->buffer = ds->ptr;
488 urb->actual_length = len;
489 ds->ptr += len;
490 break;
491 default:
492 if (first) {
493 rc = initialize_ds_nand(dev, ds);
494 if (rc)
495 return -EINVAL;
496 printf("Starting DFU Upload of partition '%s'\n",
497 ds->part->name);
498 rc = read_next_nand(urb, ds);
499 if (rc)
500 return -EINVAL;
503 if (len > ds->nand->erasesize) {
504 printf("We don't support transfers bigger than %u\n",
505 ds->nand->erasesize);
506 len = ds->nand->erasesize;
509 remain = ds->nand->erasesize - (ds->ptr - ds->buf);
510 if (len < remain)
511 remain = len;
513 debug("copying %u bytes ", remain);
514 urb->buffer = ds->ptr;
515 ds->ptr += remain;
516 urb->actual_length = remain;
518 if (ds->ptr >= ds->buf + ds->nand->erasesize &&
519 ds->off < ds->part->offset + ds->part->size) {
520 rc = read_next_nand(urb, ds);
521 if (rc)
522 return -EINVAL;
523 if (len > remain) {
524 debug("copying another %u bytes ", len - remain);
525 memcpy(urb->buffer + remain, ds->ptr, len - remain);
526 ds->ptr += (len - remain);
527 urb->actual_length += (len - remain);
530 break;
533 debug("returning len=%u\n", len);
534 return len;
537 static void handle_getstatus(struct urb *urb, int max)
539 struct usb_device_instance *dev = urb->device;
540 struct dfu_status *dstat = (struct dfu_status *) urb->buffer;
542 debug("getstatus ");
544 if (!urb->buffer || urb->buffer_length < sizeof(*dstat)) {
545 debug("invalid urb! ");
546 return;
549 switch (dev->dfu_state) {
550 case DFU_STATE_dfuDNLOAD_SYNC:
551 case DFU_STATE_dfuDNBUSY:
552 #if 0
553 if (fsr & AT91C_MC_PROGE) {
554 debug("errPROG ");
555 dev->dfu_status = DFU_STATUS_errPROG;
556 dev->dfu_state = DFU_STATE_dfuERROR;
557 } else if (fsr & AT91C_MC_LOCKE) {
558 debug("errWRITE ");
559 dev->dfu_status = DFU_STATUS_errWRITE;
560 dev->dfu_state = DFU_STATE_dfuERROR;
561 } else if (fsr & AT91C_MC_FRDY) {
562 #endif
563 debug("DNLOAD_IDLE ");
564 dev->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
565 #if 0
566 } else {
567 debug("DNBUSY ");
568 dev->dfu_state = DFU_STATE_dfuDNBUSY;
570 #endif
571 break;
572 case DFU_STATE_dfuMANIFEST_SYNC:
573 break;
574 default:
575 //return;
576 break;
579 /* send status response */
580 dstat->bStatus = dev->dfu_status;
581 dstat->bState = dev->dfu_state;
582 dstat->iString = 0;
583 /* FIXME: set dstat->bwPollTimeout */
584 urb->actual_length = MIN(sizeof(*dstat), max);
586 /* we don't need to explicitly send data here, will
587 * be done by the original caller! */
590 static void handle_getstate(struct urb *urb, int max)
592 debug("getstate ");
594 if (!urb->buffer || urb->buffer_length < sizeof(u_int8_t)) {
595 debug("invalid urb! ");
596 return;
599 urb->buffer[0] = urb->device->dfu_state & 0xff;
600 urb->actual_length = sizeof(u_int8_t);
603 #ifndef CONFIG_USBD_PRODUCTID_DFU
604 #define CONFIG_USBD_PRODUCTID_DFU CONFIG_USBD_PRODUCTID_CDCACM
605 #endif
607 static const struct usb_device_descriptor dfu_dev_descriptor = {
608 .bLength = USB_DT_DEVICE_SIZE,
609 .bDescriptorType = USB_DT_DEVICE,
610 .bcdUSB = 0x0100,
611 .bDeviceClass = 0x00,
612 .bDeviceSubClass = 0x00,
613 .bDeviceProtocol = 0x00,
614 .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE,
615 .idVendor = CONFIG_USBD_VENDORID,
616 .idProduct = CONFIG_USBD_PRODUCTID_DFU,
617 .bcdDevice = 0x0000,
618 .iManufacturer = DFU_STR_MANUFACTURER,
619 .iProduct = DFU_STR_PRODUCT,
620 .iSerialNumber = DFU_STR_SERIAL,
621 .bNumConfigurations = 0x01,
624 static struct _dfu_desc dfu_cfg_descriptor = {
625 .ucfg = {
626 .bLength = USB_DT_CONFIG_SIZE,
627 .bDescriptorType = USB_DT_CONFIG,
628 .wTotalLength = USB_DT_CONFIG_SIZE +
629 DFU_NUM_ALTERNATES * USB_DT_INTERFACE_SIZE +
630 USB_DT_DFU_SIZE,
631 .bNumInterfaces = 1,
632 .bConfigurationValue = 1,
633 .iConfiguration = DFU_STR_CONFIG,
634 .bmAttributes = BMATTRIBUTE_RESERVED,
635 .bMaxPower = 50,
637 .func_dfu = DFU_FUNC_DESC,
640 int dfu_ep0_handler(struct urb *urb)
642 int rc, ret = RET_NOTHING;
643 u_int8_t req = urb->device_request.bRequest;
644 u_int16_t val = urb->device_request.wValue;
645 u_int16_t len = urb->device_request.wLength;
646 struct usb_device_instance *dev = urb->device;
648 debug("dfu_ep0(req=0x%x, val=0x%x, len=%u) old_state = %u ",
649 req, val, len, dev->dfu_state);
651 switch (dev->dfu_state) {
652 case DFU_STATE_appIDLE:
653 switch (req) {
654 case USB_REQ_DFU_GETSTATUS:
655 handle_getstatus(urb, len);
656 break;
657 case USB_REQ_DFU_GETSTATE:
658 handle_getstate(urb, len);
659 break;
660 case USB_REQ_DFU_DETACH:
661 dev->dfu_state = DFU_STATE_appDETACH;
662 ret = RET_ZLP;
663 goto out;
664 break;
665 default:
666 ret = RET_STALL;
668 break;
669 case DFU_STATE_appDETACH:
670 switch (req) {
671 case USB_REQ_DFU_GETSTATUS:
672 handle_getstatus(urb, len);
673 break;
674 case USB_REQ_DFU_GETSTATE:
675 handle_getstate(urb, len);
676 break;
677 default:
678 dev->dfu_state = DFU_STATE_appIDLE;
679 ret = RET_STALL;
680 goto out;
681 break;
683 /* FIXME: implement timer to return to appIDLE */
684 break;
685 case DFU_STATE_dfuIDLE:
686 switch (req) {
687 case USB_REQ_DFU_DNLOAD:
688 if (len == 0) {
689 dev->dfu_state = DFU_STATE_dfuERROR;
690 ret = RET_STALL;
691 goto out;
693 dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
694 ret = handle_dnload(urb, val, len, 1);
695 break;
696 case USB_REQ_DFU_UPLOAD:
697 dev->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
698 handle_upload(urb, val, len, 1);
699 break;
700 case USB_REQ_DFU_ABORT:
701 /* no zlp? */
702 ret = RET_ZLP;
703 break;
704 case USB_REQ_DFU_GETSTATUS:
705 handle_getstatus(urb, len);
706 break;
707 case USB_REQ_DFU_GETSTATE:
708 handle_getstate(urb, len);
709 break;
710 case USB_REQ_DFU_DETACH:
711 /* Proprietary extension: 'detach' from idle mode and
712 * get back to runtime mode in case of USB Reset. As
713 * much as I dislike this, we just can't use every USB
714 * bus reset to switch back to runtime mode, since at
715 * least the Linux USB stack likes to send a number of resets
716 * in a row :( */
717 dev->dfu_state = DFU_STATE_dfuMANIFEST_WAIT_RST;
718 break;
719 default:
720 dev->dfu_state = DFU_STATE_dfuERROR;
721 ret = RET_STALL;
722 goto out;
723 break;
725 break;
726 case DFU_STATE_dfuDNLOAD_SYNC:
727 switch (req) {
728 case USB_REQ_DFU_GETSTATUS:
729 handle_getstatus(urb, len);
730 /* FIXME: state transition depending on block completeness */
731 break;
732 case USB_REQ_DFU_GETSTATE:
733 handle_getstate(urb, len);
734 break;
735 default:
736 dev->dfu_state = DFU_STATE_dfuERROR;
737 ret = RET_STALL;
738 goto out;
740 break;
741 case DFU_STATE_dfuDNBUSY:
742 switch (req) {
743 case USB_REQ_DFU_GETSTATUS:
744 /* FIXME: only accept getstatus if bwPollTimeout
745 * has elapsed */
746 handle_getstatus(urb, len);
747 break;
748 default:
749 dev->dfu_state = DFU_STATE_dfuERROR;
750 ret = RET_STALL;
751 goto out;
753 break;
754 case DFU_STATE_dfuDNLOAD_IDLE:
755 switch (req) {
756 case USB_REQ_DFU_DNLOAD:
757 dev->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
758 ret = handle_dnload(urb, val, len, 0);
759 break;
760 case USB_REQ_DFU_ABORT:
761 dev->dfu_state = DFU_STATE_dfuIDLE;
762 ret = RET_ZLP;
763 break;
764 case USB_REQ_DFU_GETSTATUS:
765 handle_getstatus(urb, len);
766 break;
767 case USB_REQ_DFU_GETSTATE:
768 handle_getstate(urb, len);
769 break;
770 default:
771 dev->dfu_state = DFU_STATE_dfuERROR;
772 ret = RET_STALL;
773 break;
775 break;
776 case DFU_STATE_dfuMANIFEST_SYNC:
777 switch (req) {
778 case USB_REQ_DFU_GETSTATUS:
779 /* We're MainfestationTolerant */
780 dev->dfu_state = DFU_STATE_dfuIDLE;
781 handle_getstatus(urb, len);
782 break;
783 case USB_REQ_DFU_GETSTATE:
784 handle_getstate(urb, len);
785 break;
786 default:
787 dev->dfu_state = DFU_STATE_dfuERROR;
788 ret = RET_STALL;
789 break;
791 break;
792 case DFU_STATE_dfuMANIFEST:
793 /* we should never go here */
794 dev->dfu_state = DFU_STATE_dfuERROR;
795 ret = RET_STALL;
796 break;
797 case DFU_STATE_dfuMANIFEST_WAIT_RST:
798 /* we should never go here */
799 break;
800 case DFU_STATE_dfuUPLOAD_IDLE:
801 switch (req) {
802 case USB_REQ_DFU_UPLOAD:
803 /* state transition if less data then requested */
804 rc = handle_upload(urb, val, len, 0);
805 if (rc >= 0 && rc < len)
806 dev->dfu_state = DFU_STATE_dfuIDLE;
807 break;
808 case USB_REQ_DFU_ABORT:
809 dev->dfu_state = DFU_STATE_dfuIDLE;
810 /* no zlp? */
811 ret = RET_ZLP;
812 break;
813 case USB_REQ_DFU_GETSTATUS:
814 handle_getstatus(urb, len);
815 break;
816 case USB_REQ_DFU_GETSTATE:
817 handle_getstate(urb, len);
818 break;
819 default:
820 dev->dfu_state = DFU_STATE_dfuERROR;
821 ret = RET_STALL;
822 break;
824 break;
825 case DFU_STATE_dfuERROR:
826 switch (req) {
827 case USB_REQ_DFU_GETSTATUS:
828 handle_getstatus(urb, len);
829 break;
830 case USB_REQ_DFU_GETSTATE:
831 handle_getstate(urb, len);
832 break;
833 case USB_REQ_DFU_CLRSTATUS:
834 dev->dfu_state = DFU_STATE_dfuIDLE;
835 dev->dfu_status = DFU_STATUS_OK;
836 /* no zlp? */
837 ret = RET_ZLP;
838 break;
839 default:
840 dev->dfu_state = DFU_STATE_dfuERROR;
841 ret = RET_STALL;
842 break;
844 break;
845 default:
846 return DFU_EP0_UNHANDLED;
847 break;
850 out:
851 debug("new_state = %u, ret = %u\n", dev->dfu_state, ret);
853 switch (ret) {
854 case RET_ZLP:
855 //udc_ep0_send_zlp();
856 urb->actual_length = 0;
857 return DFU_EP0_ZLP;
858 break;
859 case RET_STALL:
860 //udc_ep0_send_stall();
861 return DFU_EP0_STALL;
862 break;
863 case RET_NOTHING:
864 break;
867 return DFU_EP0_DATA;
870 void str2wide (char *str, u16 * wide);
871 static struct usb_string_descriptor *create_usbstring(char *string)
873 struct usb_string_descriptor *strdesc;
874 int size = sizeof(*strdesc) + strlen(string)*2;
876 if (size > 255)
877 return NULL;
879 strdesc = malloc(size);
880 if (!strdesc)
881 return NULL;
883 strdesc->bLength = size;
884 strdesc->bDescriptorType = USB_DT_STRING;
885 str2wide(string, strdesc->wData);
887 return strdesc;
891 static void dfu_init_strings(struct usb_device_instance *dev)
893 int i;
894 struct usb_string_descriptor *strdesc;
896 strdesc = create_usbstring(CONFIG_DFU_CFG_STR);
897 usb_strings[DFU_STR_CONFIG] = strdesc;
899 for (i = 0; i < DFU_NUM_ALTERNATES; i++) {
900 if (i == 0) {
901 strdesc = create_usbstring(CONFIG_DFU_ALT0_STR);
902 } else {
903 struct part_info *part = get_partition_nand(i-1);
905 if (part)
906 strdesc = create_usbstring(part->name);
907 else
908 strdesc =
909 create_usbstring("undefined partition");
911 if (!strdesc)
912 continue;
913 usb_strings[STR_COUNT+i+1] = strdesc;
918 #ifdef CONFIG_NAND_DYNPART
920 void dfu_update_strings(void)
922 int i;
924 if (!system_dfu_state) {
925 printf("NASTY SURPRISE: system_dfu_state not set\n");
926 return;
929 for (i = 1; i != DFU_NUM_ALTERNATES; i++) {
930 struct part_info *part = get_partition_nand(i-1);
931 struct usb_string_descriptor *strdesc, **slot;
933 if (part)
934 strdesc = create_usbstring(part->name);
935 else
936 strdesc = create_usbstring("undefined partition");
937 if (!strdesc)
938 continue;
939 slot = usb_strings+STR_COUNT+i+1;
940 if (*slot)
941 free(*slot);
942 *slot = strdesc;
946 #endif /* CONFIG_NAND_DYNPART */
949 int dfu_init_instance(struct usb_device_instance *dev)
951 int i;
953 for (i = 0; i != DFU_NUM_ALTERNATES; i++) {
954 struct usb_interface_descriptor *uif =
955 dfu_cfg_descriptor.uif+i;
957 uif->bLength = USB_DT_INTERFACE_SIZE;
958 uif->bDescriptorType = USB_DT_INTERFACE;
959 uif->bAlternateSetting = i;
960 uif->bInterfaceClass = 0xfe;
961 uif->bInterfaceSubClass = 1;
962 uif->bInterfaceProtocol = 2;
963 uif->iInterface = DFU_STR_ALT(i);
966 dev->dfu_dev_desc = &dfu_dev_descriptor;
967 dev->dfu_cfg_desc = &dfu_cfg_descriptor;
968 dev->dfu_state = DFU_STATE_appIDLE;
969 dev->dfu_status = DFU_STATUS_OK;
971 if (system_dfu_state)
972 printf("SURPRISE: system_dfu_state is already set\n");
973 system_dfu_state = &dev->dfu_state;
975 dfu_init_strings(dev);
977 return 0;
980 static int stdout_switched;
982 /* event handler for usb device state events */
983 void dfu_event(struct usb_device_instance *device,
984 usb_device_event_t event, int data)
986 char *out;
988 switch (event) {
989 case DEVICE_RESET:
990 switch (device->dfu_state) {
991 case DFU_STATE_appDETACH:
992 device->dfu_state = DFU_STATE_dfuIDLE;
993 out = getenv("stdout");
994 if (out && !strcmp(out, "usbtty")) {
995 setenv("stdout", "vga");
996 setenv("stderr", "vga");
997 stdout_switched = 1;
999 printf("DFU: Switching to DFU Mode\n");
1000 break;
1001 case DFU_STATE_dfuMANIFEST_WAIT_RST:
1002 device->dfu_state = DFU_STATE_appIDLE;
1003 printf("DFU: Switching back to Runtime mode\n");
1004 if (stdout_switched) {
1005 setenv("stdout", "usbtty");
1006 setenv("stderr", "usbtty");
1007 stdout_switched = 0;
1009 break;
1010 default:
1011 break;
1013 break;
1014 case DEVICE_CONFIGURED:
1015 case DEVICE_DE_CONFIGURED:
1016 debug("SET_CONFIGURATION(%u) ", device->configuration);
1017 /* fallthrough */
1018 case DEVICE_SET_INTERFACE:
1019 debug("SET_INTERFACE(%u,%u) old_state = %u ",
1020 device->interface, device->alternate,
1021 device->dfu_state);
1022 switch (device->dfu_state) {
1023 case DFU_STATE_appIDLE:
1024 case DFU_STATE_appDETACH:
1025 case DFU_STATE_dfuIDLE:
1026 case DFU_STATE_dfuMANIFEST_WAIT_RST:
1027 /* do nothing, we're fine */
1028 break;
1029 case DFU_STATE_dfuDNLOAD_SYNC:
1030 case DFU_STATE_dfuDNBUSY:
1031 case DFU_STATE_dfuDNLOAD_IDLE:
1032 case DFU_STATE_dfuMANIFEST:
1033 device->dfu_state = DFU_STATE_dfuERROR;
1034 device->dfu_status = DFU_STATUS_errNOTDONE;
1035 /* FIXME: free malloc()ed buffer! */
1036 break;
1037 case DFU_STATE_dfuMANIFEST_SYNC:
1038 case DFU_STATE_dfuUPLOAD_IDLE:
1039 case DFU_STATE_dfuERROR:
1040 device->dfu_state = DFU_STATE_dfuERROR;
1041 device->dfu_status = DFU_STATUS_errUNKNOWN;
1042 break;
1044 debug("new_state = %u\n", device->dfu_state);
1045 break;
1046 default:
1047 break;
1050 #endif /* CONFIG_USBD_DFU */