📄 usbnet.c
字号:
return -EOPNOTSUPP;}static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd){ switch (cmd) { case SIOCETHTOOL: return usbnet_ethtool_ioctl (net, (void *)rq->ifr_data); default: return -EOPNOTSUPP; }}/*-------------------------------------------------------------------------*//* work that cannot be done in interrupt context uses keventd. * * NOTE: "uhci" and "usb-uhci" may have trouble with this since they don't * queue control transfers to individual devices, and other threads could * trigger control requests concurrently. hope that's rare. */static voidkevent (void *data){ struct usbnet *dev = data; int status; /* usb_clear_halt() needs a thread context */ if (test_bit (EVENT_TX_HALT, &dev->flags)) { unlink_urbs (&dev->txq); status = usb_clear_halt (dev->udev, usb_sndbulkpipe (dev->udev, dev->driver_info->out)); if (status < 0) err ("%s: can't clear tx halt, status %d", dev->net.name, status); else { clear_bit (EVENT_TX_HALT, &dev->flags); netif_wake_queue (&dev->net); } } if (test_bit (EVENT_RX_HALT, &dev->flags)) { unlink_urbs (&dev->rxq); status = usb_clear_halt (dev->udev, usb_rcvbulkpipe (dev->udev, dev->driver_info->in)); if (status < 0) err ("%s: can't clear rx halt, status %d", dev->net.name, status); else { clear_bit (EVENT_RX_HALT, &dev->flags); tasklet_schedule (&dev->bh); } } /* tasklet could resubmit itself forever if memory is tight */ if (test_bit (EVENT_RX_MEMORY, &dev->flags)) { struct urb *urb = 0; if (netif_running (&dev->net)) urb = ALLOC_URB (0, GFP_KERNEL); else clear_bit (EVENT_RX_MEMORY, &dev->flags); if (urb != 0) { clear_bit (EVENT_RX_MEMORY, &dev->flags); rx_submit (dev, urb, GFP_KERNEL); tasklet_schedule (&dev->bh); } } if (dev->flags) dbg ("%s: kevent done, flags = 0x%lx", dev->net.name, dev->flags);}/*-------------------------------------------------------------------------*/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 == -EPIPE) defer_kevent (dev, EVENT_TX_HALT); 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 */ // 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, GFP_ATOMIC); if (!skb) { dbg ("can't tx_fixup skb"); goto drop; } } if (!(urb = ALLOC_URB (0, GFP_ATOMIC))) { 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; // 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 */ switch ((retval = SUBMIT_URB (urb, GFP_ATOMIC))) { case -EPIPE: netif_stop_queue (net); defer_kevent (dev, EVENT_TX_HALT); break; default: dbg ("%s tx: submit urb err %d", net->name, retval); break; case 0: net->trans_start = jiffies; __skb_queue_tail (&dev->txq, skb); if (dev->txq.qlen >= TX_QLEN) netif_stop_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) && !test_bit (EVENT_RX_HALT, &dev->flags)) { 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 = ALLOC_URB (0, GFP_ATOMIC)) != 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 usb-%s-%s, %s", udev->bus->bus_name, udev->devpath, 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); // assuming we used keventd, it must quiesce too flush_scheduled_tasks (); kfree (dev); usb_put_dev (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; struct usb_endpoint_descriptor *endpoint; int i; 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; } } /* check out the endpoints */ for (i = 0; i < interface->bNumEndpoints; ++i) { endpoint = &interface->endpoint[i]; if ((endpoint->bEndpointAddress & 0x80) && ((endpoint->bmAttributes & 3) == 0x02)) { /* we found a bulk in endpoint */ dbg("found bulk in"); info->in=endpoint->bEndpointAddress & 0x7f; } if (((endpoint->bEndpointAddress & 0x80) == 0x00) && ((endpoint->bmAttributes & 3) == 0x02)) { /* we found a bulk out endpoint */ dbg("found bulk out"); info->out=endpoint->bEndpointAddress & 0x7f; } } // 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_get_dev (udev); dev->udev = udev; dev->driver_info = info; dev->msg_level = msg_level; 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; INIT_TQUEUE (&dev->kevent, kevent, 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; net->do_ioctl = usbnet_ioctl; register_netdev (&dev->net); devinfo (dev, "register usbnet usb-%s-%s, %s", udev->bus->bus_name, udev->devpath, 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_EPSON2888{ USB_DEVICE (0x0525, 0x2888), // EPSON USB client driver_info: (unsigned long) &epson2888_info,},#endif#ifdef CONFIG_USB_GENESYS{ USB_DEVICE (0x05e3, 0x0502), // GL620USB-A .driver_info = (unsigned long) &genelink_info,}, /* NOT: USB_DEVICE (0x05e3, 0x0501), // GL620USB * that's half duplex, not currently supported */#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,}, { USB_DEVICE (0x0E7E, 0x1001), // G.Mate "Yopy" .driver_info = (unsigned long) &linuxdev_info,}, // NOTE: the Sharp Zaurus uses a modified version of // this driver, which is not interoperable with this.#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 = driver_name, .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 node_id [0] |= 0x02; // set local assignment bit (IEEE802) 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 + -