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

📄 ether.c

📁 ARM S3C2410 USB SLAVE LINUX驱动
💻 C
📖 第 1 页 / 共 5 页
字号:

		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
	 */
	if (rndis_active(dev)) {
		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;
	}
	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) {
drop:
		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_RNDIS

/* The interrupt endpoint is used in RNDIS to notify the host when messages
 * other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT
 * messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even
 * REMOTE_NDIS_KEEPALIVE_MSG.
 *
 * The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and
 * normally just one notification will be queued.
 */

static struct usb_request *eth_req_alloc (struct usb_ep *, unsigned, unsigned);
static void eth_req_free (struct usb_ep *ep, struct usb_request *req);

static void
rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
{
	struct eth_dev          *dev = ep->driver_data;

	if (req->status || req->actual != req->length)
		DEBUG (dev,
			"rndis control ack complete --> %d, %d/%d\n",
			req->status, req->actual, req->length);
	req->context = NULL;

	if (req != dev->stat_req)
		eth_req_free(ep, req);
}

static int rndis_control_ack (struct net_device *net)
{
	struct eth_dev          *dev = netdev_priv(net);
	u32                     length;
	struct usb_request      *resp = dev->stat_req;
	
	/* in case RNDIS calls this after disconnect */
	if (!dev->status) {
		DEBUG (dev, "status ENODEV\n");
		return -ENODEV;
	}

	/* in case queue length > 1 */
	if (resp->context) {
		resp = eth_req_alloc (dev->status_ep, 8, GFP_ATOMIC);
		if (!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;
	resp->context = dev;
	
	*((__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;
}

#else

#define	rndis_control_ack	NULL

#endif	/* RNDIS */

static void eth_start (struct eth_dev *dev, unsigned 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);
	if (rndis_active(dev)) {
		rndis_set_param_medium (dev->rndis_config,
					NDIS_MEDIUM_802_3,
					BITRATE(dev->gadget)/100);
		(void) rndis_signal_connect (dev->rndis_config);
	}
}

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);
		}
	}
	
	if (rndis_active(dev)) {
		rndis_set_param_medium (dev->rndis_config,
					NDIS_MEDIUM_802_3, 0);
		(void) rndis_signal_disconnect (dev->rndis_config);
	}

	return 0;
}

/*-------------------------------------------------------------------------*/

static struct usb_request *
eth_req_alloc (struct usb_ep *ep, unsigned size, unsigned gfp_flags)
{
	struct usb_request	*req;

	req = usb_ep_alloc_request (ep, gfp_flags);
	if (!req)
		return NULL;

	req->buf = kmalloc (size, gfp_flags);
	if (!req->buf) {
		usb_ep_free_request (ep, req);
		req = NULL;
	}
	return req;
}

static void
eth_req_free (struct usb_ep *ep, struct usb_request *req)
{
	kfree (req->buf);
	usb_ep_free_request (ep, req);
}


static void
eth_unbind (struct usb_gadget *gadget)
{
	struct eth_dev		*dev = get_gadget_data (gadget);

	DEBUG (dev, "unbind\n");
	rndis_deregister (dev->rndis_config);
	rndis_exit ();

	/* 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 __init
eth_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;
	int			gcnum;

	/* 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_pxa (gadget)) {
		/* pxa doesn't support altsettings */
		cdc = 0;
	} else if (gadget_is_sh(gadget)) {
		/* sh doesn't support multiple interfaces or configs */
		cdc = 0;
		rndis = 0;
	} else if (gadget_is_sa1100 (gadget)) {
		/* 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;
	}

	gcnum = usb_gadget_controller_number (gadget);
	if (gcnum >= 0)
		device_desc.bcdDevice = cpu_to_le16 (0x0200 + gcnum);
	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.
		 */
		dev_err (&gadget->dev,
			"controller '%s' not recognized\n",
			gadget->name);
		return -ENODEV;
	}
	snprintf (manufacturer, sizeof manufacturer, "%s %s/%s",
		system_utsname.sysname, system_utsname.release,
		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) {
			dev_err (&gadget->dev, "idVendor needs idProduct!\n");
			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)
		strlcpy (manufacturer, iManufacturer, sizeof manufacturer);
	if (iProduct)
		strlcpy (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:
		dev_err (&gadget->dev,
			"can't autoconfigure on %s\n",
			gadget->name);
		return -ENODEV;
	}
	in_ep->driver_data = in_ep;	/* claim */
	
	out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
	if (!out_ep)
		goto autoconf_fail;
	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) {
			status_ep->driver_data = status_ep;	/* claim */
		} else if (rndis) {
			dev_err (&gadget->dev,
				"can't run RNDIS on %s\n",
				gadget->name);
			return -ENODEV;
#ifdef DEV_CONFIG_CDC
		/* pxa25x only does CDC subset; often used with RNDIS */
		} else if (cdc) {
			control_intf.bNumEndpoints = 0;
			/* FIXME remove endpoint from descriptor list */
#endif
		}
	}
#endif

	/* one config:  cdc, else minimal subset */
	if (!cdc) {
		eth_config.bNumInterfaces = 1;
		eth_config.iConfiguration = STRING_SUBSET;
		fs_subset_descriptors();
		hs_subset_descriptors();
	}

	/* For now RNDIS is always a second config */
	if (rndis)
		device_desc.bNumConfigurations = 2;

#ifdef	CONFIG_USB_GADGET_DUALSPEED
	if (rndis)
		dev_qualifier.bNumConfigurations = 2

⌨️ 快捷键说明

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