📄 speedtch.c
字号:
break; default: if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]); } break; }}static void speedtch_timer_poll(unsigned long data){ struct speedtch_instance_data *instance = (void *)data; schedule_work(&instance->poll_work); mod_timer(&instance->poll_timer, jiffies + (5 * HZ));}#ifdef USE_FW_LOADERstatic void speedtch_upload_firmware(struct speedtch_instance_data *instance, const struct firmware *fw1, const struct firmware *fw2){ unsigned char *buffer; struct usb_device *usb_dev = instance->u.usb_dev; struct usb_interface *intf; int actual_length, ret; int offset; dbg("speedtch_upload_firmware"); if (!(intf = usb_ifnum_to_if(usb_dev, 2))) { dbg("speedtch_upload_firmware: interface not found!"); goto fail; } if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) { dbg("speedtch_upload_firmware: no memory for buffer!"); goto fail; } /* A user-space firmware loader may already have claimed interface #2 */ if ((ret = usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) { dbg("speedtch_upload_firmware: interface in use (%d)!", ret); goto fail_free; } /* URB 7 */ if (dl_512_first) { /* some modems need a read before writing the firmware */ ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), buffer, 0x200, &actual_length, 2 * HZ); if (ret < 0 && ret != -ETIMEDOUT) dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret); else dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret); } /* URB 8 : both leds are static green */ for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) { int thislen = min_t(int, PAGE_SIZE, fw1->size - offset); memcpy(buffer, fw1->data + offset, thislen); ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), buffer, thislen, &actual_length, DATA_TIMEOUT); if (ret < 0) { dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret); goto fail_release; } dbg("speedtch_upload_firmware: BLOCK1 uploaded (%zu bytes)", fw1->size); } /* USB led blinking green, ADSL led off */ /* URB 11 */ ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), buffer, 0x200, &actual_length, DATA_TIMEOUT); if (ret < 0) { dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret); goto fail_release; } dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length); /* URBs 12 to 139 - USB led blinking green, ADSL led off */ for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) { int thislen = min_t(int, PAGE_SIZE, fw2->size - offset); memcpy(buffer, fw2->data + offset, thislen); ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), buffer, thislen, &actual_length, DATA_TIMEOUT); if (ret < 0) { dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret); goto fail_release; } } dbg("speedtch_upload_firmware: BLOCK3 uploaded (%zu bytes)", fw2->size); /* USB led static green, ADSL led static red */ /* URB 142 */ ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), buffer, 0x200, &actual_length, DATA_TIMEOUT); if (ret < 0) { dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret); goto fail_release; } /* success */ dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length); /* Delay to allow firmware to start up. We can do this here because we're in our own kernel thread anyway. */ msleep(1000); /* Enable software buffering, if requested */ if (sw_buffering) speedtch_set_swbuff(instance, 1); /* Magic spell; don't ask us what this does */ speedtch_test_sequence(instance); /* Start modem synchronisation */ if (speedtch_start_synchro(instance)) dbg("speedtch_start_synchro: failed"); speedtch_got_firmware(instance, 1); free_page((unsigned long)buffer); return; fail_release: /* Only release interface #2 if uploading failed; we don't release it we succeeded. This prevents the userspace tools from trying to load the firmware themselves */ usb_driver_release_interface(&speedtch_usb_driver, intf); fail_free: free_page((unsigned long)buffer); fail: speedtch_got_firmware(instance, 0);}static int speedtch_find_firmware(struct speedtch_instance_data *instance, int phase, const struct firmware **fw_p){ char buf[24]; const u16 bcdDevice = le16_to_cpu(instance->u.usb_dev->descriptor.bcdDevice); const u8 major_revision = bcdDevice >> 8; const u8 minor_revision = bcdDevice & 0xff; sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision); dbg("speedtch_find_firmware: looking for %s", buf); if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision); dbg("speedtch_find_firmware: looking for %s", buf); if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { sprintf(buf, "speedtch-%d.bin", phase); dbg("speedtch_find_firmware: looking for %s", buf); if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase); return -ENOENT; } } } dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf); return 0;}static int speedtch_load_firmware(void *arg){ const struct firmware *fw1, *fw2; struct speedtch_instance_data *instance = arg; BUG_ON(!instance); daemonize("firmware/speedtch"); if (!speedtch_find_firmware(instance, 1, &fw1)) { if (!speedtch_find_firmware(instance, 2, &fw2)) { speedtch_upload_firmware(instance, fw1, fw2); release_firmware(fw2); } release_firmware(fw1); } /* In case we failed, set state back to NO_FIRMWARE so that another later attempt may work. Otherwise, we never actually manage to recover if, for example, the firmware is on /usr and we look for it too early. */ speedtch_got_firmware(instance, 0); module_put(THIS_MODULE); udsl_put_instance(&instance->u); return 0;}#endif /* USE_FW_LOADER */static void speedtch_firmware_start(struct speedtch_instance_data *instance){#ifdef USE_FW_LOADER int ret;#endif dbg("speedtch_firmware_start"); down(&instance->u.serialize); /* vs self, speedtch_got_firmware */ if (instance->u.status >= UDSL_LOADING_FIRMWARE) { up(&instance->u.serialize); return; } instance->u.status = UDSL_LOADING_FIRMWARE; up(&instance->u.serialize);#ifdef USE_FW_LOADER udsl_get_instance(&instance->u); try_module_get(THIS_MODULE); ret = kernel_thread(speedtch_load_firmware, instance, CLONE_FS | CLONE_FILES); if (ret >= 0) return; /* OK */ dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret); module_put(THIS_MODULE); udsl_put_instance(&instance->u); /* Just pretend it never happened... hope modem_run happens */#endif /* USE_FW_LOADER */ speedtch_got_firmware(instance, 0);}static int speedtch_firmware_wait(struct udsl_instance_data *instance){ speedtch_firmware_start((void *)instance); if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0) return -ERESTARTSYS; return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN;}/************ USB ************/static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code, void *user_data){ struct speedtch_instance_data *instance = usb_get_intfdata(intf); dbg("speedtch_usb_ioctl entered"); if (!instance) { dbg("speedtch_usb_ioctl: NULL instance!"); return -ENODEV; } switch (code) { case UDSL_IOCTL_LINE_UP: instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; speedtch_got_firmware(instance, 1); return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO; case UDSL_IOCTL_LINE_DOWN: instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; return 0; default: return -ENOTTY; }}static int speedtch_usb_probe(struct usb_interface *intf, const struct usb_device_id *id){ struct usb_device *dev = interface_to_usbdev(intf); int ifnum = intf->altsetting->desc.bInterfaceNumber; struct speedtch_instance_data *instance; unsigned char mac_str[13]; int ret, i; char buf7[SIZE_7]; dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct), ifnum); if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || (ifnum != 1)) return -ENODEV; dbg("speedtch_usb_probe: device accepted"); /* instance init */ instance = kmalloc(sizeof(*instance), GFP_KERNEL); if (!instance) { dbg("speedtch_usb_probe: no memory for instance data!"); return -ENOMEM; } memset(instance, 0, sizeof(struct speedtch_instance_data)); if ((ret = usb_set_interface(dev, 0, 0)) < 0) goto fail; if ((ret = usb_set_interface(dev, 2, 0)) < 0) goto fail; instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA; instance->u.firmware_wait = speedtch_firmware_wait; instance->u.driver_name = speedtch_driver_name; ret = udsl_instance_setup(dev, &instance->u); if (ret) goto fail; init_timer(&instance->poll_timer); instance->poll_timer.function = speedtch_timer_poll; instance->poll_timer.data = (unsigned long)instance; INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance); /* set MAC address, it is stored in the serial number */ memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi)); if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) { for (i = 0; i < 6; i++) instance->u.atm_dev->esi[i] = (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1])); } /* First check whether the modem already seems to be alive */ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ / 2); if (ret == SIZE_7) { dbg("firmware appears to be already loaded"); speedtch_got_firmware(instance, 1); speedtch_poll_status(instance); } else { speedtch_firmware_start(instance); } usb_set_intfdata(intf, instance); return 0; fail: kfree(instance); return -ENOMEM;}static void speedtch_usb_disconnect(struct usb_interface *intf){ struct speedtch_instance_data *instance = usb_get_intfdata(intf); dbg("speedtch_usb_disconnect entered"); if (!instance) { dbg("speedtch_usb_disconnect: NULL instance!"); return; }/*QQ need to handle disconnects on interface #2 while uploading firmware *//*QQ and what about interface #1? */ if (instance->int_urb) { struct urb *int_urb = instance->int_urb; instance->int_urb = NULL; wmb(); usb_unlink_urb(int_urb); usb_free_urb(int_urb); } instance->int_data[0] = 1; del_timer_sync(&instance->poll_timer); wmb(); flush_scheduled_work(); udsl_instance_disconnect(&instance->u); /* clean up */ usb_set_intfdata(intf, NULL); udsl_put_instance(&instance->u);}/************* init *************/static int __init speedtch_usb_init(void){ dbg("speedtch_usb_init: driver version " DRIVER_VERSION); return usb_register(&speedtch_usb_driver);}static void __exit speedtch_usb_cleanup(void){ dbg("speedtch_usb_cleanup entered"); usb_deregister(&speedtch_usb_driver);}module_init(speedtch_usb_init);module_exit(speedtch_usb_cleanup);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");MODULE_VERSION(DRIVER_VERSION);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -