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

📄 ether.c

📁 h内核
💻 C
📖 第 1 页 / 共 5 页
字号:
	if (strcmp (ep->name, EP_IN_NAME) == 0) {		d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc);		ep->driver_data = dev;		dev->in_ep = ep;		dev->in = d;	/* one endpoint just reads OUT packets */	} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {		d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc);		ep->driver_data = dev;		dev->out_ep = ep;		dev->out = d;	/* optional status/notification endpoint */	} else if (EP_STATUS_NAME &&			strcmp (ep->name, EP_STATUS_NAME) == 0) {		int			result;		d = ep_desc (dev->gadget, &hs_status_desc, &fs_status_desc);		result = usb_ep_enable (ep, d);		if (result < 0)			return result;		ep->driver_data = dev;		dev->status_ep = ep;		dev->status = d;	}	return 0;}#endif#if	defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)static inline int ether_ep_setup (struct eth_dev *dev, struct usb_ep *ep){	int					result;	const struct usb_endpoint_descriptor	*d;	/* CDC subset is simpler:  if the device is there,	 * it's live with rx and tx endpoints.	 *	 * Do this as a shortcut for RNDIS too.	 */	/* one endpoint writes data back IN to the host */	if (strcmp (ep->name, EP_IN_NAME) == 0) {		d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc);		result = usb_ep_enable (ep, d);		if (result < 0)			return result;		ep->driver_data = dev;		dev->in_ep = ep;		dev->in = d;	/* one endpoint just reads OUT packets */	} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {		d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc);		result = usb_ep_enable (ep, d);		if (result < 0)			return result;		ep->driver_data = dev;		dev->out_ep = ep;		dev->out = d;	}	return 0;}#endifstatic intset_ether_config (struct eth_dev *dev, int gfp_flags){	int			result = 0;	struct usb_ep		*ep;	struct usb_gadget	*gadget = dev->gadget;	gadget_for_each_ep (ep, gadget) {#ifdef	DEV_CONFIG_CDC		if (!dev->rndis && dev->cdc) {			result = ether_alt_ep_setup (dev, ep);			if (result == 0)				continue;		}#endif#ifdef	CONFIG_USB_ETH_RNDIS		if (dev->rndis && strcmp (ep->name, EP_STATUS_NAME) == 0) {			const struct usb_endpoint_descriptor	*d;			d = ep_desc (gadget, &hs_status_desc, &fs_status_desc);			result = usb_ep_enable (ep, d);			if (result == 0) {				ep->driver_data = dev;				dev->status_ep = ep;				dev->status = d;				continue;			}		} else#endif		{#if	defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)			result = ether_ep_setup (dev, ep);			if (result == 0)				continue;#endif		}		/* stop on error */		ERROR (dev, "can't enable %s, result %d\n", ep->name, result);		break;	}	if (!result && (!dev->in_ep || !dev->out_ep))		result = -ENODEV;	if (result == 0)		result = alloc_requests (dev, qlen (gadget), gfp_flags);	/* on error, disable any endpoints  */	if (result < 0) {#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)		if (dev->status_ep)			(void) usb_ep_disable (dev->status_ep);#endif		dev->status_ep = NULL;		dev->status = NULL;#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)		if (dev->rndis || !dev->cdc) {			if (dev->in_ep)				(void) usb_ep_disable (dev->in_ep);			if (dev->out_ep)				(void) usb_ep_disable (dev->out_ep);		}#endif		dev->in_ep = NULL;		dev->in = NULL;		dev->out_ep = NULL;		dev->out = NULL;	} else	/* activate non-CDC configs right away	 * this isn't strictly according to the RNDIS spec	 */#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)	if (dev->rndis || !dev->cdc) {		netif_carrier_on (dev->net);		if (netif_running (dev->net)) {			spin_unlock (&dev->lock);			eth_start (dev, GFP_ATOMIC);			spin_lock (&dev->lock);		}	}#endif	if (result == 0)		DEBUG (dev, "qlen %d\n", qlen (gadget));	/* caller is responsible for cleanup on error */	return result;}static void eth_reset_config (struct eth_dev *dev){	struct usb_request	*req;	if (dev->config == 0)		return;	DEBUG (dev, "%s\n", __FUNCTION__);	netif_stop_queue (dev->net);	netif_carrier_off (dev->net);	/* disable endpoints, forcing (synchronous) completion of	 * pending i/o.  then free the requests.	 */	if (dev->in_ep) {		usb_ep_disable (dev->in_ep);		while (likely (!list_empty (&dev->tx_reqs))) {			req = container_of (dev->tx_reqs.next,						struct usb_request, list);			list_del (&req->list);			usb_ep_free_request (dev->in_ep, req);		}		dev->in_ep = NULL;	}	if (dev->out_ep) {		usb_ep_disable (dev->out_ep);		while (likely (!list_empty (&dev->rx_reqs))) {			req = container_of (dev->rx_reqs.next,						struct usb_request, list);			list_del (&req->list);			usb_ep_free_request (dev->out_ep, req);		}		dev->out_ep = NULL;	}	if (dev->status_ep) {		usb_ep_disable (dev->status_ep);		dev->status_ep = NULL;	}	dev->config = 0;}/* change our operational config.  must agree with the code * that returns config descriptors, and altsetting code. */static inteth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags){	int			result = 0;	struct usb_gadget	*gadget = dev->gadget;	if (number == dev->config)		return 0;	if (gadget_is_sa1100 (gadget)			&& dev->config			&& atomic_read (&dev->tx_qlen) != 0) {		/* tx fifo is full, but we can't clear it...*/		INFO (dev, "can't change configurations\n");		return -ESPIPE;	}	eth_reset_config (dev);	/* default:  pass all packets, no multicast filtering */	dev->cdc_filter = 0x000f;	switch (number) {	case DEV_CONFIG_VALUE:		dev->rndis = 0;		result = set_ether_config (dev, gfp_flags);		break;#ifdef	CONFIG_USB_ETH_RNDIS	case DEV_RNDIS_CONFIG_VALUE:		dev->rndis = 1;		result = set_ether_config (dev, gfp_flags);		break;#endif	default:		result = -EINVAL;		/* FALL THROUGH */	case 0:		break;	}	if (result) {		if (number)			eth_reset_config (dev);		usb_gadget_vbus_draw(dev->gadget,				dev->gadget->is_otg ? 8 : 100);	} else {		char *speed;		unsigned power;		power = 2 * eth_config.bMaxPower;		usb_gadget_vbus_draw(dev->gadget, power);		switch (gadget->speed) {		case USB_SPEED_FULL:	speed = "full"; break;#ifdef CONFIG_USB_GADGET_DUALSPEED		case USB_SPEED_HIGH:	speed = "high"; break;#endif		default: 		speed = "?"; break;		}		dev->config = number;		INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n",				speed, number, power, driver_desc,				dev->rndis					? "RNDIS"					: (dev->cdc						? "CDC Ethernet"						: "CDC Ethernet Subset"));	}	return result;}/*-------------------------------------------------------------------------*//* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS * and RNDIS also defines its own bit-incompatible notifications */#define CDC_NOTIFY_NETWORK_CONNECTION	0x00	/* required; 6.3.1 */#define CDC_NOTIFY_RESPONSE_AVAILABLE	0x01	/* optional; 6.3.2 */#define CDC_NOTIFY_SPEED_CHANGE		0x2a	/* required; 6.3.8 */#ifdef	DEV_CONFIG_CDCstruct cdc_notification {	u8	bmRequestType;	u8	bNotificationType;	u16	wValue;	u16	wIndex;	u16	wLength;	/* SPEED_CHANGE data looks like this */	u32	data [2];};static void eth_status_complete (struct usb_ep *ep, struct usb_request *req){	struct cdc_notification	*event = req->buf;	int			value = req->status;	struct eth_dev		*dev = ep->driver_data;	/* issue the second notification if host reads the first */	if (event->bNotificationType == CDC_NOTIFY_NETWORK_CONNECTION			&& value == 0) {		event->bmRequestType = 0xA1;		event->bNotificationType = CDC_NOTIFY_SPEED_CHANGE;		event->wValue = __constant_cpu_to_le16 (0);		event->wIndex = __constant_cpu_to_le16 (1);		event->wLength = __constant_cpu_to_le16 (8);		/* SPEED_CHANGE data is up/down speeds in bits/sec */		event->data [0] = event->data [1] =			(dev->gadget->speed == USB_SPEED_HIGH)				? (13 * 512 * 8 * 1000 * 8)				: (19 *  64 * 1 * 1000 * 8);		req->length = 16;		value = usb_ep_queue (ep, req, GFP_ATOMIC);		DEBUG (dev, "send SPEED_CHANGE --> %d\n", value);		if (value == 0)			return;	} else		DEBUG (dev, "event %02x --> %d\n",			event->bNotificationType, value);	/* free when done */	usb_ep_free_buffer (ep, req->buf, req->dma, 16);	usb_ep_free_request (ep, req);}static void issue_start_status (struct eth_dev *dev){	struct usb_request	*req;	struct cdc_notification	*event;	int			value; 	DEBUG (dev, "%s, flush old status first\n", __FUNCTION__);	/* flush old status	 *	 * FIXME ugly idiom, maybe we'd be better with just	 * a "cancel the whole queue" primitive since any	 * unlink-one primitive has way too many error modes.	 * here, we "know" toggle is already clear...	 */	usb_ep_disable (dev->status_ep);	usb_ep_enable (dev->status_ep, dev->status);	/* FIXME make these allocations static like dev->req */	req = usb_ep_alloc_request (dev->status_ep, GFP_ATOMIC);	if (req == 0) {		DEBUG (dev, "status ENOMEM\n");		return;	}	req->buf = usb_ep_alloc_buffer (dev->status_ep, 16,				&dev->req->dma, GFP_ATOMIC);	if (req->buf == 0) {		DEBUG (dev, "status buf ENOMEM\n");free_req:		usb_ep_free_request (dev->status_ep, req);		return;	}	/* 3.8.1 says to issue first NETWORK_CONNECTION, then	 * a SPEED_CHANGE.  could be useful in some configs.	 */	event = req->buf;	event->bmRequestType = 0xA1;	event->bNotificationType = CDC_NOTIFY_NETWORK_CONNECTION;	event->wValue = __constant_cpu_to_le16 (1);	/* connected */	event->wIndex = __constant_cpu_to_le16 (1);	event->wLength = 0;	req->length = 8;	req->complete = eth_status_complete;	value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC);	if (value < 0) {		DEBUG (dev, "status buf queue --> %d\n", value);		usb_ep_free_buffer (dev->status_ep,				req->buf, dev->req->dma, 16);		goto free_req;	}}#endif/*-------------------------------------------------------------------------*/static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req){	if (req->status || req->actual != req->length)		DEBUG ((struct eth_dev *) ep->driver_data,				"setup complete --> %d, %d/%d\n",				req->status, req->actual, req->length);}/* see section 3.8.2 table 10 of the CDC spec for more ethernet * requests, mostly for filters (multicast, pm) and statistics * section 3.6.2.1 table 4 has ACM requests; RNDIS requires the * encapsulated command mechanism. */#define CDC_SEND_ENCAPSULATED_COMMAND		0x00	/* optional */#define CDC_GET_ENCAPSULATED_RESPONSE		0x01	/* optional */#define CDC_SET_ETHERNET_MULTICAST_FILTERS	0x40	/* optional */#define CDC_SET_ETHERNET_PM_PATTERN_FILTER	0x41	/* optional */#define CDC_GET_ETHERNET_PM_PATTERN_FILTER	0x42	/* optional */#define CDC_SET_ETHERNET_PACKET_FILTER		0x43	/* required */#define CDC_GET_ETHERNET_STATISTIC		0x44	/* optional *//* table 62; bits in cdc_filter */#define	CDC_PACKET_TYPE_PROMISCUOUS		(1 << 0)#define	CDC_PACKET_TYPE_ALL_MULTICAST		(1 << 1) /* no filter */#define	CDC_PACKET_TYPE_DIRECTED		(1 << 2)#define	CDC_PACKET_TYPE_BROADCAST		(1 << 3)#define	CDC_PACKET_TYPE_MULTICAST		(1 << 4) /* filtered */#ifdef CONFIG_USB_ETH_RNDISstatic void rndis_response_complete (struct usb_ep *ep, struct usb_request *req){	if (req->status || req->actual != req->length)		DEBUG ((struct eth_dev *) ep->driver_data,			"rndis response complete --> %d, %d/%d\n",			req->status, req->actual, req->length);	/* done sending after CDC_GET_ENCAPSULATED_RESPONSE */}static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req){	struct eth_dev          *dev = ep->driver_data;	int			status;		/* received RNDIS command from CDC_SEND_ENCAPSULATED_COMMAND */	spin_lock(&dev->lock);	status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf);	if (status < 0)		ERROR(dev, "%s: rndis parse error %d\n", __FUNCTION__, status);	spin_unlock(&dev->lock);}#endif	/* RNDIS *//* * The setup() callback implements all the ep0 functionality that's not * handled lower down.  CDC has a number of less-common features: * *  - two interfaces:  control, and ethernet data *  - Ethernet data interface has two altsettings:  default, and active *  - class-specific descriptors for the control interface *  - class-specific control requests */static inteth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl){	struct eth_dev		*dev = get_gadget_data (gadget);	struct usb_request	*req = dev->req;	int			value = -EOPNOTSUPP;	/* descriptors just go into the pre-allocated ep0 buffer,	 * while config change events may enable network traffic.	 */	req->complete = eth_setup_complete;	switch (ctrl->bRequest) {	case USB_REQ_GET_DESCRIPTOR:		if (ctrl->bRequestType != USB_DIR_IN)			break;		switch (ctrl->wValue >> 8) {		case USB_DT_DEVICE:			value = min (ctrl->wLength, (u16) sizeof device_desc);			memcpy (req->buf, &device_desc, value);			break;#ifdef CONFIG_USB_GADGET_DUALSPEED		case USB_DT_DEVICE_QUALIFIER:

⌨️ 快捷键说明

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