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

📄 usbnet.c

📁 S3C2440ARM9开发板的USB驱动程序
💻 C
📖 第 1 页 / 共 4 页
字号:
	return 1;}static struct sk_buff *net1080_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags){	int			padlen;	struct sk_buff		*skb2;	padlen = ((skb->len + sizeof (struct nc_header)			+ sizeof (struct nc_trailer)) & 0x01) ? 0 : 1;	if (!skb_cloned (skb)) {		int	headroom = skb_headroom (skb);		int	tailroom = skb_tailroom (skb);		if ((padlen + sizeof (struct nc_trailer)) <= tailroom			    && sizeof (struct nc_header) <= headroom)			return skb;		if ((sizeof (struct nc_header) + padlen					+ sizeof (struct nc_trailer)) <				(headroom + tailroom)) {			skb->data = memmove (skb->head						+ sizeof (struct nc_header),					    skb->data, skb->len);			skb->tail = skb->data + skb->len;			return skb;		}	}	skb2 = skb_copy_expand (skb,				sizeof (struct nc_header),				sizeof (struct nc_trailer) + padlen,				flags);	dev_kfree_skb_any (skb);	return skb2;}static const struct driver_info	net1080_info = {	.description =	"NetChip TurboCONNECT",	.flags =	FLAG_FRAMING_NC,	.reset =	net1080_reset,	.check_connect =net1080_check_connect,	.rx_fixup =	net1080_rx_fixup,	.tx_fixup =	net1080_tx_fixup,	.in = 1, .out = 1,		// direction distinguishes these	.epsize =64,};#endif /* CONFIG_USB_NET1080 */#ifdef CONFIG_USB_PL2301/*------------------------------------------------------------------------- * * Prolific PL-2301/PL-2302 driver ... http://www.prolifictech.com * *-------------------------------------------------------------------------*//* * Bits 0-4 can be used for software handshaking; they're set from * one end, cleared from the other, "read" with the interrupt byte. */#define	PL_S_EN		(1<<7)		/* (feature only) suspend enable *//* reserved bit -- rx ready (6) ? */#define	PL_TX_READY	(1<<5)		/* (interrupt only) transmit ready */#define	PL_RESET_OUT	(1<<4)		/* reset output pipe */#define	PL_RESET_IN	(1<<3)		/* reset input pipe */#define	PL_TX_C		(1<<2)		/* transmission complete */#define	PL_TX_REQ	(1<<1)		/* transmission received */#define	PL_PEER_E	(1<<0)		/* peer exists */static inline intpl_vendor_req (struct usbnet *dev, u8 req, u8 val, u8 index){	return usb_control_msg (dev->udev,		usb_rcvctrlpipe (dev->udev, 0),		req,		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,		val, index,		0, 0,		CONTROL_TIMEOUT_JIFFIES);}static inline intpl_clear_QuickLink_features (struct usbnet *dev, int val){	return pl_vendor_req (dev, 1, (u8) val, 0);}static inline intpl_set_QuickLink_features (struct usbnet *dev, int val){	return pl_vendor_req (dev, 3, (u8) val, 0);}/*-------------------------------------------------------------------------*/static int pl_reset (struct usbnet *dev){	return pl_set_QuickLink_features (dev,		PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E);}static const struct driver_info	prolific_info = {	.description =	"Prolific PL-2301/PL-2302",	.flags =	FLAG_NO_SETINT,		/* some PL-2302 versions seem to fail usb_set_interface() */	.reset =	pl_reset,	.in = 3, .out = 2,	.epsize =64,};#endif /* CONFIG_USB_PL2301 *//*------------------------------------------------------------------------- * * 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 = (struct usbnet *) net->priv;	if (new_mtu <= MIN_PACKET || new_mtu > MAX_PACKET)		return -EINVAL;#ifdef	CONFIG_USB_NET1080	if (((dev->driver_info->flags) & FLAG_FRAMING_NC)) {		if (FRAMED_SIZE (new_mtu) > MAX_PACKET)			return -EINVAL;	}#endif#ifdef	CONFIG_USB_GENESYS	if (((dev->driver_info->flags) & FLAG_FRAMING_GL)			&& new_mtu > GL_MAX_PACKET_LEN)		return -EINVAL;#endif	// no second zero-length packet read wanted after mtu-sized packets	if (((new_mtu + sizeof (struct ethhdr)) % EP_SIZE (dev)) == 0)		return -EDOM;	net->mtu = new_mtu;	return 0;}/*-------------------------------------------------------------------------*/static struct net_device_stats *usbnet_get_stats (struct net_device *net){	return &((struct usbnet *) net->priv)->stats;}/*-------------------------------------------------------------------------*//* urb completions are currently in_irq; avoid doing real work then. */static void defer_bh (struct usbnet *dev, struct sk_buff *skb){	struct sk_buff_head	*list = skb->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_task() fails, * but tasklet_schedule() doesn't.  hope the failure is rare. */static void defer_kevent (struct usbnet *dev, int work){	set_bit (work, &dev->flags);	if (!schedule_task (&dev->kevent))		err ("%s: kevent %d may have been dropped",			dev->net.name, work);	else		dbg ("%s: kevent %d scheduled", dev->net.name, work);}/*-------------------------------------------------------------------------*/static void rx_complete (struct urb *urb);static void rx_submit (struct usbnet *dev, struct urb *urb, int flags){	struct sk_buff		*skb;	struct skb_data		*entry;	int			retval = 0;	unsigned long		lockflags;	size_t			size;#ifdef CONFIG_USB_NET1080	if (dev->driver_info->flags & FLAG_FRAMING_NC)		size = FRAMED_SIZE (dev->net.mtu);	else#endif#ifdef CONFIG_USB_GENESYS	if (dev->driver_info->flags & FLAG_FRAMING_GL)		size = GL_RCV_BUF_SIZE;	else#endif		size = (sizeof (struct ethhdr) + dev->net.mtu);	if ((skb = alloc_skb (size, flags)) == 0) {		dbg ("no rx skb");		defer_kevent (dev, EVENT_RX_MEMORY);		usb_free_urb (urb);		return;	}	entry = (struct skb_data *) skb->cb;	entry->urb = urb;	entry->dev = dev;	entry->state = rx_start;	entry->length = 0;	FILL_BULK_URB (urb, dev->udev,		usb_rcvbulkpipe (dev->udev, dev->driver_info->in),		skb->data, size, rx_complete, skb);	urb->transfer_flags |= USB_ASYNC_UNLINK;#if 0	// Idle-but-posted reads with UHCI really chew up	// PCI bandwidth unless FSBR is disabled	urb->transfer_flags |= USB_NO_FSBR;#endif	spin_lock_irqsave (&dev->rxq.lock, lockflags);	if (netif_running (&dev->net)			&& !test_bit (EVENT_RX_HALT, &dev->flags)) {		switch (retval = SUBMIT_URB (urb, GFP_ATOMIC)){ 		case -EPIPE:			defer_kevent (dev, EVENT_RX_HALT);			break;		case -ENOMEM:			defer_kevent (dev, EVENT_RX_MEMORY);			break;		default:			dbg ("%s rx submit, %d", dev->net.name, retval);			tasklet_schedule (&dev->bh);			break;		case 0:			__skb_queue_tail (&dev->rxq, skb);		}	} else {		dbg ("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) {		int	status;// FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ?		skb->dev = &dev->net;		skb->protocol = eth_type_trans (skb, &dev->net);		dev->stats.rx_packets++;		dev->stats.rx_bytes += skb->len;#ifdef	VERBOSE		devdbg (dev, "< rx, len %d, type 0x%x",			skb->len + sizeof (struct ethhdr), skb->protocol);#endif		memset (skb->cb, 0, sizeof (struct skb_data));		status = netif_rx (skb);		if (status != NET_RX_SUCCESS)			devdbg (dev, "netif_rx status %d", status);	} else {		dbg ("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 = 0;	switch (urb_status) {	    // success	    case 0:		if (MIN_PACKET > skb->len || skb->len > MAX_PACKET) {			entry->state = rx_cleanup;			dev->stats.rx_errors++;			dev->stats.rx_length_errors++;			dbg ("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:		defer_kevent (dev, EVENT_RX_HALT);		// FALLTHROUGH	    // software-driven interface shutdown	    case -ECONNRESET:		// according to API spec	    case -ECONNABORTED:		// some (now fixed?) UHCI bugs		dbg ("%s rx shutdown, code %d", dev->net.name, urb_status);		entry->state = rx_cleanup;		// do urb frees only in the tasklet (UHCI has oopsed ...)		entry->urb = urb;		urb = 0;		break;	    // data overrun ... flush fifo?	    case -EOVERFLOW:		dev->stats.rx_over_errors++;		// FALLTHROUGH	    	    default:		// on unplug we get ETIMEDOUT (ohci) or EILSEQ (uhci)		// until khubd sees its interrupt and disconnects us.		// that can easily be hundreds of passes through here.		entry->state = rx_cleanup;		dev->stats.rx_errors++;		dbg ("%s rx: status %d", dev->net.name, urb_status);		break;	}	defer_bh (dev, skb);	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);	}#ifdef	VERBOSE	dbg ("no read resubmitted");#endif /* VERBOSE */}/*-------------------------------------------------------------------------*/// unlink pending rx/tx; completion handlers do all other cleanupstatic int unlink_urbs (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)			dbg ("unlink urb err, %d", retval);		else			count++;	}	spin_unlock_irqrestore (&q->lock, flags);	return count;}/*-------------------------------------------------------------------------*/// precondition: never called in_interruptstatic int usbnet_stop (struct net_device *net){	struct usbnet		*dev = (struct usbnet *) net->priv;	int			temp;	DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); 	DECLARE_WAITQUEUE (wait, current);	mutex_lock (&dev->mutex);	netif_stop_queue (net);	if (dev->msg_level >= 2)		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->txq) + unlink_urbs (&dev->rxq);	// maybe wait for deletions to finish.	while (skb_queue_len (&dev->rxq)			&& skb_queue_len (&dev->txq)			&& skb_queue_len (&dev->done)) {		set_current_state (TASK_UNINTERRUPTIBLE);		schedule_timeout (UNLINK_TIMEOUT_JIFFIES);		dbg ("waited for %d urb completions", temp);	}	dev->wait = 0;	remove_wait_queue (&unlink_wakeup, &wait); 	mutex_unlock (&dev->mutex);	return 0;}/*-------------------------------------------------------------------------*/// posts reads, and enables write queing// precondition: never called in_interruptstatic int usbnet_open (struct net_device *net){	struct usbnet		*dev = (struct usbnet *) net->priv;	int			retval = 0;	struct driver_info	*info = dev->driver_info;	mutex_lock (&dev->mutex);	// put into "known safe" state	if (info->reset && (retval = info->reset (dev)) < 0) {		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) {		devdbg (dev, "can't open; %d", retval);		goto done;	}	netif_start_queue (net);	if (dev->msg_level >= 2)		devinfo (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;}/*-------------------------------------------------------------------------*/static int usbnet_ethtool_ioctl (struct net_device *net, void *useraddr){	struct usbnet	*dev = (struct usbnet *) net->priv;	u32		cmd;	if (get_user (cmd, (u32 *)useraddr))		return -EFAULT;	switch (cmd) {	case ETHTOOL_GDRVINFO: {	/* get driver info */		struct ethtool_drvinfo		info;		memset (&info, 0, sizeof info);		info.cmd = ETHTOOL_GDRVINFO;		strncpy (info.driver, 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);		if (copy_to_user (useraddr, &info, sizeof (info)))			return -EFAULT;		return 0;		}	case ETHTOOL_GLINK: 		/* get link status */		if (dev->driver_info->check_connect) {			struct ethtool_value	edata = { ETHTOOL_GLINK };			edata.data = dev->driver_info->check_connect (dev) == 0;			if (copy_to_user (useraddr, &edata, sizeof (edata)))				return -EFAULT;			return 0;		}		break;	case ETHTOOL_GMSGLVL: {		/* get message-level */		struct ethtool_value	edata = {ETHTOOL_GMSGLVL};		edata.data = dev->msg_level;		if (copy_to_user (useraddr, &edata, sizeof (edata)))			return -EFAULT;		return 0;		}	case ETHTOOL_SMSGLVL: {		/* set message-level */		struct ethtool_value	edata;		if (copy_from_user (&edata, useraddr, sizeof (edata)))			return -EFAULT;		dev->msg_level = edata.data;		return 0;		}		/* could also map RINGPARAM to RX/TX QLEN */	}        /* Note that the ethtool user space code requires EOPNOTSUPP */

⌨️ 快捷键说明

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