⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usbnet.c

📁 omap3 linux 2.6 用nocc去除了冗余代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * USB Network driver infrastructure * Copyright (C) 2000-2005 by David Brownell * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *//* * This is a generic "USB networking" framework that works with several * kinds of full and high speed networking devices:  host-to-host cables, * smart usb peripherals, and actual Ethernet adapters. * * These devices usually differ in terms of control protocols (if they * even have one!) and sometimes they define new framing to wrap or batch * Ethernet packets.  Otherwise, they talk to USB pretty much the same, * so interface (un)binding, endpoint I/O queues, fault handling, and other * issues can usefully be addressed by this framework. */// #define	DEBUG			// error path messages, extra info// #define	VERBOSE			// more; success messages#include <linux/module.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/workqueue.h>#include <linux/mii.h>#include <linux/usb.h>#include "usbnet.h"#define DRIVER_VERSION		"22-Aug-2005"/*-------------------------------------------------------------------------*//* * Nineteen USB 1.1 max size bulk transactions per frame (ms), max. * Several dozen bytes of IPv4 data can fit in two such transactions. * One maximum size Ethernet packet takes twenty four of them. * For high speed, each frame comfortably fits almost 36 max size * Ethernet packets (so queues should be bigger). * * REVISIT qlens should be members of 'struct usbnet'; the goal is to * let the USB host controller be busy for 5msec or more before an irq * is required, under load.  Jumbograms change the equation. */#define RX_MAX_QUEUE_MEMORY (60 * 1518)#define	RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \			(RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4)#define	TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \			(RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4)// reawaken network queue this soon after stopping; else watchdog barks#define TX_TIMEOUT_JIFFIES	(5*HZ)// throttle rx/tx briefly after some faults, so khubd might disconnect()// us (it polls at HZ/4 usually) before we report too many false errors.#define THROTTLE_JIFFIES	(HZ/8)// between wakeups#define UNLINK_TIMEOUT_MS	3/*-------------------------------------------------------------------------*/// randomly generated ethernet addressstatic u8	node_id [ETH_ALEN];static const char driver_name [] = "usbnet";/* use ethtool to change the level for any given device */static int msg_level = -1;module_param (msg_level, int, 0);MODULE_PARM_DESC (msg_level, "Override default message level");/*-------------------------------------------------------------------------*//* handles CDC Ethernet and many other network "bulk data" interfaces */int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf){	int				tmp;	struct usb_host_interface	*alt = NULL;	struct usb_host_endpoint	*in = NULL, *out = NULL;	struct usb_host_endpoint	*status = NULL;	for (tmp = 0; tmp < intf->num_altsetting; tmp++) {		unsigned	ep;		in = out = status = NULL;		alt = intf->altsetting + tmp;		/* take the first altsetting with in-bulk + out-bulk;		 * remember any status endpoint, just in case;		 * ignore other endpoints and altsetttings.		 */		for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {			struct usb_host_endpoint	*e;			int				intr = 0;			e = alt->endpoint + ep;			switch (e->desc.bmAttributes) {			case USB_ENDPOINT_XFER_INT:				if (!usb_endpoint_dir_in(&e->desc))					continue;				intr = 1;				/* FALLTHROUGH */			case USB_ENDPOINT_XFER_BULK:				break;			default:				continue;			}			if (usb_endpoint_dir_in(&e->desc)) {				if (!intr && !in)					in = e;				else if (intr && !status)					status = e;			} else {				if (!out)					out = e;			}		}		if (in && out)			break;	}	if (!alt || !in || !out)		return -EINVAL;	if (alt->desc.bAlternateSetting != 0			|| !(dev->driver_info->flags & FLAG_NO_SETINT)) {		tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,				alt->desc.bAlternateSetting);		if (tmp < 0)			return tmp;	}	dev->in = usb_rcvbulkpipe (dev->udev,			in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);	dev->out = usb_sndbulkpipe (dev->udev,			out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);	dev->status = status;	return 0;}EXPORT_SYMBOL_GPL(usbnet_get_endpoints);static void intr_complete (struct urb *urb);static int init_status (struct usbnet *dev, struct usb_interface *intf){	char		*buf = NULL;	unsigned	pipe = 0;	unsigned	maxp;	unsigned	period;	if (!dev->driver_info->status)		return 0;	pipe = usb_rcvintpipe (dev->udev,			dev->status->desc.bEndpointAddress				& USB_ENDPOINT_NUMBER_MASK);	maxp = usb_maxpacket (dev->udev, pipe, 0);	/* avoid 1 msec chatter:  min 8 msec poll rate */	period = max ((int) dev->status->desc.bInterval,		(dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);	buf = kmalloc (maxp, GFP_KERNEL);	if (buf) {		dev->interrupt = usb_alloc_urb (0, GFP_KERNEL);		if (!dev->interrupt) {			kfree (buf);			return -ENOMEM;		} else {			usb_fill_int_urb(dev->interrupt, dev->udev, pipe,				buf, maxp, intr_complete, dev, period);			dev_dbg(&intf->dev,				"status ep%din, %d bytes period %d\n",				usb_pipeendpoint(pipe), maxp, period);		}	}	return  0;}/* Passes this packet up the stack, updating its accounting. * Some link protocols batch packets, so their rx_fixup paths * can return clones as well as just modify the original skb. */void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb){	int	status;	skb->protocol = eth_type_trans (skb, dev->net);	dev->stats.rx_packets++;	dev->stats.rx_bytes += skb->len;	if (netif_msg_rx_status (dev))		devdbg (dev, "< rx, len %zu, type 0x%x",			skb->len + sizeof (struct ethhdr), skb->protocol);	memset (skb->cb, 0, sizeof (struct skb_data));	status = netif_rx (skb);	if (status != NET_RX_SUCCESS && netif_msg_rx_err (dev))		devdbg (dev, "netif_rx status %d", status);}EXPORT_SYMBOL_GPL(usbnet_skb_return);/*------------------------------------------------------------------------- * * Network Device Driver (peer link to "Host Device", from USB host) * *-------------------------------------------------------------------------*/static int usbnet_change_mtu (struct net_device *net, int new_mtu){	struct usbnet	*dev = netdev_priv(net);	int		ll_mtu = new_mtu + net->hard_header_len;	int		old_hard_mtu = dev->hard_mtu;	int		old_rx_urb_size = dev->rx_urb_size;	if (new_mtu <= 0)		return -EINVAL;	// no second zero-length packet read wanted after mtu-sized packets	if ((ll_mtu % dev->maxpacket) == 0)		return -EDOM;	net->mtu = new_mtu;	dev->hard_mtu = net->mtu + net->hard_header_len;	if (dev->rx_urb_size == old_hard_mtu) {		dev->rx_urb_size = dev->hard_mtu;		if (dev->rx_urb_size > old_rx_urb_size)			usbnet_unlink_rx_urbs(dev);	}	return 0;}/*-------------------------------------------------------------------------*/static struct net_device_stats *usbnet_get_stats (struct net_device *net){	struct usbnet	*dev = netdev_priv(net);	return &dev->stats;}/*-------------------------------------------------------------------------*//* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from * completion callbacks.  2.5 should have fixed those bugs... */static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list){	unsigned long		flags;	spin_lock_irqsave(&list->lock, flags);	__skb_unlink(skb, list);	spin_unlock(&list->lock);	spin_lock(&dev->done.lock);	__skb_queue_tail(&dev->done, skb);	if (dev->done.qlen == 1)		tasklet_schedule(&dev->bh);	spin_unlock_irqrestore(&dev->done.lock, flags);}/* some work can't be done in tasklets, so we use keventd * * NOTE:  annoying asymmetry:  if it's active, schedule_work() fails, * but tasklet_schedule() doesn't.  hope the failure is rare. */void usbnet_defer_kevent (struct usbnet *dev, int work){	set_bit (work, &dev->flags);	if (!schedule_work (&dev->kevent))		deverr (dev, "kevent %d may have been dropped", work);	else		devdbg (dev, "kevent %d scheduled", work);}EXPORT_SYMBOL_GPL(usbnet_defer_kevent);/*-------------------------------------------------------------------------*/static void rx_complete (struct urb *urb);static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags){	struct sk_buff		*skb;	struct skb_data		*entry;	int			retval = 0;	unsigned long		lockflags;	size_t			size = dev->rx_urb_size;	if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {		if (netif_msg_rx_err (dev))			devdbg (dev, "no rx skb");		usbnet_defer_kevent (dev, EVENT_RX_MEMORY);		usb_free_urb (urb);		return;	}	skb_reserve (skb, NET_IP_ALIGN);	entry = (struct skb_data *) skb->cb;	entry->urb = urb;	entry->dev = dev;	entry->state = rx_start;	entry->length = 0;	usb_fill_bulk_urb (urb, dev->udev, dev->in,		skb->data, size, rx_complete, skb);	spin_lock_irqsave (&dev->rxq.lock, lockflags);	if (netif_running (dev->net)			&& netif_device_present (dev->net)			&& !test_bit (EVENT_RX_HALT, &dev->flags)) {		switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){		case -EPIPE:			usbnet_defer_kevent (dev, EVENT_RX_HALT);			break;		case -ENOMEM:			usbnet_defer_kevent (dev, EVENT_RX_MEMORY);			break;		case -ENODEV:			if (netif_msg_ifdown (dev))				devdbg (dev, "device gone");			netif_device_detach (dev->net);			break;		default:			if (netif_msg_rx_err (dev))				devdbg (dev, "rx submit, %d", retval);			tasklet_schedule (&dev->bh);			break;		case 0:			__skb_queue_tail (&dev->rxq, skb);		}	} else {		if (netif_msg_ifdown (dev))			devdbg (dev, "rx: stopped");		retval = -ENOLINK;	}	spin_unlock_irqrestore (&dev->rxq.lock, lockflags);	if (retval) {		dev_kfree_skb_any (skb);		usb_free_urb (urb);	}}/*-------------------------------------------------------------------------*/static inline void rx_process (struct usbnet *dev, struct sk_buff *skb){	if (dev->driver_info->rx_fixup			&& !dev->driver_info->rx_fixup (dev, skb))		goto error;	// else network stack removes extra byte if we forced a short packet	if (skb->len)		usbnet_skb_return (dev, skb);	else {		if (netif_msg_rx_err (dev))			devdbg (dev, "drop");error:		dev->stats.rx_errors++;		skb_queue_tail (&dev->done, skb);	}}/*-------------------------------------------------------------------------*/static void rx_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;	int			urb_status = urb->status;	skb_put (skb, urb->actual_length);	entry->state = rx_done;	entry->urb = NULL;	switch (urb_status) {	    // success	    case 0:		if (skb->len < dev->net->hard_header_len) {			entry->state = rx_cleanup;			dev->stats.rx_errors++;			dev->stats.rx_length_errors++;			if (netif_msg_rx_err (dev))				devdbg (dev, "rx length %d", skb->len);		}		break;	    // stalls need manual reset. this is rare ... except that	    // when going through USB 2.0 TTs, unplug appears this way.	    // we avoid the highspeed version of the ETIMEOUT/EILSEQ	    // storm, recovering as needed.	    case -EPIPE:		dev->stats.rx_errors++;		usbnet_defer_kevent (dev, EVENT_RX_HALT);		// FALLTHROUGH	    // software-driven interface shutdown	    case -ECONNRESET:		// async unlink	    case -ESHUTDOWN:		// hardware gone		if (netif_msg_ifdown (dev))			devdbg (dev, "rx shutdown, code %d", urb_status);		goto block;	    // we get controller i/o faults during khubd disconnect() delays.	    // throttle down resubmits, to avoid log floods; just temporarily,	    // so we still recover when the fault isn't a khubd delay.	    case -EPROTO:	    case -ETIME:	    case -EILSEQ:		dev->stats.rx_errors++;		if (!timer_pending (&dev->delay)) {			mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);			if (netif_msg_link (dev))				devdbg (dev, "rx throttle %d", urb_status);		}block:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -