📄 usbnet.c
字号:
// put into "known safe" state if (info->reset && (retval = info->reset (dev)) < 0) { devinfo (dev, "open reset fail (%d) usbnet %03d/%03d, %s", retval, dev->udev->bus->busnum, dev->udev->devnum, info->description); goto done; } // insist peer be connected if (info->check_connect && (retval = info->check_connect (dev)) < 0) { devdbg (dev, "can't open; %d", retval); goto done; } netif_start_queue (net); devdbg (dev, "open: enable queueing (rx %d, tx %d) mtu %d %s framing", RX_QLEN, TX_QLEN, dev->net.mtu, (info->flags & (FLAG_FRAMING_NC | FLAG_FRAMING_GL)) ? ((info->flags & FLAG_FRAMING_NC) ? "NetChip" : "GeneSys") : "raw" ); // delay posting reads until we're fully open tasklet_schedule (&dev->bh);done: mutex_unlock (&dev->mutex); return retval;}/*-------------------------------------------------------------------------*//* usb_clear_halt cannot be called in interrupt context */static voidtx_clear_halt (void *data){ struct usbnet *dev = data; usb_clear_halt (dev->udev, usb_sndbulkpipe (dev->udev, dev->driver_info->out)); netif_wake_queue (&dev->net);}/*-------------------------------------------------------------------------*/static void tx_complete (struct urb *urb){ struct sk_buff *skb = (struct sk_buff *) urb->context; struct skb_data *entry = (struct skb_data *) skb->cb; struct usbnet *dev = entry->dev; if (urb->status == USB_ST_STALL) { if (dev->ctrl_task.sync == 0) { dev->ctrl_task.routine = tx_clear_halt; dev->ctrl_task.data = dev; schedule_task (&dev->ctrl_task); } else { dbg ("Cannot clear TX stall"); } } urb->dev = 0; entry->state = tx_done; defer_bh (dev, skb);}/*-------------------------------------------------------------------------*/static void usbnet_tx_timeout (struct net_device *net){ struct usbnet *dev = (struct usbnet *) net->priv; unlink_urbs (&dev->txq); tasklet_schedule (&dev->bh); // FIXME: device recovery -- reset?}/*-------------------------------------------------------------------------*/static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net){ struct usbnet *dev = (struct usbnet *) net->priv; int length = skb->len; int retval = NET_XMIT_SUCCESS; struct urb *urb = 0; struct skb_data *entry; struct driver_info *info = dev->driver_info; unsigned long flags;#ifdef CONFIG_USB_NET1080 struct nc_header *header = 0; struct nc_trailer *trailer = 0;#endif /* CONFIG_USB_NET1080 */ flags = in_interrupt () ? GFP_ATOMIC : GFP_NOIO; /* might be used for nfs */ // some devices want funky USB-level framing, for // win32 driver (usually) and/or hardware quirks if (info->tx_fixup) { skb = info->tx_fixup (dev, skb, flags); if (!skb) { dbg ("can't tx_fixup skb"); goto drop; } } if (!(urb = usb_alloc_urb (0))) { dbg ("no urb"); goto drop; } entry = (struct skb_data *) skb->cb; entry->urb = urb; entry->dev = dev; entry->state = tx_start; entry->length = length; // FIXME: reorganize a bit, so that fixup() fills out NetChip // framing too. (Packet ID update needs the spinlock...)#ifdef CONFIG_USB_NET1080 if (info->flags & FLAG_FRAMING_NC) { header = (struct nc_header *) skb_push (skb, sizeof *header); header->hdr_len = cpu_to_le16 (sizeof (*header)); header->packet_len = cpu_to_le16 (length); if (!((skb->len + sizeof *trailer) & 0x01)) *skb_put (skb, 1) = PAD_BYTE; trailer = (struct nc_trailer *) skb_put (skb, sizeof *trailer); } else#endif /* CONFIG_USB_NET1080 */ /* don't assume the hardware handles USB_ZERO_PACKET */ if ((length % EP_SIZE (dev)) == 0) skb->len++; FILL_BULK_URB (urb, dev->udev, usb_sndbulkpipe (dev->udev, info->out), skb->data, skb->len, tx_complete, skb); urb->transfer_flags |= USB_ASYNC_UNLINK;#ifdef REALLY_QUEUE urb->transfer_flags |= USB_QUEUE_BULK;#endif // FIXME urb->timeout = ... jiffies ... ; spin_lock_irqsave (&dev->txq.lock, flags);#ifdef CONFIG_USB_NET1080 if (info->flags & FLAG_FRAMING_NC) { header->packet_id = cpu_to_le16 (dev->packet_id++); put_unaligned (header->packet_id, &trailer->packet_id);#if 0 devdbg (dev, "frame >tx h %d p %d id %d", header->hdr_len, header->packet_len, header->packet_id);#endif }#endif /* CONFIG_USB_NET1080 */ netif_stop_queue (net); if ((retval = usb_submit_urb (urb)) != 0) { netif_start_queue (net); dbg ("%s tx: submit urb err %d", net->name, retval); } else { net->trans_start = jiffies; __skb_queue_tail (&dev->txq, skb); if (dev->txq.qlen < TX_QLEN) netif_start_queue (net); } spin_unlock_irqrestore (&dev->txq.lock, flags); if (retval) { devdbg (dev, "drop, code %d", retval);drop: retval = NET_XMIT_DROP; dev->stats.tx_dropped++; if (skb) dev_kfree_skb_any (skb); usb_free_urb (urb);#ifdef VERBOSE } else { devdbg (dev, "> tx, len %d, type 0x%x", length, skb->protocol);#endif } return retval;}/*-------------------------------------------------------------------------*/// tasklet ... work that avoided running in_irq()static void usbnet_bh (unsigned long param){ struct usbnet *dev = (struct usbnet *) param; struct sk_buff *skb; struct skb_data *entry; while ((skb = skb_dequeue (&dev->done))) { entry = (struct skb_data *) skb->cb; switch (entry->state) { case rx_done: entry->state = rx_cleanup; rx_process (dev, skb); continue; case tx_done: if (entry->urb->status) { // can this statistic become more specific? dev->stats.tx_errors++; dbg ("%s tx: err %d", dev->net.name, entry->urb->status); } else { dev->stats.tx_packets++; dev->stats.tx_bytes += entry->length; } // FALLTHROUGH: case rx_cleanup: usb_free_urb (entry->urb); dev_kfree_skb (skb); continue; default: dbg ("%s: bogus skb state %d", dev->net.name, entry->state); } } // waiting for all pending urbs to complete? if (dev->wait) { if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { wake_up (dev->wait); } // or are we maybe short a few urbs? } else if (netif_running (&dev->net)) { int temp = dev->rxq.qlen; if (temp < RX_QLEN) { struct urb *urb; int i; for (i = 0; i < 3 && dev->rxq.qlen < RX_QLEN; i++) { if ((urb = usb_alloc_urb (0)) != 0) rx_submit (dev, urb, GFP_ATOMIC); } if (temp != dev->rxq.qlen) devdbg (dev, "rxqlen %d --> %d", temp, dev->rxq.qlen); if (dev->rxq.qlen < RX_QLEN) tasklet_schedule (&dev->bh); } if (dev->txq.qlen < TX_QLEN) netif_wake_queue (&dev->net); }}/*------------------------------------------------------------------------- * * USB Device Driver support * *-------------------------------------------------------------------------*/ // precondition: never called in_interruptstatic void usbnet_disconnect (struct usb_device *udev, void *ptr){ struct usbnet *dev = (struct usbnet *) ptr; devinfo (dev, "unregister usbnet %03d/%03d, %s", udev->bus->busnum, udev->devnum, dev->driver_info->description); unregister_netdev (&dev->net); mutex_lock (&usbnet_mutex); mutex_lock (&dev->mutex); list_del (&dev->dev_list); mutex_unlock (&usbnet_mutex); kfree (dev); usb_dec_dev_use (udev);}/*-------------------------------------------------------------------------*/// precondition: never called in_interruptstatic void *usbnet_probe (struct usb_device *udev, unsigned ifnum, const struct usb_device_id *prod){ struct usbnet *dev; struct net_device *net; struct usb_interface_descriptor *interface; struct driver_info *info; int altnum = 0; info = (struct driver_info *) prod->driver_info; // sanity check; expect dedicated interface/devices for now. interface = &udev->actconfig->interface [ifnum].altsetting [altnum]; if (udev->descriptor.bNumConfigurations != 1 || udev->config[0].bNumInterfaces != 1// || interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC ) { dbg ("Bogus config info"); return 0; } // more sanity (unless the device is broken) if (!(info->flags & FLAG_NO_SETINT)) { if (usb_set_interface (udev, ifnum, altnum) < 0) { err ("set_interface failed"); return 0; } } // set up our own records if (!(dev = kmalloc (sizeof *dev, GFP_KERNEL))) { dbg ("can't kmalloc dev"); return 0; } memset (dev, 0, sizeof *dev); init_MUTEX_LOCKED (&dev->mutex); usb_inc_dev_use (udev); dev->udev = udev; dev->driver_info = info; INIT_LIST_HEAD (&dev->dev_list); skb_queue_head_init (&dev->rxq); skb_queue_head_init (&dev->txq); skb_queue_head_init (&dev->done); dev->bh.func = usbnet_bh; dev->bh.data = (unsigned long) dev; // set up network interface records net = &dev->net; SET_MODULE_OWNER (net); net->priv = dev; strcpy (net->name, "usb%d"); memcpy (net->dev_addr, node_id, sizeof node_id); // point-to-point link ... we always use Ethernet headers // supports win32 interop and the bridge driver. ether_setup (net); net->change_mtu = usbnet_change_mtu; net->get_stats = usbnet_get_stats; net->hard_start_xmit = usbnet_start_xmit; net->open = usbnet_open; net->stop = usbnet_stop; net->watchdog_timeo = TX_TIMEOUT_JIFFIES; net->tx_timeout = usbnet_tx_timeout; register_netdev (&dev->net); devinfo (dev, "register usbnet %03d/%03d, %s", udev->bus->busnum, udev->devnum, dev->driver_info->description); // ok, it's ready to go. mutex_lock (&usbnet_mutex); list_add (&dev->dev_list, &usbnet_list); mutex_unlock (&dev->mutex); // start as if the link is up netif_device_attach (&dev->net); mutex_unlock (&usbnet_mutex); return dev;}/*-------------------------------------------------------------------------*//* * chip vendor names won't normally be on the cables, and * may not be on the device. */static const struct usb_device_id products [] = {#ifdef CONFIG_USB_AN2720{ USB_DEVICE (0x0547, 0x2720), // AnchorChips defaults driver_info: (unsigned long) &an2720_info,},{ USB_DEVICE (0x0547, 0x2727), // Xircom PGUNET driver_info: (unsigned long) &an2720_info,},#endif#ifdef CONFIG_USB_BELKIN{ USB_DEVICE (0x050d, 0x0004), // Belkin driver_info: (unsigned long) &belkin_info,}, { USB_DEVICE (0x056c, 0x8100), // eTEK driver_info: (unsigned long) &belkin_info,}, { USB_DEVICE (0x0525, 0x9901), // Advance USBNET (eTEK) driver_info: (unsigned long) &belkin_info,},#endif#ifdef CONFIG_USB_GENESYS{ USB_DEVICE (0x05e3, 0x0502), // GL620USB-A driver_info: (unsigned long) &genelink_info,},#endif#ifdef CONFIG_USB_LINUXDEV/* * for example, this can be a host side talk-to-PDA driver. * this driver is NOT what runs _inside_ a Linux device !! */{ // 1183 = 0x049F, both used as hex values? USB_DEVICE (0x049F, 0x505A), // Compaq "Itsy" driver_info: (unsigned long) &linuxdev_info,},#endif#ifdef CONFIG_USB_NET1080{ USB_DEVICE (0x0525, 0x1080), // NetChip ref design driver_info: (unsigned long) &net1080_info,},{ USB_DEVICE (0x06D0, 0x0622), // Laplink Gold driver_info: (unsigned long) &net1080_info,},#endif#ifdef CONFIG_USB_PL2301{ USB_DEVICE (0x067b, 0x0000), // PL-2301 driver_info: (unsigned long) &prolific_info,}, { USB_DEVICE (0x067b, 0x0001), // PL-2302 driver_info: (unsigned long) &prolific_info,},#endif/* KC2190 from www.sepoong.co.kr "InstaNET" */ { }, // END};MODULE_DEVICE_TABLE (usb, products);static struct usb_driver usbnet_driver = { name: "usbnet", id_table: products, probe: usbnet_probe, disconnect: usbnet_disconnect,};/*-------------------------------------------------------------------------*/static int __init usbnet_init (void){ // compiler should optimize this out if (sizeof (((struct sk_buff *)0)->cb) < sizeof (struct skb_data)) BUG (); get_random_bytes (node_id, sizeof node_id); node_id [0] &= 0xfe; // clear multicast bit if (usb_register (&usbnet_driver) < 0) return -1; return 0;}module_init (usbnet_init);static void __exit usbnet_exit (void){ usb_deregister (&usbnet_driver);}module_exit (usbnet_exit);EXPORT_NO_SYMBOLS;MODULE_AUTHOR ("David Brownell <dbrownell@users.sourceforge.net>");MODULE_DESCRIPTION ("USB Host-to-Host Link Drivers (numerous vendors)");MODULE_LICENSE ("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -