📄 ether.c
字号:
}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 + -