📄 usbnet.c
字号:
entry->state = rx_cleanup; entry->urb = urb; urb = NULL; break; // data overrun ... flush fifo? case -EOVERFLOW: dev->stats.rx_over_errors++; // FALLTHROUGH default: entry->state = rx_cleanup; dev->stats.rx_errors++; if (netif_msg_rx_err (dev)) devdbg (dev, "rx status %d", urb_status); break; } defer_bh(dev, skb, &dev->rxq); if (urb) { if (netif_running (dev->net) && !test_bit (EVENT_RX_HALT, &dev->flags)) { rx_submit (dev, urb, GFP_ATOMIC); return; } usb_free_urb (urb); } if (netif_msg_rx_err (dev)) devdbg (dev, "no read resubmitted");}static void intr_complete (struct urb *urb){ struct usbnet *dev = urb->context; int status = urb->status; switch (status) { /* success */ case 0: dev->driver_info->status(dev, urb); break; /* software-driven interface shutdown */ case -ENOENT: // urb killed case -ESHUTDOWN: // hardware gone if (netif_msg_ifdown (dev)) devdbg (dev, "intr shutdown, code %d", status); return; /* NOTE: not throttling like RX/TX, since this endpoint * already polls infrequently */ default: devdbg (dev, "intr status %d", status); break; } if (!netif_running (dev->net)) return; memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); status = usb_submit_urb (urb, GFP_ATOMIC); if (status != 0 && netif_msg_timer (dev)) deverr(dev, "intr resubmit --> %d", status);}/*-------------------------------------------------------------------------*/// unlink pending rx/tx; completion handlers do all other cleanupstatic int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q){ unsigned long flags; struct sk_buff *skb, *skbnext; int count = 0; spin_lock_irqsave (&q->lock, flags); for (skb = q->next; skb != (struct sk_buff *) q; skb = skbnext) { struct skb_data *entry; struct urb *urb; int retval; entry = (struct skb_data *) skb->cb; urb = entry->urb; skbnext = skb->next; // during some PM-driven resume scenarios, // these (async) unlinks complete immediately retval = usb_unlink_urb (urb); if (retval != -EINPROGRESS && retval != 0) devdbg (dev, "unlink urb err, %d", retval); else count++; } spin_unlock_irqrestore (&q->lock, flags); return count;}// Flush all pending rx urbs// minidrivers may need to do this when the MTU changesvoid usbnet_unlink_rx_urbs(struct usbnet *dev){ if (netif_running(dev->net)) { (void) unlink_urbs (dev, &dev->rxq); tasklet_schedule(&dev->bh); }}EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);/*-------------------------------------------------------------------------*/// precondition: never called in_interruptstatic int usbnet_stop (struct net_device *net){ struct usbnet *dev = netdev_priv(net); int temp; DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup); DECLARE_WAITQUEUE (wait, current); netif_stop_queue (net); if (netif_msg_ifdown (dev)) devinfo (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld", dev->stats.rx_packets, dev->stats.tx_packets, dev->stats.rx_errors, dev->stats.tx_errors ); // ensure there are no more active urbs add_wait_queue (&unlink_wakeup, &wait); dev->wait = &unlink_wakeup; temp = unlink_urbs (dev, &dev->txq) + unlink_urbs (dev, &dev->rxq); // maybe wait for deletions to finish. while (!skb_queue_empty(&dev->rxq) && !skb_queue_empty(&dev->txq) && !skb_queue_empty(&dev->done)) { msleep(UNLINK_TIMEOUT_MS); if (netif_msg_ifdown (dev)) devdbg (dev, "waited for %d urb completions", temp); } dev->wait = NULL; remove_wait_queue (&unlink_wakeup, &wait); usb_kill_urb(dev->interrupt); /* deferred work (task, timer, softirq) must also stop. * can't flush_scheduled_work() until we drop rtnl (later), * else workers could deadlock; so make workers a NOP. */ dev->flags = 0; del_timer_sync (&dev->delay); tasklet_kill (&dev->bh); return 0;}/*-------------------------------------------------------------------------*/// posts reads, and enables write queuing// precondition: never called in_interruptstatic int usbnet_open (struct net_device *net){ struct usbnet *dev = netdev_priv(net); int retval = 0; struct driver_info *info = dev->driver_info; // put into "known safe" state if (info->reset && (retval = info->reset (dev)) < 0) { if (netif_msg_ifup (dev)) devinfo (dev, "open reset fail (%d) usbnet usb-%s-%s, %s", retval, dev->udev->bus->bus_name, dev->udev->devpath, info->description); goto done; } // insist peer be connected if (info->check_connect && (retval = info->check_connect (dev)) < 0) { if (netif_msg_ifup (dev)) devdbg (dev, "can't open; %d", retval); goto done; } /* start any status interrupt transfer */ if (dev->interrupt) { retval = usb_submit_urb (dev->interrupt, GFP_KERNEL); if (retval < 0) { if (netif_msg_ifup (dev)) deverr (dev, "intr submit %d", retval); goto done; } } netif_start_queue (net); if (netif_msg_ifup (dev)) { char *framing; if (dev->driver_info->flags & FLAG_FRAMING_NC) framing = "NetChip"; else if (dev->driver_info->flags & FLAG_FRAMING_GL) framing = "GeneSys"; else if (dev->driver_info->flags & FLAG_FRAMING_Z) framing = "Zaurus"; else if (dev->driver_info->flags & FLAG_FRAMING_RN) framing = "RNDIS"; else if (dev->driver_info->flags & FLAG_FRAMING_AX) framing = "ASIX"; else framing = "simple"; devinfo (dev, "open: enable queueing " "(rx %d, tx %d) mtu %d %s framing", (int)RX_QLEN (dev), (int)TX_QLEN (dev), dev->net->mtu, framing); } // delay posting reads until we're fully open tasklet_schedule (&dev->bh);done: return retval;}/*-------------------------------------------------------------------------*//* ethtool methods; minidrivers may need to add some more, but * they'll probably want to use this base set. */#define HAVE_MIIint usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd){ struct usbnet *dev = netdev_priv(net); if (!dev->mii.mdio_read) return -EOPNOTSUPP; return mii_ethtool_gset(&dev->mii, cmd);}EXPORT_SYMBOL_GPL(usbnet_get_settings);int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd){ struct usbnet *dev = netdev_priv(net); int retval; if (!dev->mii.mdio_write) return -EOPNOTSUPP; retval = mii_ethtool_sset(&dev->mii, cmd); /* link speed/duplex might have changed */ if (dev->driver_info->link_reset) dev->driver_info->link_reset(dev); return retval;}EXPORT_SYMBOL_GPL(usbnet_set_settings);u32 usbnet_get_link (struct net_device *net){ struct usbnet *dev = netdev_priv(net); /* If a check_connect is defined, return its result */ if (dev->driver_info->check_connect) return dev->driver_info->check_connect (dev) == 0; /* if the device has mii operations, use those */ if (dev->mii.mdio_read) return mii_link_ok(&dev->mii); /* Otherwise, say we're up (to avoid breaking scripts) */ return 1;}EXPORT_SYMBOL_GPL(usbnet_get_link);int usbnet_nway_reset(struct net_device *net){ struct usbnet *dev = netdev_priv(net); if (!dev->mii.mdio_write) return -EOPNOTSUPP; return mii_nway_restart(&dev->mii);}EXPORT_SYMBOL_GPL(usbnet_nway_reset);void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info){ struct usbnet *dev = netdev_priv(net); strncpy (info->driver, dev->driver_name, sizeof info->driver); strncpy (info->version, DRIVER_VERSION, sizeof info->version); strncpy (info->fw_version, dev->driver_info->description, sizeof info->fw_version); usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info);}EXPORT_SYMBOL_GPL(usbnet_get_drvinfo);u32 usbnet_get_msglevel (struct net_device *net){ struct usbnet *dev = netdev_priv(net); return dev->msg_enable;}EXPORT_SYMBOL_GPL(usbnet_get_msglevel);void usbnet_set_msglevel (struct net_device *net, u32 level){ struct usbnet *dev = netdev_priv(net); dev->msg_enable = level;}EXPORT_SYMBOL_GPL(usbnet_set_msglevel);/* drivers may override default ethtool_ops in their bind() routine */static struct ethtool_ops usbnet_ethtool_ops = { .get_settings = usbnet_get_settings, .set_settings = usbnet_set_settings, .get_link = usbnet_get_link, .nway_reset = usbnet_nway_reset, .get_drvinfo = usbnet_get_drvinfo, .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel,};/*-------------------------------------------------------------------------*//* work that cannot be done in interrupt context uses keventd. * * NOTE: with 2.5 we could do more of this using completion callbacks, * especially now that control transfers can be queued. */static voidkevent (struct work_struct *work){ struct usbnet *dev = container_of(work, struct usbnet, kevent); int status; /* usb_clear_halt() needs a thread context */ if (test_bit (EVENT_TX_HALT, &dev->flags)) { unlink_urbs (dev, &dev->txq); status = usb_clear_halt (dev->udev, dev->out); if (status < 0 && status != -EPIPE && status != -ESHUTDOWN) { if (netif_msg_tx_err (dev)) deverr (dev, "can't clear tx halt, status %d", status); } else { clear_bit (EVENT_TX_HALT, &dev->flags); if (status != -ESHUTDOWN) netif_wake_queue (dev->net); } } if (test_bit (EVENT_RX_HALT, &dev->flags)) { unlink_urbs (dev, &dev->rxq); status = usb_clear_halt (dev->udev, dev->in); if (status < 0 && status != -EPIPE && status != -ESHUTDOWN) { if (netif_msg_rx_err (dev)) deverr (dev, "can't clear rx halt, status %d", 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 = NULL; if (netif_running (dev->net)) urb = usb_alloc_urb (0, GFP_KERNEL); else clear_bit (EVENT_RX_MEMORY, &dev->flags); if (urb != NULL) { clear_bit (EVENT_RX_MEMORY, &dev->flags); rx_submit (dev, urb, GFP_KERNEL); tasklet_schedule (&dev->bh); } } if (test_bit (EVENT_LINK_RESET, &dev->flags)) { struct driver_info *info = dev->driver_info; int retval = 0; clear_bit (EVENT_LINK_RESET, &dev->flags); if(info->link_reset && (retval = info->link_reset(dev)) < 0) { devinfo(dev, "link reset failed (%d) usbnet usb-%s-%s, %s", retval, dev->udev->bus->bus_name, dev->udev->devpath, info->description); } } if (dev->flags) devdbg (dev, "kevent done, flags = 0x%lx", 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 == 0) { dev->stats.tx_packets++; dev->stats.tx_bytes += entry->length; } else { dev->stats.tx_errors++; switch (urb->status) { case -EPIPE: usbnet_defer_kevent (dev, EVENT_TX_HALT); break; /* software-driven interface shutdown */ case -ECONNRESET: // async unlink case -ESHUTDOWN: // hardware gone break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -