On Sat, 7 Jul 2001, Brad Hards wrote: > [EMAIL PROTECTED] wrote: > > > > I've written a small program to download firmware to a device (according > > to the USB DFU specs). This generates several timeouts on the control > > pipe, which kinda kills the firmware upgrade. I've tried several packet > > sizes (100, 500, 512, 1024, the interface descriptor says 1024 byte max), > > with the same result. > > Your style is better than my hack, so I fixed yours instead. Looked like you > were using the firmware block without malloc'ing it first, so I added some > code for this too. I malloc'ed the firmware memory in the ReadFirmware function. You're loosing 32k of memory :) > > > Can anyone give me a hand with this? I've got to use DFU to load up the > > firmware in my Wireless USB adapter. I've included my little program > > for good measure. If you see anything that might be causing timeouts, > > please let me know. > > Setting timeout parameter too short :) 100 is probably overkill, but time > isn't really critical with this sort of application, so crude and working > beats optimal and flakey. I had the timeout for the Download stuff at 50000. It just didn't work. I just tried your version, and that doesn't work either. What controller are you using? Mine is a i815 with usb-uhci on 2.4.6. Should I be trying this on 2.4.6-ac1? > See attached. This produces the following: *grmbl* Doesn't on my box. I keep getting Connection timeouts and Broken pipes. I've changed my version to match yours (except the GetStatus struct changes, I don't really like those), with timeout = 100 for all stuff except the DFUDownload, where I've got a timeout of 5000000. This results in the following: Start FW Downloading... DFU IDLE DOWNLOADING 1024 Bytes (Block = 0) STATE_DFU_DOWNLOAD_SYNC DOWNLOAD DFU IDLE DOWNLOADING 1024 Bytes (Block = 1) STATE_DFU_DOWNLOAD_SYNC DOWNLOAD DFU IDLE DOWNLOADING 1024 Bytes (Block = 2) STATE_DFU_DOWNLOAD_SYNC DOWNLOAD DFU IDLE DOWNLOADING 1024 Bytes (Block = 3) STATE_DFU_DOWNLOAD_SYNC DOWNLOAD DFU IDLE DOWNLOADING 1024 Bytes (Block = 4) Write FW Block 4 failed (-1: error sending control message: Connection timed out)! In my dmesg I see the following: hub.c: USB new device connect on bus1/1, assigned device number 21 usb.c: USB device 21 (vend/prod 0x3eb/0x7603) is not claimed by any active driver. usb-uhci.c: interrupt, status 2, frame# 1978 usbdevfs: USBDEVFS_CONTROL failed dev 21 rqt 33 rq 1 len 1024 ret -110 > I am trying to get back to working on the AT76C503A, since it seems we have a > few interested people. Yeah, I've seen the mails as well. I'm working on a kernel mode dfu driver at this point, so I don't have to worry about timeouts because of userspace. I've theoretically got it working, but it needs a userspace counterpart and some testing. The included code is my current base for the dfu module. I think the ioctl model should work without a problem, but as I said, I need the userspace counterpart to go with it before I can test this. Any comments/hints/tips are appreciated, Bas Vermeulen -- "God, root, what is difference?" -- Pitr, User Friendly "God is more forgiving." -- Dave Aronson
/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/proc_fs.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/usb.h> #include <linux/smp_lock.h> #include <linux/ioctl.h> #include <linux/delay.h> MODULE_AUTHOR("Bas Vermeulen <[EMAIL PROTECTED]>"); MODULE_DESCRIPTION("USB Device Firmware Upgrade subsystem v0.0.1"); struct usbdevfs_dfu_download { unsigned short index; unsigned char buffer[1024]; unsigned long size; }; struct usbdevfs_dfu_upload { unsigned short index; unsigned char buffer[1024]; unsigned long size; }; #define USBDEVFS_DFU_DOWNLOAD _IOR('U', 22, struct usbdevfs_dfu_download) #define USBDEVFS_DFU_UPLOAD _IOW('U', 23, struct usbdevfs_dfu_upload) static void *usb_dfu_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id); static void usb_dfu_disconnect(struct usb_device *dev, void *ptr); static int usb_dfu_ioctl(struct usb_device *dev, unsigned int ifno, unsigned int code, void *buf); /* We will attempt to probe anything that has a DFU class. * We will sort through them later, when we've got * the firmware file from userspace. */ static const struct usb_device_id usb_dfu_table[] = { {USB_INTERFACE_INFO(0xfe, 1, 0)}, {} }; MODULE_DEVICE_TABLE(usb, usb_dfu_table); static struct usb_driver usb_dfu_driver = { name: "dfu", probe: &usb_dfu_probe, disconnect: &usb_dfu_disconnect, ioctl: &usb_dfu_ioctl, id_table: usb_dfu_table, }; struct usb_dfu_context { struct usb_device *usb; unsigned int ifnum; unsigned int dfu_download_index; unsigned int dfu_download_size; unsigned int dfu_download_progress; unsigned char *dfu_download_buffer; unsigned int dfu_upload_index; unsigned int dfu_upload_size; unsigned int dfu_upload_progress; unsigned char *dfu_upload_buffer; }; /* DFU routines */ #define STATE_IDLE 0x00 #define STATE_DETACH 0x01 #define STATE_DFU_IDLE 0x02 #define STATE_DFU_DOWNLOAD_SYNC 0x03 #define STATE_DFU_DOWNLOAD_BUSY 0x04 #define STATE_DFU_DOWNLOAD_IDLE 0x05 #define STATE_DFU_MANIFEST_SYNC 0x06 #define STATE_DFU_MANIFEST 0x07 #define STATE_DFU_MANIFEST_WAIT_RESET 0x08 #define STATE_DFU_UPLOAD_IDLE 0x09 #define STATE_DFU_ERROR 0x0a #define STATUS_SUCCESS 1 #define STATUS_UNSUCCESSFUL -1 #define STATUS_INVALID_PARAMETER -9999 #define DFU_DETACH 0 #define DFU_DNLOAD 1 #define DFU_UPLOAD 2 #define DFU_GETSTATUS 3 #define DFU_CLRSTATUS 4 #define DFU_GETSTATE 5 #define DFU_ABORT 6 #define TRUE 1 #define FALSE 0 int dfu_detach(struct usb_device *dev) { int result; dbg("DFU Detach\n"); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), DFU_DETACH, USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE, 1000, /* Value */ 0, /* Index */ NULL, /* Buffer */ 0, /* Size */ 5000); return result; } int dfu_download(struct usb_device *dev, unsigned char *Buffer, unsigned long Bytes, unsigned short Block) { int result; dbg("DOWNLOADING %ld Bytes (Block = %d)\n", Bytes, Block); result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), DFU_DNLOAD, USB_TYPE_CLASS|USB_DIR_OUT|USB_RECIP_INTERFACE, Block, /* Value */ 0, /* Index */ Buffer, /* Buffer */ Bytes, /* Size */ 5000000); return result; } int dfu_get_status(struct usb_device *dev, unsigned char *status) { int result; result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), DFU_GETSTATUS, USB_TYPE_CLASS|USB_DIR_IN|USB_RECIP_INTERFACE, 0, /* Value */ 0, /* Index */ status, /* Buffer */ 6, /* Size */ 5000); return result; } unsigned char dfu_get_state(struct usb_device *dev, unsigned char *state) { int result; result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), DFU_GETSTATE, /* Request */ USB_TYPE_CLASS|USB_DIR_IN|USB_RECIP_INTERFACE, 0, /* Value */ 0, /* Index */ state, /* Buffer */ 1, /* Size */ 5000); return result; } int DownloadFirmware(struct usb_device *dev, unsigned char *DfuBuffer, unsigned int DfuLen) { int status = STATUS_SUCCESS; int NeedDFUState = TRUE; int IsDone = FALSE; unsigned char DfuStatusBuffer[6]; unsigned char DfuState = 0; unsigned long DfuTimeout = 0; unsigned long DfuBlockBytes, DfuBytesLeft, DfuBufferOffset; unsigned long DfuBlockCnt; int t; DfuBlockCnt = 0; DfuBytesLeft = DfuLen; DfuBlockBytes = 0; DfuBufferOffset = 0; if (DfuLen == 0) { err("FW Buffer length invalid!\n"); return STATUS_UNSUCCESSFUL; } dbg("Start FW Downloading...\n"); do { if (NeedDFUState) { status = dfu_get_state(dev, &DfuState); if (status <= 0) { err("DFU: Failed to get DFU state...\n"); return STATUS_UNSUCCESSFUL; } NeedDFUState = FALSE; } switch (DfuState) { case STATE_DFU_DOWNLOAD_SYNC: dbg("STATE_DFU_DOWNLOAD_SYNC\n"); if (dfu_get_status(dev, DfuStatusBuffer) > 0) { DfuState = DfuStatusBuffer[4]; DfuTimeout = 0; DfuTimeout = (unsigned long) (DfuStatusBuffer[3] << 16); DfuTimeout |= (unsigned long) (DfuStatusBuffer[2] << 8); DfuTimeout |= (unsigned long) (DfuStatusBuffer[1]); NeedDFUState = FALSE; } break; case STATE_DFU_DOWNLOAD_BUSY: dbg("STATE_DFU_DOWNLOAD_BUSY\n"); NeedDFUState = TRUE; if (DfuTimeout > 0) info("DFU: Resetting device\n"); else info("DFU: In progress\n"); for (t = 1; t <= DfuTimeout / 500; t++) mdelay(500); /* Sleep for 500 ms */ break; case STATE_DFU_DOWNLOAD_IDLE: dbg("DOWNLOAD "); case STATE_DFU_IDLE: dbg("DFU IDLE\n"); if (DfuBytesLeft <= 1024 /* DFU_PACKETSIZE */ ) DfuBlockBytes = DfuBytesLeft; else DfuBlockBytes = 1024 /* DFU_PACKETSIZE */ ; if (status > 0) { DfuBytesLeft -= DfuBlockBytes; status = dfu_download(dev, DfuBuffer + DfuBufferOffset, DfuBlockBytes, DfuBlockCnt); DfuBufferOffset += DfuBlockBytes; DfuBlockCnt++; } NeedDFUState = TRUE; break; case STATE_DFU_MANIFEST_SYNC: dbg("STATE_DFU_MANIFEST_SYNC\n"); status = dfu_get_status(dev, DfuStatusBuffer); if (status > 0) { DfuState = DfuStatusBuffer[4]; DfuTimeout = 0; DfuTimeout = (unsigned long) (DfuStatusBuffer[3] << 16); DfuTimeout |= (unsigned long) (DfuStatusBuffer[2] << 8); DfuTimeout |= (unsigned long) (DfuStatusBuffer[1]); NeedDFUState = FALSE; if (DfuTimeout > 0) dbg("DFU: Resetting device\n"); else dbg("DFU: In progress\n"); for (t = 1; t <= DfuTimeout / 500; t++) mdelay(500); /* Sleep for 500 ms */ } break; case STATE_DFU_MANIFEST: dbg("STATE_DFU_MANIFEST\n"); IsDone = TRUE; break; case STATE_DFU_MANIFEST_WAIT_RESET: dbg("STATE_DFU_MANIFEST_WAIT_RESET\n"); usb_reset_device(dev); break; case STATE_DFU_UPLOAD_IDLE: dbg("STATE_DFU_UPLOAD_IDLE\n"); break; case STATE_DFU_ERROR: dbg("STATE_DFU_ERROR\n"); status = STATUS_INVALID_PARAMETER; break; default: err("DFU UNKNOWN STATE (%d)\n", DfuState); status = STATUS_INVALID_PARAMETER; break; } } while (!IsDone && status > 0); return status; } /* USB Entry points */ static void *usb_dfu_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) { struct usb_interface *ifp; struct usb_interface_descriptor *if_desc; struct usb_dfu_context *dfu; dbg("(usb_dfu_probe) called."); ifp = &(dev->actconfig->interface[ifnum]); if_desc = &(ifp->altsetting[0]); if ((if_desc->bInterfaceClass != 0xfe) || (if_desc->bInterfaceSubClass != 0x01) || (if_desc->bInterfaceProtocol != 0x00)) { err("probe fails -> Wrong Interface"); return 0; } if (usb_interface_claimed(ifp)) { err("probe fails -> Interface already claimed"); return 0; } if (!(dfu = kmalloc(sizeof(struct usb_dfu_context), GFP_KERNEL))) { err("probe fails -> Out of memory allocating device context"); return 0; } memset(dfu, 0, sizeof(struct usb_dfu_context)); dfu->usb = dev; dfu->ifnum = ifnum; usb_driver_claim_interface(&usb_dfu_driver, ifp, dfu); MOD_INC_USE_COUNT; usb_inc_dev_use(dev); return NULL; } static void usb_dfu_disconnect(struct usb_device *dev, void *ptr) { struct usb_dfu_context *dfu = ptr; dbg("(usb_dfu_disconnect) called"); usb_driver_release_interface(&usb_dfu_driver, &dev->actconfig->interface[dfu-> ifnum]); kfree(dfu); usb_dec_dev_use(dev); MOD_DEC_USE_COUNT; } static int usb_dfu_ioctl(struct usb_device *dev, unsigned int ifno, unsigned int code, void *buf) { int retval = 0; struct usb_dfu_context *dfu = (struct usb_dfu_context *) dev->actconfig->interface[ifno]. private_data; switch (code) { case USBDEVFS_DFU_DOWNLOAD: { struct usbdevfs_dfu_download *download = (struct usbdevfs_dfu_download *) buf; if (download->index == 0) { /* Index == 0; * - Free any allocated buffer * - Allocate the new buffer * - Set size, progress and index */ if (dfu->dfu_download_buffer) { kfree(dfu->dfu_download_buffer); dfu->dfu_download_buffer = NULL; } if (download->size > 0) { if (! (dfu->dfu_download_buffer = kmalloc(download->size, GFP_DMA))) { err("ioctl -> could not allocate memory!"); return -ENOMEM; } dfu->dfu_download_size = download->size; dfu->dfu_download_progress = 0; dfu->dfu_download_index = 1; } else { err("ioctl -> FW size is 0."); return -EINVAL; } } else if (download->index == dfu->dfu_download_index) { /* Check the size. We're in download mode, so size can't be bigger than 1024. */ if ((download->size < 0) || (download->size > 1024)) { err("ioctl -> invalid size"); return -EINVAL; } if (dfu->dfu_download_progress + download->size > dfu->dfu_download_size) { err("ioctl -> buffer too large!"); return -E2BIG; } memcpy(dfu->dfu_download_buffer + dfu->dfu_download_progress, download->buffer, download->size); dfu->dfu_download_progress += download->size; if (dfu->dfu_download_progress == dfu->dfu_download_size) { /* We've finished getting the file from userspace. * Now we have to download it to the device. */ /* Reset the download state. */ dfu->dfu_download_index = 0; kfree(dfu->dfu_download_buffer); dfu->dfu_download_buffer = NULL; return dfu->dfu_download_size; } dfu->dfu_download_index++; } else { err("ioctl -> out of order request"); return -EINVAL; } break; } case USBDEVFS_DFU_UPLOAD: break; default: return -ENOTSUPP; } return retval; } /* Module Entry Points */ static int __init dfu_init(void) { usb_register(&usb_dfu_driver); return 0; } static void __exit dfu_exit(void) { usb_deregister(&usb_dfu_driver); } module_init(dfu_init); module_exit(dfu_exit);