ether.c
来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 2,360 行 · 第 1/5 页
C
2,360 行
} 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; case DEV_RNDIS_CONFIG_VALUE: dev->rndis = 1; result = set_ether_config (dev, gfp_flags); break; 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; case USB_SPEED_HIGH: speed = "high"; break; 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;}/*-------------------------------------------------------------------------*//* 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);}/*-------------------------------------------------------------------------*/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);}static 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);}/* * 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; 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 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; } 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; }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; 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); dev->cdc_filter = wValue; value = 0; break; /* and potentially: * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: * case USB_CDC_GET_ETHERNET_STATISTIC: */ /* RNDIS uses the CDC command encapsulation mechanism to implement * an RPC scheme, with much getting/setting of attributes by OID. */ case USB_CDC_SEND_ENCAPSULATED_COMMAND: if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) || !rndis_active(dev) || wLength > USB_BUFSIZ || wValue || rndis_control_intf.bInterfaceNumber != wIndex) break; /* read the request, then process it */ value = wLength; req->complete = rndis_command_complete; /* later, rndis_control_ack () sends a notification */ break; case USB_CDC_GET_ENCAPSULATED_RESPONSE: if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) == ctrl->bRequestType && rndis_active(dev) // && wLength >= 0x0400 && !wValue && rndis_control_intf.bInterfaceNumber == wIndex) { u8 *buf; /* return the result */ buf = rndis_get_next_response (dev->rndis_config, &value); if (buf) { memcpy (req->buf, buf, value); req->complete = rndis_response_complete; rndis_free_response(dev->rndis_config, buf); } /* else stalls ... spec says to avoid that */ } break; default: VDEBUG (dev, "unknown control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); } /* respond with data transfer before status phase? */ if (value >= 0) { req->length = value; req->zero = value < wLength && (value % gadget->ep0->maxpacket) == 0; value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); if (value < 0) { DEBUG (dev, "ep_queue --> %d\n", value); req->status = 0; eth_setup_complete (gadget->ep0, req); } } /* host either stalls (value < 0) or reports success */ return value;}static voideth_disconnect (struct usb_gadget *gadget){ struct eth_dev *dev = get_gadget_data (gadget); unsigned long flags; spin_lock_irqsave (&dev->lock, flags); netif_stop_queue (dev->net); netif_carrier_off (dev->net); eth_reset_config (dev); spin_unlock_irqrestore (&dev->lock, flags); /* FIXME RNDIS should enter RNDIS_UNINITIALIZED */ /* next we may get setup() calls to enumerate new connections; * or an unbind() during shutdown (including removing module). */}/*-------------------------------------------------------------------------*//* NETWORK DRIVER HOOKUP (to the layer above this driver) */static int eth_change_mtu (struct net_device *net, int new_mtu){ struct eth_dev *dev = netdev_priv(net); if (dev->rndis) return -EBUSY; if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) return -ERANGE; /* no zero-length packet read wanted after mtu-sized packets */ if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0) return -EDOM; net->mtu = new_mtu; return 0;}static struct net_device_stats *eth_get_stats (struct net_device *net){ return &((struct eth_dev *)netdev_priv(net))->stats;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?