ether.c

来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 2,360 行 · 第 1/5 页

C
2,360
字号
	}	dev->rndis = 0;	dev->cdc_filter = 0;	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, gfp_t gfp_flags){	int			result = 0;	struct usb_gadget	*gadget = dev->gadget;	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);	switch (number) {	case DEV_CONFIG_VALUE:		result = set_ether_config (dev, gfp_flags);		break;	case DEV_RNDIS_CONFIG_VALUE:		dev->rndis = 1;		result = set_ether_config (dev, gfp_flags);		break;	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;		case USB_SPEED_HIGH:	speed = "high"; break;		default:		speed = "?"; break;		}		dev->config = number;		INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n",				speed, number, power, driver_desc,				rndis_active(dev)					? "RNDIS"					: (cdc_active(dev)						? "CDC Ethernet"						: "CDC Ethernet Subset"));	}	return result;}/*-------------------------------------------------------------------------*//* The interrupt endpoint is used in CDC networking models (Ethernet, ATM) * only to notify the host about link status changes (which we support) or * report completion of some encapsulated command (as used in RNDIS).  Since * we want this CDC Ethernet code to be vendor-neutral, we don't use that * command mechanism; and only one status request is ever queued. */static 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);	req->context = NULL;}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...	 *	 * FIXME iff req->context != null just dequeue it	 */	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;	req->context = dev;	value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC);	if (value < 0)		DEBUG (dev, "status buf queue --> %d\n", value);}/*-------------------------------------------------------------------------*/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);}static 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);}/* * 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 = le16_to_cpu(ctrl->wIndex);	u16			wValue = le16_to_cpu(ctrl->wValue);	u16			wLength = le16_to_cpu(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;		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		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 (!cdc_active(dev) && 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;		}		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);			}			value = 0;			break;		case 1:		/* data intf */			if (wValue > 1)				break;			usb_ep_disable (dev->in_ep);			usb_ep_disable (dev->out_ep);			/* CDC requires the data transfers not be done from			 * the default interface setting ... also, setting			 * the non-default interface resets filters etc.			 */			if (wValue == 1) {				if (!cdc_active (dev))					break;				usb_ep_enable (dev->in_ep, dev->in);				usb_ep_enable (dev->out_ep, dev->out);				dev->cdc_filter = DEFAULT_FILTER;				netif_carrier_on (dev->net);				if (dev->status)					issue_start_status (dev);				if (netif_running (dev->net)) {					spin_unlock (&dev->lock);					eth_start (dev, GFP_ATOMIC);					spin_lock (&dev->lock);				}			} else {				netif_stop_queue (dev->net);				netif_carrier_off (dev->net);			}			value = 0;			break;		}done_set_intf:		spin_unlock (&dev->lock);		break;	case USB_REQ_GET_INTERFACE:		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)				|| !dev->config				|| wIndex > 1)			break;		if (!(cdc_active(dev) || rndis_active(dev)) && wIndex != 0)			break;		/* for CDC, iff carrier is on, data interface is active. */		if (rndis_active(dev) || wIndex != 1)			*(u8 *)req->buf = 0;		else			*(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0;		value = min (wLength, (u16) 1);		break;	case USB_CDC_SET_ETHERNET_PACKET_FILTER:		/* see 6.2.30: no data, wIndex = interface,		 * wValue = packet filter bitmap		 */		if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)				|| !cdc_active(dev)				|| wLength != 0				|| wIndex > 1)			break;		DEBUG (dev, "packet filter %02x\n", wValue);		dev->cdc_filter = wValue;		value = 0;		break;	/* and potentially:	 * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:	 * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:	 * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:	 * case USB_CDC_GET_ETHERNET_STATISTIC:	 */	/* RNDIS uses the CDC command encapsulation mechanism to implement	 * an RPC scheme, with much getting/setting of attributes by OID.	 */	case USB_CDC_SEND_ENCAPSULATED_COMMAND:		if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)				|| !rndis_active(dev)				|| wLength > USB_BUFSIZ				|| wValue				|| rndis_control_intf.bInterfaceNumber					!= wIndex)			break;		/* read the request, then process it */		value = wLength;		req->complete = rndis_command_complete;		/* later, rndis_control_ack () sends a notification */		break;	case USB_CDC_GET_ENCAPSULATED_RESPONSE:		if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)					== ctrl->bRequestType				&& rndis_active(dev)				// && wLength >= 0x0400				&& !wValue				&& rndis_control_intf.bInterfaceNumber					== wIndex) {			u8 *buf;			/* return the result */			buf = rndis_get_next_response (dev->rndis_config,						       &value);			if (buf) {				memcpy (req->buf, buf, value);				req->complete = rndis_response_complete;				rndis_free_response(dev->rndis_config, buf);			}			/* else stalls ... spec says to avoid that */		}		break;	default:		VDEBUG (dev,			"unknown control req%02x.%02x v%04x i%04x l%d\n",			ctrl->bRequestType, ctrl->bRequest,			wValue, wIndex, wLength);	}	/* respond with data transfer before status phase? */	if (value >= 0) {		req->length = value;		req->zero = value < wLength				&& (value % gadget->ep0->maxpacket) == 0;		value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);		if (value < 0) {			DEBUG (dev, "ep_queue --> %d\n", value);			req->status = 0;			eth_setup_complete (gadget->ep0, req);		}	}	/* host either stalls (value < 0) or reports success */	return value;}static voideth_disconnect (struct usb_gadget *gadget){	struct eth_dev		*dev = get_gadget_data (gadget);	unsigned long		flags;	spin_lock_irqsave (&dev->lock, flags);	netif_stop_queue (dev->net);	netif_carrier_off (dev->net);	eth_reset_config (dev);	spin_unlock_irqrestore (&dev->lock, flags);	/* FIXME RNDIS should enter RNDIS_UNINITIALIZED */	/* next we may get setup() calls to enumerate new connections;	 * or an unbind() during shutdown (including removing module).	 */}/*-------------------------------------------------------------------------*//* NETWORK DRIVER HOOKUP (to the layer above this driver) */static int eth_change_mtu (struct net_device *net, int new_mtu){	struct eth_dev	*dev = netdev_priv(net);	if (dev->rndis)		return -EBUSY;	if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)		return -ERANGE;	/* no zero-length packet read wanted after mtu-sized packets */	if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0)		return -EDOM;	net->mtu = new_mtu;	return 0;}static struct net_device_stats *eth_get_stats (struct net_device *net){	return &((struct eth_dev *)netdev_priv(net))->stats;

⌨️ 快捷键说明

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