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