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

📄 ether.c

📁 LINUX2.4.18内核下的usb GADGET驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
	/* 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);		ep->driver_data = dev;		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 = 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 = 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 = 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 = 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 = 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)			(void) usb_ep_disable (dev->status_ep);#endif		dev->status = NULL;#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)		if (dev->rndis || !dev->cdc) {			if (dev->in)				(void) usb_ep_disable (dev->in_ep);			if (dev->out)				(void) usb_ep_disable (dev->out_ep);		}#endif		dev->in = 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) {		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);		}	}	if (dev->out) {		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);		}	}	if (dev->status) {		usb_ep_disable (dev->status_ep);	}	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;}/*-------------------------------------------------------------------------*/#ifdef	DEV_CONFIG_CDCstatic void eth_status_complete (struct usb_ep *ep, struct usb_request *req){	struct usb_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 == USB_CDC_NOTIFY_NETWORK_CONNECTION			&& value == 0) {		__le32	*data = req->buf + sizeof *event;		event->bmRequestType = 0xA1;		event->bNotificationType = USB_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 */		data [0] = data [1] = cpu_to_le32 (BITRATE (dev->gadget));		req->length = STATUS_BYTECOUNT;		value = usb_ep_queue (ep, req, GFP_ATOMIC);		DEBUG (dev, "send SPEED_CHANGE --> %d\n", value);		if (value == 0)			return;	} else if (value != -ECONNRESET)		DEBUG (dev, "event %02x --> %d\n",			event->bNotificationType, value);	event->bmRequestType = 0xff;}static void issue_start_status (struct eth_dev *dev){	struct usb_request		*req = dev->stat_req;	struct usb_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);	/* 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 = USB_CDC_NOTIFY_NETWORK_CONNECTION;	event->wValue = __constant_cpu_to_le16 (1);	/* connected */	event->wIndex = __constant_cpu_to_le16 (1);	event->wLength = 0;	req->length = sizeof *event;	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);}#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);}#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 USB_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 USB_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;	u16			wIndex = ctrl->wIndex;	u16			wValue = ctrl->wValue;	u16			wLength = ctrl->wLength;	/* 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 (wValue >> 8) {		case USB_DT_DEVICE:			value = min (wLength, (u16) sizeof device_desc);			memcpy (req->buf, &device_desc, value);			break;#ifdef CONFIG_USB_GADGET_DUALSPEED		case USB_DT_DEVICE_QUALIFIER:			if (!gadget->is_dualspeed)				break;			value = min (wLength, (u16) sizeof dev_qualifier);			memcpy (req->buf, &dev_qualifier, value);			break;		case USB_DT_OTHER_SPEED_CONFIG:			if (!gadget->is_dualspeed)				break;			// FALLTHROUGH#endif /* CONFIG_USB_GADGET_DUALSPEED */		case USB_DT_CONFIG:			value = config_buf (gadget->speed, req->buf,					wValue >> 8,					wValue & 0xff,					gadget->is_otg);			if (value >= 0)				value = min (wLength, (u16) value);			break;		case USB_DT_STRING:			value = usb_gadget_get_string (&stringtab,					wValue & 0xff, req->buf);			if (value >= 0)				value = min (wLength, (u16) value);			break;		}		break;	case USB_REQ_SET_CONFIGURATION:		if (ctrl->bRequestType != 0)			break;		if (gadget->a_hnp_support)			DEBUG (dev, "HNP available\n");		else if (gadget->a_alt_hnp_support)			DEBUG (dev, "HNP needs a different root port\n");		spin_lock (&dev->lock);		value = eth_set_config (dev, wValue, GFP_ATOMIC);		spin_unlock (&dev->lock);		break;	case USB_REQ_GET_CONFIGURATION:		if (ctrl->bRequestType != USB_DIR_IN)			break;		*(u8 *)req->buf = dev->config;		value = min (wLength, (u16) 1);		break;	case USB_REQ_SET_INTERFACE:		if (ctrl->bRequestType != USB_RECIP_INTERFACE				|| !dev->config				|| wIndex > 1)			break;		if (!dev->cdc && wIndex != 0)			break;		spin_lock (&dev->lock);		/* PXA hardware partially handles SET_INTERFACE;		 * we need to kluge around that interference.		 */		if (gadget_is_pxa (gadget)) {			value = eth_set_config (dev, DEV_CONFIG_VALUE,						GFP_ATOMIC);			goto done_set_intf;		}#ifdef DEV_CONFIG_CDC		switch (wIndex) {		case 0:		/* control/master intf */			if (wValue != 0)				break;			if (dev->status) {				usb_ep_disable (dev->status_ep);				usb_ep_enable (dev->status_ep, dev->status);			}

⌨️ 快捷键说明

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