📄 usbdfu.c
字号:
staticint usbdfu_download(struct usbdfu *dev, u8 *fw_buf, u32 fw_len){ int ret = 0; if (dev->info->pre_download_hook) { ret = dev->info->pre_download_hook(dev->udev); } if (ret) return ret; info("Downloading firmware for USB device %d...", dev->udev->devnum); ret = do_dfu_download(dev->udev, fw_buf, fw_len); if (ret) return ret; if (dev->info->post_download_hook) { ret = dev->info->post_download_hook(dev->udev); } return ret;}static inline void usbdfu_delete (struct usbdfu *dev){ kfree (dev);}/* shamelessly copied from usbnet.c (oku) */static void defer_kevent (struct usbdfu *dev, int flag){ set_bit (flag, &dev->kevent_flags); if (!schedule_task (&dev->kevent)) err ("kevent %d may have been dropped", flag); else dbg ("kevent %d scheduled", flag);}static void kevent_timer(unsigned long data){ struct usbdfu *dev = (struct usbdfu *)data; defer_kevent(dev, KEVENT_FLAG_RESET);/* jal: this hangs SMP systems. no need to stop the timer, as it is non-periodic */// del_timer_sync(&dev->timer);}/* TODO: how do we make sure the device hasn't been plugged out in the meantime? *//* We don't really need to (trying to reset a disconnected device shouldn't * cause a problem), we just need to make sure that disconnect hasn't freed the * dev structure already. We do this by not freeing dev as long as * "kevent_flags" has something set (indicating a kevent is pending) --alex */static voidkevent(void *data){ struct usbdfu *dev = data; struct usb_device *udev; struct usbdfu_info *info; struct usb_interface *interface; dbg("kevent entered"); /* some paranoid checks: */ if(!dev){ err("kevent: no dev!"); return; } dfu_down(&dev->sem); info = dev->info; if(!info){ err("kevent: no dev->info!"); goto exit; } udev = dev->udev; if(!udev){ err("kevent: no device!"); goto exit; } if (test_bit(KEVENT_FLAG_SCHEDRESET, &dev->kevent_flags)) { clear_bit(KEVENT_FLAG_SCHEDRESET, &dev->kevent_flags); defer_kevent (dev, KEVENT_FLAG_RESET); } else if (test_bit(KEVENT_FLAG_RESET, &dev->kevent_flags)) { clear_bit(KEVENT_FLAG_RESET, &dev->kevent_flags); /* releasing interface, so it can be claimed by our fellow driver */ interface = &udev->actconfig->interface[0]; usb_driver_release_interface(&usbdfu_driver, interface); /* Once we release the interface, the USB system won't call * usbdfu_disconnect for us, so we need to do that ourselves. * Note: we cannot use dev after this point. */ dfu_up(&dev->sem); usbdfu_disconnect(udev, dev); dbg("resetting device"); usb_reset_device(udev); dbg("scanning unclaimed devices"); usb_scan_devices(); return; } exit: dfu_up(&dev->sem); return;}int usbdfu_register(struct usbdfu_info *info){ struct usbdfu_infolist *infolist = kmalloc(sizeof(struct usbdfu_infolist), GFP_KERNEL); if(!infolist) return -ENOMEM; infolist->info = info; dfu_down(&usbdfu_lock); list_add_tail(&infolist->list, &usbdfu_infolist_head); dfu_up(&usbdfu_lock); /* before scan, because that calls probe() */ dbg("registered new driver %s", info->name); /* if the device is not yet plugged in, we are settled. If it already is (and it's already in DFU state), we have to scan for unclaimed devices. This will call our probe function again. */ usb_scan_devices(); return 0;}void usbdfu_deregister(struct usbdfu_info *info){ struct list_head *tmp; struct usbdfu_infolist *infolist = NULL; dbg("deregistering driver %s", info->name); dfu_down(&usbdfu_lock); for(tmp = usbdfu_infolist_head.next; tmp != &usbdfu_infolist_head; tmp = tmp->next) { infolist = list_entry(tmp, struct usbdfu_infolist, list); if(infolist->info == info) break; } if(tmp != &usbdfu_infolist_head){ list_del(tmp); kfree(infolist); } else { err("unregistering %s: driver was not previously registered!", info->name); } dfu_up(&usbdfu_lock);}int usbdfu_in_use(struct usb_device *udev, unsigned int ifnum){ int result; u8 state; struct usb_interface *interface; struct usb_interface_descriptor *idesc; if (ifnum != 0) { /* DFU-mode devices only have one interface */ return 0; } /* Check to see whether the interface's class is a DFU device. * We need to check this first to make sure the DFU_GETSTATE command * isn't misinterpreted as something else. */ interface = &udev->actconfig->interface[ifnum]; idesc = &interface->altsetting[interface->act_altsetting]; if ((idesc->bInterfaceClass != DFU_USB_CLASS) || (idesc->bInterfaceSubClass != DFU_USB_SUBCLASS)) { dbg("interface class is not DFU"); return 0; } result = dfu_get_state(udev, &state); if (result < 0) { return result; } else if (result != 1) { /* This should be an error. The device reported this interface * as a DFU-class interface, but it's not responding correctly * to DFU-class commands on this interface. However, there * appear to be some broken devices out there where this is * normal behavior in some cases (at76c503 immediately after * fw-load-reset), so just continue on (and hope we didn't * screw anything up with that DFU command).. */ dbg("DFU state query returned %d-byte response", result); return 0; } switch (state) { case STATE_IDLE: case STATE_DETACH: /* Device is in an application mode, it's up to other drivers * to deal with it */ dbg("DFU state=App (%d)", state); return 0; case STATE_DFU_IDLE: case STATE_DFU_DOWNLOAD_SYNC: case STATE_DFU_DOWNLOAD_BUSY: case STATE_DFU_DOWNLOAD_IDLE: case STATE_DFU_MANIFEST_SYNC: case STATE_DFU_MANIFEST: case STATE_DFU_MANIFEST_WAIT_RESET: case STATE_DFU_UPLOAD_IDLE: case STATE_DFU_ERROR: /* This is what we're looking for. We're in the middle * of dealing with this device */ dbg("DFU state=DFU (%d)", state); return 1; default: /* We got something that shouldn't be a valid response to a DFU * state query. Again, this sometimes happens on broken * devices (at76c503 immediately after fw-load-reset) which * report DFU class but aren't really DFU-capable. */ dbg("DFU state query returned bizarre response (%d)", state); return 0; }}static struct usbdfu_info *find_info(struct usb_device *udev){ struct usb_interface *interface; struct list_head *tmp; struct usbdfu_infolist *infolist; dbg("searching for driver"); interface = &udev->actconfig->interface[0]; for(tmp = usbdfu_infolist_head.next; tmp != &usbdfu_infolist_head; tmp = tmp->next) { infolist = list_entry(tmp, struct usbdfu_infolist, list); if(usb_match_id(udev, interface, infolist->info->id_table)) return infolist->info; } return NULL;}int usbdfu_initiate_download(struct usb_device *udev){ int result; if (!find_info(udev)) { return -ENOENT; } result = dfu_detach(udev); if (!result) { dbg("dfu_detach failed (%d)", result); return result; } usb_reset_device(udev); usb_scan_devices(); return 0;}static void * usbdfu_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id){ struct usbdfu *dev = NULL; struct usbdfu_info *info = NULL; int ret; dbg("usbdfu_probe entered"); if (ifnum != 0) { dbg("more than one interface, cannot be DFU mode"); return NULL; } dfu_down(&usbdfu_lock); info = find_info(udev); if (!info) goto exit; /* not for us */ dbg("device is registered (%s)", info->name); if (usbdfu_in_use(udev, ifnum) != 1) { dbg("device not in DFU-idle mode"); goto exit; } dbg("device is in DFU mode"); /* allocate memory for our device state and intialize it */ dev = kmalloc (sizeof(struct usbdfu), GFP_KERNEL); if (dev == NULL) { err ("out of memory"); goto exit; } memset (dev, 0, sizeof (*dev)); init_MUTEX (&dev->sem); dfu_down(&dev->sem); INIT_TQUEUE (&dev->kevent, kevent, dev); dev->udev = udev; dev->info = info; dbg("going for download"); /* here our main action takes place: */ ret = usbdfu_download(dev, info->fw_buf, info->fw_buf_len); if(ret < 0){ err("Firmware download failed for USB device %d", udev->devnum); goto error; } init_timer(&dev->timer); if(info->reset_delay){ dev->timer.data = (long) dev; dev->timer.function = kevent_timer; mod_timer(&dev->timer, jiffies + info->reset_delay); }else{ defer_kevent (dev, KEVENT_FLAG_SCHEDRESET); } dfu_up(&dev->sem); goto exit; error: dfu_up(&dev->sem); usbdfu_delete (dev); dev = NULL;exit: dfu_up(&usbdfu_lock); dbg("usbdfu_probe() exiting"); return dev;}static void usbdfu_disconnect(struct usb_device *udev, void *ptr){ struct usbdfu *dev = (struct usbdfu *)ptr; int kevent_pending; dbg("usbdfu_disconnect called"); while (1) { dfu_down(&dev->sem); kevent_pending = dev->kevent_flags; dfu_up(&dev->sem); if (!kevent_pending) break; dbg("usbdfu_disconnect: waiting for kevent to complete (%d pending)...", kevent_pending); schedule(); } del_timer_sync(&dev->timer); usbdfu_delete(dev); dbg("USB DFU now disconnected");}static int __init usbdfu_init(void){ int result; info(DRIVER_DESC " " DRIVER_VERSION); init_MUTEX(&usbdfu_lock); /* register this driver with the USB subsystem */ result = usb_register(&usbdfu_driver); if (result < 0) { err("usb_register failed for the "__FILE__" driver. Error number %d", result); return -1; } return 0;}/** * usbdfu_exit */static void __exit usbdfu_exit(void){ /* deregister this driver with the USB subsystem */ usb_deregister(&usbdfu_driver);}module_init (usbdfu_init);module_exit (usbdfu_exit);EXPORT_SYMBOL(usbdfu_register);EXPORT_SYMBOL(usbdfu_deregister);EXPORT_SYMBOL(usbdfu_in_use);EXPORT_SYMBOL(usbdfu_initiate_download);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -