📄 usbnet.c
字号:
/*-------------------------------------------------------------------------*//* 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; 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; } } length = skb->len; 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...) // [ BETTER: we already own net->xmit_lock, that's enough ]#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++; usb_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; 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 driver_info *info; int altnum = 0; info = (struct driver_info *) prod->driver_info;#ifdef CONFIG_USB_ZAURUS if (info == &zaurus_sl5x00_info) { int status; /* old ROMs have more than one config * so we have to make sure config="1" (?) */ status = usb_set_configuration (udev, 1); if (status < 0) { err ("set_config failed, %d", status); return 0; } altnum = 1; }#endif // 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_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_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#ifdef CONFIG_USB_PXA/* * PXA250 or PXA210 ... these use a "usb-eth" driver much like * the sa1
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -