📄 ether.c
字号:
if (!gadget->is_dualspeed) break; value = min (ctrl->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, ctrl->wValue >> 8, ctrl->wValue & 0xff, gadget->is_otg); if (value >= 0) value = min (ctrl->wLength, (u16) value); break; case USB_DT_STRING: value = usb_gadget_get_string (&stringtab, ctrl->wValue & 0xff, req->buf); if (value >= 0) value = min (ctrl->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, ctrl->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 (ctrl->wLength, (u16) 1); break; case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE || !dev->config || ctrl->wIndex > 1) break; if (!dev->cdc && ctrl->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 (ctrl->wIndex) { case 0: /* control/master intf */ if (ctrl->wValue != 0) break; if (dev->status_ep) { usb_ep_disable (dev->status_ep); usb_ep_enable (dev->status_ep, dev->status); } value = 0; break; case 1: /* data intf */ if (ctrl->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 clears filters etc. */ if (ctrl->wValue == 1) { usb_ep_enable (dev->in_ep, dev->in); usb_ep_enable (dev->out_ep, dev->out); netif_carrier_on (dev->net); if (dev->status_ep) 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 || ctrl->wIndex > 1) break; if (!(dev->cdc || dev->rndis) && ctrl->wIndex != 0) break; /* for CDC, iff carrier is on, data interface is active. */ if (dev->rndis || ctrl->wIndex != 1) *(u8 *)req->buf = 0; else *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; value = min (ctrl->wLength, (u16) 1); break;#ifdef DEV_CONFIG_CDC case 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) || !dev->cdc || dev->rndis || ctrl->wLength != 0 || ctrl->wIndex > 1) break; DEBUG (dev, "NOP packet filter %04x\n", ctrl->wValue); /* NOTE: table 62 has 5 filter bits to reduce traffic, * and we "must" support multicast and promiscuous. * this NOP implements a bad filter (always promisc) */ dev->cdc_filter = ctrl->wValue; value = 0; break;#endif /* DEV_CONFIG_CDC */#ifdef CONFIG_USB_ETH_RNDIS /* RNDIS uses the CDC command encapsulation mechanism to implement * an RPC scheme, with much getting/setting of attributes by OID. */ case CDC_SEND_ENCAPSULATED_COMMAND: if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) || !dev->rndis || ctrl->wLength > USB_BUFSIZ || ctrl->wValue || rndis_control_intf.bInterfaceNumber != ctrl->wIndex) break; /* read the request, then process it */ value = ctrl->wLength; req->complete = rndis_command_complete; /* later, rndis_control_ack () sends a notification */ break; case CDC_GET_ENCAPSULATED_RESPONSE: if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) == ctrl->bRequestType && dev->rndis // && ctrl->wLength >= 0x0400 && !ctrl->wValue && rndis_control_intf.bInterfaceNumber == ctrl->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;#endif /* RNDIS */ default: VDEBUG (dev, "unknown control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, ctrl->wLength); } /* respond with data transfer before status phase? */ if (value >= 0) { req->length = value; req->zero = value < ctrl->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); // FIXME if rndis, don't change while link's live 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;}static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p){ struct eth_dev *dev = netdev_priv(net); strlcpy(p->driver, shortname, sizeof p->driver); strlcpy(p->version, DRIVER_VERSION, sizeof p->version); strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); strlcpy (p->bus_info, dev->gadget->dev.bus_id, sizeof p->bus_info);}static u32 eth_get_link(struct net_device *net){ struct eth_dev *dev = netdev_priv(net); return dev->gadget->speed != USB_SPEED_UNKNOWN;}static struct ethtool_ops ops = { .get_drvinfo = eth_get_drvinfo, .get_link = eth_get_link};static void defer_kevent (struct eth_dev *dev, int flag){ if (test_and_set_bit (flag, &dev->todo)) return; if (!schedule_work (&dev->work)) ERROR (dev, "kevent %d may have been dropped\n", flag); else DEBUG (dev, "kevent %d scheduled\n", flag);}static void rx_complete (struct usb_ep *ep, struct usb_request *req);static intrx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags){ struct sk_buff *skb; int retval = -ENOMEM; size_t size; /* Padding up to RX_EXTRA handles minor disagreements with host. * Normally we use the USB "terminate on short read" convention; * so allow up to (N*maxpacket), since that memory is normally * already allocated. Some hardware doesn't deal well with short * reads (e.g. DMA must be N*maxpacket), so for now don't trim a * byte off the end (to force hardware errors on overflow). * * RNDIS uses internal framing, and explicitly allows senders to * pad to end-of-packet. That's potentially nice for speed, * but means receivers can't recover synch on their own. */ size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA); size += dev->out_ep->maxpacket - 1;#ifdef CONFIG_USB_ETH_RNDIS if (dev->rndis) size += sizeof (struct rndis_packet_msg_type);#endif size -= size % dev->out_ep->maxpacket; if ((skb = alloc_skb (size, gfp_flags)) == 0) { DEBUG (dev, "no rx skb\n"); goto enomem; } req->buf = skb->data; req->length = size; req->complete = rx_complete; req->context = skb; retval = usb_ep_queue (dev->out_ep, req, gfp_flags); if (retval == -ENOMEM)enomem: defer_kevent (dev, WORK_RX_MEMORY); if (retval) { DEBUG (dev, "rx submit --> %d\n", retval); dev_kfree_skb_any (skb); spin_lock (&dev->lock); list_add (&req->list, &dev->rx_reqs); spin_unlock (&dev->lock); } return retval;}static void rx_complete (struct usb_ep *ep, struct usb_request *req){ struct sk_buff *skb = req->context; struct eth_dev *dev = ep->driver_data; int status = req->status; switch (status) { /* normal completion */ case 0: skb_put (skb, req->actual);#ifdef CONFIG_USB_ETH_RNDIS /* we know MaxPacketsPerTransfer == 1 here */ if (dev->rndis) rndis_rm_hdr (req->buf, &(skb->len));#endif if (ETH_HLEN > skb->len || skb->len > ETH_FRAME_LEN) { dev->stats.rx_errors++; dev->stats.rx_length_errors++; DEBUG (dev, "rx length %d\n", skb->len); break; } skb->dev = dev->net; skb->protocol = eth_type_trans (skb, dev->net); dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; /* no buffer copies needed, unless hardware can't * use skb buffers. */ status = netif_rx (skb); skb = NULL; break; /* software-driven interface shutdown */ case -ECONNRESET: // unlink case -ESHUTDOWN: // disconnect etc VDEBUG (dev, "rx shutdown, code %d\n", status); goto quiesce; /* for hardware automagic (such as pxa) */ case -ECONNABORTED: // endpoint reset DEBUG (dev, "rx %s reset\n", ep->name); defer_kevent (dev, WORK_RX_MEMORY);quiesce: dev_kfree_skb_any (skb); goto clean; /* data overrun */ case -EOVERFLOW: dev->stats.rx_over_errors++; // FALLTHROUGH default: dev->stats.rx_errors++; DEBUG (dev, "rx status %d\n", status); break; } if (skb) dev_kfree_skb_any (skb); if (!netif_running (dev->net)) {clean: /* nobody reading rx_reqs, so no dev->lock */ list_add (&req->list, &dev->rx_reqs); req = NULL; } if (req) rx_submit (dev, req, GFP_ATOMIC);}static int prealloc (struct list_head *list, struct usb_ep *ep, unsigned n, int gfp_flags){ unsigned i; struct usb_request *req; if (!n) return -ENOMEM; /* queue/recycle up to N requests */ i = n; list_for_each_entry (req, list, list) { if (i-- == 0) goto extra; } while (i--) { req = usb_ep_alloc_request (ep, gfp_flags); if (!req) return list_empty (list) ? -ENOMEM : 0; list_add (&req->list, list); } return 0;extra: /* free extras */ for (;;) { struct list_head *next; next = req->list.next; list_del (&req->list); usb_ep_free_request (ep, req); if (next == list) break; req = container_of (next, struct usb_request, list); } return 0;}static int alloc_requests (struct eth_dev *dev, unsigned n, int gfp_flags){ int status; status = prealloc (&dev->tx_reqs, dev->in_ep, n, gfp_flags); if (status < 0) goto fail; status = prealloc (&dev->rx_reqs, dev->out_ep, n, gfp_flags); if (status < 0) goto fail; return 0;fail: DEBUG (dev, "can't alloc requests\n"); return status;}static void rx_fill (struct eth_dev *dev, int gfp_flags){ struct usb_request *req; unsigned long flags; clear_bit (WORK_RX_MEMORY, &dev->todo); /* fill unused rxq slots with some skb */ spin_lock_irqsave (&dev->lock, flags); while (!list_empty (&dev->rx_reqs)) { req = container_of (dev->rx_reqs.next, struct usb_request, list); list_del_init (&req->list);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -