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

📄 ether.c

📁 LINUX2.4.18内核下的usb GADGET驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
}static int eth_start_xmit (struct sk_buff *skb, struct net_device *net){	struct eth_dev		*dev = netdev_priv(net);	int			length = skb->len;	int			retval;	struct usb_request	*req = NULL;	unsigned long		flags;	/* apply outgoing CDC or RNDIS filters */	if (!eth_is_promisc (dev)) {		u8		*dest = skb->data;		if (dest [0] & 0x01) {			u16	type;			/* ignores USB_CDC_PACKET_TYPE_MULTICAST and host			 * SET_ETHERNET_MULTICAST_FILTERS requests			 */			if (memcmp (dest, net->broadcast, ETH_ALEN) == 0)				type = USB_CDC_PACKET_TYPE_BROADCAST;			else				type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;			if (!(dev->cdc_filter & type)) {				dev_kfree_skb_any (skb);				return 0;			}		}		/* ignores USB_CDC_PACKET_TYPE_DIRECTED */	}	spin_lock_irqsave (&dev->lock, flags);	req = container_of (dev->tx_reqs.next, struct usb_request, list);	list_del (&req->list);	if (list_empty (&dev->tx_reqs))		netif_stop_queue (net);	spin_unlock_irqrestore (&dev->lock, flags);	/* no buffer copies needed, unless the network stack did it	 * or the hardware can't use skb buffers.	 * or there's not enough space for any RNDIS headers we need	 */#ifdef CONFIG_USB_ETH_RNDIS	if (dev->rndis) {		struct sk_buff	*skb_rndis;		skb_rndis = skb_realloc_headroom (skb,				sizeof (struct rndis_packet_msg_type));		if (!skb_rndis)			goto drop;			dev_kfree_skb_any (skb);		skb = skb_rndis;		rndis_add_hdr (skb);		length = skb->len;	}#endif	req->buf = skb->data;	req->context = skb;	req->complete = tx_complete;	/* use zlp framing on tx for strict CDC-Ether conformance,	 * though any robust network rx path ignores extra padding.	 * and some hardware doesn't like to write zlps.	 */	req->zero = 1;	if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0)		length++;	req->length = length;#ifdef	CONFIG_USB_GADGET_DUALSPEED	/* throttle highspeed IRQ rate back slightly */	req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)		? ((atomic_read (&dev->tx_qlen) % TX_DELAY) != 0)		: 0;#endif	retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);	switch (retval) {	default:		DEBUG (dev, "tx queue err %d\n", retval);		break;	case 0:		net->trans_start = jiffies;		atomic_inc (&dev->tx_qlen);	}	if (retval) {#ifdef CONFIG_USB_ETH_RNDISdrop:#endif		dev->stats.tx_dropped++;		dev_kfree_skb_any (skb);		spin_lock_irqsave (&dev->lock, flags);		if (list_empty (&dev->tx_reqs))			netif_start_queue (net);		list_add (&req->list, &dev->tx_reqs);		spin_unlock_irqrestore (&dev->lock, flags);	}	return 0;}/*-------------------------------------------------------------------------*/#ifdef CONFIG_USB_ETH_RNDISstatic void rndis_send_media_state (struct eth_dev *dev, int connect){	if (!dev)		return;		if (connect) {		if (rndis_signal_connect (dev->rndis_config))			return;	} else {		if (rndis_signal_disconnect (dev->rndis_config))			return;	}}static voidrndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req){	if (req->status || req->actual != req->length)		DEBUG ((struct eth_dev *) ep->driver_data,			"rndis control ack complete --> %d, %d/%d\n",			req->status, req->actual, req->length);	usb_ep_free_buffer(ep, req->buf, req->dma, 8);	usb_ep_free_request(ep, req);}static int rndis_control_ack (struct net_device *net){	struct eth_dev          *dev = netdev_priv(net);	u32                     length;	struct usb_request      *resp;		/* in case RNDIS calls this after disconnect */	if (!dev->status_ep) {		DEBUG (dev, "status ENODEV\n");		return -ENODEV;	}	/* Allocate memory for notification ie. ACK */	resp = usb_ep_alloc_request (dev->status_ep, GFP_ATOMIC);	if (!resp) {		DEBUG (dev, "status ENOMEM\n");		return -ENOMEM;	}		resp->buf = usb_ep_alloc_buffer (dev->status_ep, 8,					 &resp->dma, GFP_ATOMIC);	if (!resp->buf) {		DEBUG (dev, "status buf ENOMEM\n");		usb_ep_free_request (dev->status_ep, resp);		return -ENOMEM;	}		/* Send RNDIS RESPONSE_AVAILABLE notification;	 * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too	 */	resp->length = 8;	resp->complete = rndis_control_ack_complete;		*((__le32 *) resp->buf) = __constant_cpu_to_le32 (1);	*((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0);		length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC);	if (length < 0) {		resp->status = 0;		rndis_control_ack_complete (dev->status_ep, resp);	}		return 0;}#endif	/* RNDIS */static void eth_start (struct eth_dev *dev, int gfp_flags){	DEBUG (dev, "%s\n", __FUNCTION__);	/* fill the rx queue */	rx_fill (dev, gfp_flags);	/* and open the tx floodgates */ 	atomic_set (&dev->tx_qlen, 0);	netif_wake_queue (dev->net);#ifdef CONFIG_USB_ETH_RNDIS	if (dev->rndis) {		rndis_set_param_medium (dev->rndis_config,					NDIS_MEDIUM_802_3,					BITRATE(dev->gadget));		rndis_send_media_state (dev, 1);	}#endif	}static int eth_open (struct net_device *net){	struct eth_dev		*dev = netdev_priv(net);	DEBUG (dev, "%s\n", __FUNCTION__);	if (netif_carrier_ok (dev->net))		eth_start (dev, GFP_KERNEL);	return 0;}static int eth_stop (struct net_device *net){	struct eth_dev		*dev = netdev_priv(net);	VDEBUG (dev, "%s\n", __FUNCTION__);	netif_stop_queue (net);	DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",		dev->stats.rx_packets, dev->stats.tx_packets, 		dev->stats.rx_errors, dev->stats.tx_errors		);	/* ensure there are no more active requests */	if (dev->config) {		usb_ep_disable (dev->in_ep);		usb_ep_disable (dev->out_ep);		if (netif_carrier_ok (dev->net)) {			DEBUG (dev, "host still using in/out endpoints\n");			// FIXME idiom may leave toggle wrong here			usb_ep_enable (dev->in_ep, dev->in);			usb_ep_enable (dev->out_ep, dev->out);		}		if (dev->status_ep) {			usb_ep_disable (dev->status_ep);			usb_ep_enable (dev->status_ep, dev->status);		}	}	#ifdef	CONFIG_USB_ETH_RNDIS	if (dev->rndis) {		rndis_set_param_medium (dev->rndis_config,					NDIS_MEDIUM_802_3, 0);		rndis_send_media_state (dev, 0);	}#endif	return 0;}/*-------------------------------------------------------------------------*/static struct usb_request *eth_req_alloc (struct usb_ep *ep, unsigned size){	struct usb_request	*req;	req = usb_ep_alloc_request (ep, GFP_KERNEL);	if (!req)		return NULL;	req->buf = kmalloc (size, GFP_KERNEL);	if (!req->buf) {		usb_ep_free_request (ep, req);		req = NULL;	}	return req;}static voideth_req_free (struct usb_ep *ep, struct usb_request *req){	kfree (req->buf);	usb_ep_free_request (ep, req);}static voideth_unbind (struct usb_gadget *gadget){	struct eth_dev		*dev = get_gadget_data (gadget);	DEBUG (dev, "unbind\n");#ifdef CONFIG_USB_ETH_RNDIS	rndis_deregister (dev->rndis_config);	rndis_exit ();#endif	/* we've already been disconnected ... no i/o is active */	if (dev->req) {		eth_req_free (gadget->ep0, dev->req);		dev->req = NULL;	}	if (dev->stat_req) {		eth_req_free (dev->status_ep, dev->stat_req);		dev->stat_req = NULL;	}	unregister_netdev (dev->net);	free_netdev(dev->net);	/* assuming we used keventd, it must quiesce too */	flush_scheduled_work ();	set_gadget_data (gadget, NULL);}static u8 __init nibble (unsigned char c){	if (likely (isdigit (c)))		return c - '0';	c = toupper (c);	if (likely (isxdigit (c)))		return 10 + c - 'A';	return 0;}static void __init get_ether_addr (const char *str, u8 *dev_addr){	if (str) {		unsigned	i;		for (i = 0; i < 6; i++) {			unsigned char num;			if((*str == '.') || (*str == ':'))				str++;			num = nibble(*str++) << 4;			num |= (nibble(*str++));			dev_addr [i] = num;		}		if (is_valid_ether_addr (dev_addr))			return;	}	random_ether_addr(dev_addr);}static int __initeth_bind (struct usb_gadget *gadget){	struct eth_dev		*dev;	struct net_device	*net;	u8			cdc = 1, zlp = 1, rndis = 1;	struct usb_ep		*in_ep, *out_ep, *status_ep = NULL;	int			status = -ENOMEM;	/* these flags are only ever cleared; compiler take note */#ifndef	DEV_CONFIG_CDC	cdc = 0;#endif#ifndef	CONFIG_USB_ETH_RNDIS	rndis = 0;#endif	/* Because most host side USB stacks handle CDC Ethernet, that	 * standard protocol is _strongly_ preferred for interop purposes.	 * (By everyone except Microsoft.)	 */	if (gadget_is_net2280 (gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201);	} else if (gadget_is_dummy (gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0202);	} else if (gadget_is_pxa (gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0203);		/* pxa doesn't support altsettings */		cdc = 0;	} else if (gadget_is_sh(gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204);		/* sh doesn't support multiple interfaces or configs */		cdc = 0;		rndis = 0;	} else if (gadget_is_sa1100 (gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205);		/* hardware can't write zlps */		zlp = 0;		/* sa1100 CAN do CDC, without status endpoint ... we use		 * non-CDC to be compatible with ARM Linux-2.4 "usb-eth".		 */		cdc = 0;	} else if (gadget_is_goku (gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0206);	} else if (gadget_is_mq11xx (gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207);	} else if (gadget_is_omap (gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);	} else if (gadget_is_lh7a40x(gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209);	} else if (gadget_is_n9604(gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210);	} else if (gadget_is_pxa27x(gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211);	} else if (gadget_is_s3c2410(gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0212);	} else if (gadget_is_at91(gadget)) {		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0213);	} else {		/* can't assume CDC works.  don't want to default to		 * anything less functional on CDC-capable hardware,		 * so we fail in this case.		 */		printk (KERN_ERR "%s: "			"controller '%s' not recognized\n",			shortname, gadget->name);		return -ENODEV;	}	snprintf (manufacturer, sizeof manufacturer,		UTS_SYSNAME " " UTS_RELEASE "/%s",		gadget->name);	/* If there's an RNDIS configuration, that's what Windows wants to	 * be using ... so use these product IDs here and in the "linux.inf"	 * needed to install MSFT drivers.  Current Linux kernels will use	 * the second configuration if it's CDC Ethernet, and need some help	 * to choose the right configuration otherwise.	 */	if (rndis) {		device_desc.idVendor =			__constant_cpu_to_le16(RNDIS_VENDOR_NUM);		device_desc.idProduct =			__constant_cpu_to_le16(RNDIS_PRODUCT_NUM);		snprintf (product_desc, sizeof product_desc,			"RNDIS/%s", driver_desc);	/* CDC subset ... recognized by Linux since 2.4.10, but Windows	 * drivers aren't widely available.	 */	} else if (!cdc) {		device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;		device_desc.idVendor =			__constant_cpu_to_le16(SIMPLE_VENDOR_NUM);		device_desc.idProduct =			__constant_cpu_to_le16(SIMPLE_PRODUCT_NUM);	}	/* support optional vendor/distro customization */	if (idVendor) {		if (!idProduct) {			printk (KERN_ERR "%s: idVendor needs idProduct!\n",				shortname);			return -ENODEV;		}		device_desc.idVendor = cpu_to_le16(idVendor);		device_desc.idProduct = cpu_to_le16(idProduct);		if (bcdDevice)			device_desc.bcdDevice = cpu_to_le16(bcdDevice);	}	if (iManufacturer)		strncpy (manufacturer, iManufacturer, sizeof manufacturer);	if (iProduct)		strncpy (product_desc, iProduct, sizeof product_desc);	/* all we really need is bulk IN/OUT */	usb_ep_autoconfig_reset (gadget);	in_ep = usb_ep_autoconfig (gadget, &fs_source_desc);	if (!in_ep) {autoconf_fail:		printk (KERN_ERR "%s: can't autoconfigure on %s\n",			shortname, gadget->name);		return -ENODEV;	}	EP_IN_NAME = in_ep->name;	in_ep->driver_data = in_ep;	/* claim */		out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc);	if (!out_ep)		goto autoconf_fail;	EP_OUT_NAME = out_ep->name;	out_ep->driver_data = out_ep;	/* claim */#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)	/* CDC Ethernet control interface doesn't require a status endpoint.	 * Since some hosts expect one, try to allocate one anyway.	 */	if (cdc || rndis) {		status_ep = usb_ep_autoconfig (gadget, &fs_status_desc);		if (status_ep) {			EP_STATUS_NAME = status_ep->name;			status_ep->driver_data = status_ep;	/* claim */		} else if (rndis) {			printk (KERN_ERR "%s: "				"can't run RNDIS on %s\n",				shortname, gadget->name);			return -ENODEV;#ifdef DEV_CONFIG_CDC		/* pxa25x only does CDC subset; often used with RNDIS */		} else if (cdc) {			control_i

⌨️ 快捷键说明

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