📄 plusb.c
字号:
* */static void plusb_int_complete(urb_t *purb){ plusb_t *s=purb->context; s->status=((unsigned char*)purb->transfer_buffer)[0]&255; #if 0 /* This isn't right because 0x20 is TX_RDY and sometimes will not be set */ if((s->status&0x3f)!=0x20) { warn("invalid device status %02X", s->status); return; }#endif if(!s->connected) return; /* Don't turn this on unless you want to see the log flooded. */#if 0 printk("plusb_int_complete: PEER_E:%d TX_REQ:%d TX_C:%d RESET_IN:%d RESET_O: %d TX_RDY:%d RES1:%d RES2:%d\n", s->status & _PL_INT_PEER_E ? 1 : 0, s->status & _PL_INT_TX_REQ ? 1 : 0, s->status & _PL_INT_TX_C ? 1 : 0, s->status & _PL_INT_RESET_I ? 1 : 0, s->status & _PL_INT_RESET_O ? 1 : 0, s->status & _PL_INT_TX_RDY ? 1 : 0, s->status & _PL_INT_RES1 ? 1 : 0, s->status & _PL_INT_RES2 ? 1 : 0);#endif#if 1 /* At first glance, this logic appears to not really be needed, but it can help recover from intermittent problems where the usb_submit_urb() fails in the read callback. -EZA */ /* Try to submit the read URB again. Make sure we aren't in contention with the bulk read callback */ submit_read_urb ("plusb_int_complete", s); /* While we are at it, why not check to see if the write urb should be re-submitted? */ dequeue_next_skb("plusb_int_complete", s); #endif}/* --------------------------------------------------------------------- *//* * plusb_free_all - deallocate all memory kept for an instance of the device. */static void plusb_free_all(plusb_t *s){ struct list_head *skb; skb_list_t *skb_list; dbg("plusb_free_all"); /* set a flag to tell all callbacks to cease and desist */ s->connected = 0; /* If the interrupt handler is about to fire, let it finish up */ run_task_queue(&tq_immediate); if(s->inturb) { dbg("unlink inturb"); usb_unlink_urb(s->inturb); dbg("free_urb inturb"); usb_free_urb(s->inturb); s->inturb=NULL; } if(s->interrupt_in_buffer) { dbg("kfree s->interrupt_in_buffer"); kfree(s->interrupt_in_buffer); s->interrupt_in_buffer=NULL; } if(s->readurb) { dbg("unlink readurb"); usb_unlink_urb(s->readurb); dbg("free_urb readurb:"); usb_free_urb(s->readurb); s->readurb=NULL; } if(s->bulk_in_buffer) { dbg("kfree s->bulk_in_buffer"); kfree(s->bulk_in_buffer); s->bulk_in_buffer=NULL; } s->readurb_submitted = 0; if(s->writeurb) { dbg("unlink writeurb"); usb_unlink_urb(s->writeurb); dbg("free_urb writeurb:"); usb_free_urb(s->writeurb); s->writeurb=NULL; } s->writeurb_submitted = 0; while(!list_empty(&s->free_skb_list)) { skb=s->free_skb_list.next; list_del(skb); skb_list = list_entry (skb, skb_list_t, skb_list); kfree(skb_list); } while(!list_empty(&s->tx_skb_list)) { skb=s->tx_skb_list.next; list_del(skb); skb_list = list_entry (skb, skb_list_t, skb_list); if (skb_list->skb) { dbg ("Freeing SKB in queue"); dev_kfree_skb_any(skb_list->skb); skb_list->skb = NULL; } kfree(skb_list); } s->in_bh=0; dbg("plusb_free_all: finished"); }/*-------------------------------------------------------------------*//* * plusb_alloc - allocate memory associated with one instance of the device */static int plusb_alloc(plusb_t *s){ int i; skb_list_t *skb; dbg("plusb_alloc"); for(i=0 ; i < _SKB_NUM ; i++) { skb=kmalloc(sizeof(skb_list_t), GFP_KERNEL); if(!skb) { err("kmalloc for skb_list failed"); goto reject; } memset(skb, 0, sizeof(skb_list_t)); list_add(&skb->skb_list, &s->free_skb_list); } dbg("inturb allocation:"); s->inturb=usb_alloc_urb(0); if(!s->inturb) { err("alloc_urb failed"); goto reject; } dbg("bulk read urb allocation:"); s->readurb=usb_alloc_urb(0); if(!s->readurb) { err("alloc_urb failed"); goto reject; } dbg("bulk write urb allocation:"); s->writeurb=usb_alloc_urb(0); if(!s->writeurb) { err("alloc_urb for writeurb failed"); goto reject; } dbg("readurb/inturb init:"); s->interrupt_in_buffer=kmalloc(64, GFP_KERNEL); if(!s->interrupt_in_buffer) { err("kmalloc failed"); goto reject; } /* The original value of '10' makes this interrupt fire off a LOT. It was set so low because the callback determined when to sumbit the buld read URB. I've lowered it to 100 - the driver doesn't depend on that logic anymore. -EZA */ FILL_INT_URB(s->inturb, s->usbdev, usb_rcvintpipe (s->usbdev, _PLUSB_INTPIPE), s->interrupt_in_buffer, 1, plusb_int_complete, s, HZ); dbg("inturb submission:"); if(usb_submit_urb(s->inturb)<0) { err("usb_submit_urb failed"); goto reject; } dbg("readurb init:"); s->bulk_in_buffer = kmalloc(_BULK_DATA_LEN, GFP_KERNEL); if (!s->bulk_in_buffer) { err("kmalloc %d bytes for bulk in buffer failed", _BULK_DATA_LEN); } FILL_BULK_URB(s->readurb, s->usbdev, usb_rcvbulkpipe(s->usbdev, _PLUSB_BULKINPIPE), s->bulk_in_buffer, _BULK_DATA_LEN, plusb_read_bulk_complete, s); /* The write urb will be initialized inside the network interrupt. */ /* get the bulk read going */ submit_read_urb("plusb_alloc", s); dbg ("plusb_alloc: finished. readurb=%p writeurb=%p inturb=%p", s->readurb, s->writeurb, s->inturb); return 0; reject: dbg("plusb_alloc: failed"); plusb_free_all(s); return -ENOMEM;}/*-------------------------------------------------------------------*/static int plusb_net_open(struct net_device *dev){ plusb_t *s=dev->priv; dbg("plusb_net_open"); if(plusb_alloc(s)) return -ENOMEM; s->opened=1; MOD_INC_USE_COUNT; netif_start_queue(dev); dbg("plusb_net_open: success"); return 0; }/* --------------------------------------------------------------------- */static int plusb_net_stop(struct net_device *dev){ plusb_t *s=dev->priv; netif_stop_queue(dev); dbg("plusb_net_stop"); s->opened=0; plusb_free_all(s); MOD_DEC_USE_COUNT; dbg("plusb_net_stop:finished"); return 0;}/* --------------------------------------------------------------------- */static struct net_device_stats *plusb_net_get_stats(struct net_device *dev){ plusb_t *s=dev->priv; dbg("net_device_stats"); return &s->net_stats;}/* --------------------------------------------------------------------- */static plusb_t *plusb_find_struct (void){ int u; for (u = 0; u < NRPLUSB; u++) { plusb_t *s = &plusb[u]; if (!s->connected) return s; } return NULL;}/* --------------------------------------------------------------------- */static void plusb_disconnect (struct usb_device *usbdev, void *ptr){ plusb_t *s = ptr; dbg("plusb_disconnect"); plusb_free_all(s); if(!s->opened && s->net_dev.name) { dbg("unregistering netdev: %s",s->net_dev.name); unregister_netdev(&s->net_dev); s->net_dev.name[0] = '\0';#if (LINUX_VERSION_CODE < 0x020300) dbg("plusb_disconnect: About to free name"); kfree (s->net_dev.name); s->net_dev.name = NULL;#endif } dbg("plusb_disconnect: finished"); MOD_DEC_USE_COUNT;}/* --------------------------------------------------------------------- */static int plusb_change_mtu(struct net_device *dev, int new_mtu){ if ((new_mtu < 68) || (new_mtu > _BULK_DATA_LEN)) return -EINVAL; printk("plusb: changing mtu to %d\n", new_mtu); dev->mtu = new_mtu; /* NOTE: Could we change the size of the READ URB here dynamically to save kernel memory? */ return 0;}/* --------------------------------------------------------------------- */int plusb_net_init(struct net_device *dev){ dbg("plusb_net_init"); dev->open=plusb_net_open; dev->stop=plusb_net_stop; dev->hard_start_xmit=plusb_net_xmit; dev->get_stats = plusb_net_get_stats; ether_setup(dev); dev->change_mtu = plusb_change_mtu; /* Setting the default MTU to 16K gives good performance for me, and keeps the ping latency low too. Setting it up to 32K made performance go down. -EZA Pavel says it would be best not to do this... */ /*dev->mtu=16384; */ dev->tx_queue_len = 0; dev->flags = IFF_POINTOPOINT|IFF_NOARP; dbg("plusb_net_init: finished"); return 0;}/* --------------------------------------------------------------------- */static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum){ plusb_t *s; dbg("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d", usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); if (usbdev->descriptor.idVendor != 0x067b || usbdev->descriptor.idProduct > 0x1) return NULL; /* We don't handle multiple configurations */ if (usbdev->descriptor.bNumConfigurations != 1) return NULL; s = plusb_find_struct (); if (!s) return NULL; s->usbdev = usbdev; if (usb_set_configuration (s->usbdev, usbdev->config[0].bConfigurationValue) < 0) { err("set_configuration failed"); return NULL; } if (usb_set_interface (s->usbdev, 0, 0) < 0) { err("set_interface failed"); return NULL; }#if (LINUX_VERSION_CODE < 0x020300) { int i; /* For Kernel version 2.2, the driver is responsible for allocating this memory. For version 2.4, the rules have apparently changed, but there is a nifty function 'init_netdev' that might make this easier... It's in ../net/net_init.c - but can we get there from here? (no) -EZA */ /* Find the device number... we seem to have lost it... -EZA */ for (i=0; i<NRPLUSB; i++) { if (&plusb[i] == s) break; } if(!s->net_dev.name) { s->net_dev.name = kmalloc(strlen("plusbXXXX"), GFP_KERNEL); sprintf (s->net_dev.name, "plusb%d", i); s->net_dev.init=plusb_net_init; s->net_dev.priv=s; printk ("plusb_probe: Registering Device\n"); if(!register_netdev(&s->net_dev)) info("registered: %s", s->net_dev.name); else { err("register_netdev failed"); s->net_dev.name[0] = '\0'; } dbg ("plusb_probe: Connected!"); } }#else /* Kernel version 2.3+ works a little bit differently than 2.2 */ if(!s->net_dev.name[0]) { strcpy(s->net_dev.name, "plusb%d"); s->net_dev.init=plusb_net_init; s->net_dev.priv=s; if(!register_netdev(&s->net_dev)) info("registered: %s", s->net_dev.name); else { err("register_netdev failed"); s->net_dev.name[0] = '\0'; } }#endif s->connected = 1; if(s->opened) { dbg("net device already allocated, restarting USB transfers"); plusb_alloc(s); } info("bound to interface: %d dev: %p", ifnum, usbdev); MOD_INC_USE_COUNT; return s;}/* --------------------------------------------------------------------- */static struct usb_driver plusb_driver ={ name: "plusb", probe: plusb_probe, disconnect: plusb_disconnect,};/* --------------------------------------------------------------------- */static int __init plusb_init (void){ unsigned u; dbg("plusb_init"); /* initialize struct */ for (u = 0; u < NRPLUSB; u++) { plusb_t *s = &plusb[u]; memset (s, 0, sizeof (plusb_t)); INIT_LIST_HEAD (&s->tx_skb_list); INIT_LIST_HEAD (&s->free_skb_list); spin_lock_init (&s->lock); } /* register misc device */ usb_register (&plusb_driver); dbg("plusb_init: driver registered"); return 0;}/* --------------------------------------------------------------------- */static void __exit plusb_cleanup (void){ unsigned u; dbg("plusb_cleanup"); for (u = 0; u < NRPLUSB; u++) { plusb_t *s = &plusb[u];#if (LINUX_VERSION_CODE < 0x020300) if(s->net_dev.name) { dbg("unregistering netdev: %s",s->net_dev.name); unregister_netdev(&s->net_dev); s->net_dev.name[0] = '\0'; kfree (s->net_dev.name); s->net_dev.name = NULL; } #else if(s->net_dev.name[0]) { dbg("unregistering netdev: %s",s->net_dev.name); unregister_netdev(&s->net_dev); s->net_dev.name[0] = '\0'; }#endif } usb_deregister (&plusb_driver); dbg("plusb_cleanup: finished");}/* --------------------------------------------------------------------- */MODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de");MODULE_DESCRIPTION ("PL-2302 USB Interface Driver for Linux (c)2000");module_init (plusb_init);module_exit (plusb_cleanup);/* --------------------------------------------------------------------- */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -