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