📄 ether.c
字号:
if (result != 0) { DEBUG (dev, "enable %s --> %d\n", dev->status_ep->name, result); goto done; } }#endif dev->in = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); dev->in_ep->driver_data = dev; dev->out = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); dev->out_ep->driver_data = dev; /* With CDC, the host isn't allowed to use these two data * endpoints in the default altsetting for the interface. * so we don't activate them yet. Reset from SET_INTERFACE. * * Strictly speaking RNDIS should work the same: activation is * a side effect of setting a packet filter. Deactivation is * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG. */ if (!cdc_active(dev)) { result = usb_ep_enable (dev->in_ep, dev->in); if (result != 0) { DEBUG(dev, "enable %s --> %d\n", dev->in_ep->name, result); goto done; } result = usb_ep_enable (dev->out_ep, dev->out); if (result != 0) { DEBUG (dev, "enable %s --> %d\n", dev->in_ep->name, result); goto done; } }done: if (result == 0) result = alloc_requests (dev, qlen (gadget), gfp_flags); /* on error, disable any endpoints */ if (result < 0) { if (!subset_active(dev)) (void) usb_ep_disable (dev->status_ep); dev->status = NULL; (void) usb_ep_disable (dev->in_ep); (void) usb_ep_disable (dev->out_ep); dev->in = NULL; dev->out = NULL; } else /* activate non-CDC configs right away * this isn't strictly according to the RNDIS spec */ if (!cdc_active (dev)) { netif_carrier_on (dev->net); if (netif_running (dev->net)) { spin_unlock (&dev->lock); eth_start (dev, GFP_ATOMIC); spin_lock (&dev->lock); } } 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); rndis_uninit(dev->rndis_config); /* 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->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;#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, rndis_active(dev) ? "RNDIS" : (cdc_active(dev) ? "CDC Ethernet" : "CDC Ethernet Subset")); } return result;}/*-------------------------------------------------------------------------*/#ifdef DEV_CONFIG_CDC/* 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);}#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 = 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;#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 (!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; }#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); } 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; }#else /* FIXME this is wrong, as is the assumption that * all non-PXA hardware talks real CDC ... */ dev_warn (&gadget->dev, "set_interface ignored!\n");#endif /* DEV_CONFIG_CDC */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;#ifdef DEV_CONFIG_CDC 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -