/* * This function is a "CDC Ethernet Networking Control Model" (CDC ECM) * Ethernet link. The data transfer model is simple (packets sent and * received over bulk endpoints using normal short packet termination), * and the control model exposes various data and optional notifications. * * ECM is well standardized and (except for Microsoft) supported by most * operating systems with USB host support. It's the preferred interop * solution for Ethernet over USB, at least for firmware based solutions. * (Hardware solutions tend to be more minimalist.) A newer and simpler * "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on. * * Note that ECM requires the use of "alternate settings" for its data * interface. This means that the set_alt() method has real work to do, * and also means that a get_alt() method is required.
*/
/* * Include the status endpoint if we can, even though it's optional. * * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one * packet, to simplify cancellation; and a big transfer interval, to * waste less bandwidth. * * Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even * if they ignore the connect/disconnect notifications that real aether * can provide. More advanced cdc configurations might want to support * encapsulated commands (vendor-specific, using control-OUT).
*/
/* the following 3 values can be tweaked if necessary */ /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */
.wBytesPerInterval = cpu_to_le16(ECM_STATUS_BYTECOUNT),
};
/* NOTE: status endpoint might need to be removed */
(struct usb_descriptor_header *) &ss_ecm_notify_desc,
(struct usb_descriptor_header *) &ss_ecm_intr_comp_desc,
atomic_inc(&ecm->notify_count);
status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC); if (status < 0) {
atomic_dec(&ecm->notify_count);
DBG(cdev, "notify --> %d\n", status);
}
}
staticvoid ecm_notify(struct f_ecm *ecm)
{ /* NOTE on most versions of Linux, host side cdc-ethernet * won't listen for notifications until its netdevice opens. * The first notification then sits in the FIFO for a long * time, and the second one is queued.
*/
ecm->notify_state = ECM_NOTIFY_CONNECT;
ecm_do_notify(ecm);
}
/* composite driver infrastructure handles everything except * CDC class messages; interface activation uses set_alt().
*/ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_SET_ETHERNET_PACKET_FILTER: /* see 6.2.30: no data, wIndex = interface, * wValue = packet filter bitmap
*/ if (w_length != 0 || w_index != ecm->ctrl_id) goto invalid;
DBG(cdev, "packet filter %02x\n", w_value); /* REVISIT locking of cdc_filter. This assumes the UDC * driver won't have a concurrent packet TX irq running on * another CPU; or that if it does, this write is atomic...
*/
ecm->port.cdc_filter = w_value;
value = 0; break;
/* and optionally: * case USB_CDC_SEND_ENCAPSULATED_COMMAND: * case USB_CDC_GET_ENCAPSULATED_RESPONSE: * 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:
*/
/* CDC Ethernet only sends data in non-default altsettings. * Changing altsettings resets filters, statistics, etc.
*/ if (alt == 1) { struct net_device *net;
/* Enable zlps by default for ECM conformance; * override for musb_hdrc (avoids txdma ovhead).
*/
ecm->port.is_zlp_ok =
gadget_is_zlp_supported(cdev->gadget);
ecm->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate ecm\n");
net = gether_connect(&ecm->port); if (IS_ERR(net)) return PTR_ERR(net);
}
/* NOTE this can be a minor disagreement with the ECM spec, * which says speed notifications will "always" follow * connection notifications. But we allow one connect to * follow another (if the first is in flight), and instead * just guarantee that a speed notification is always sent.
*/
ecm_notify(ecm);
} else goto fail;
return 0;
fail: return -EINVAL;
}
/* Because the data interface supports multiple altsettings, * this ECM function *MUST* implement a get_alt() method.
*/ staticint ecm_get_alt(struct usb_function *f, unsigned intf)
{ struct f_ecm *ecm = func_to_ecm(f);
/* * Callbacks let us notify the host about connect/disconnect when the * net device is opened or closed. * * For testing, note that link states on this side include both opened * and closed variants of: * * - disconnected/unconfigured * - configured but inactive (data alt 0) * - configured and active (data alt 1) * * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and * SET_INTERFACE (altsetting). Remember also that "configured" doesn't * imply the host is actually polling the notification endpoint, and * likewise that "active" doesn't imply it's actually using the data * endpoints for traffic.
*/
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc); if (!ep) return -ENODEV;
ecm->port.in_ep = ep;
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc); if (!ep) return -ENODEV;
ecm->port.out_ep = ep;
/* NOTE: a status/notification endpoint is *OPTIONAL* but we * don't treat it that way. It's simpler, and some newer CDC * profiles (wireless handsets) no longer treat it as optional.
*/
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc); if (!ep) return -ENODEV;
ecm->notify = ep;
/* allocate notification request and buffer */
request = usb_ep_alloc_request(ep, GFP_KERNEL); if (!request) return -ENOMEM;
request->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL); if (!request->buf) return -ENOMEM;
request->context = ecm;
request->complete = ecm_notify_complete;
/* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds
*/
hs_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress;
hs_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress;
hs_ecm_notify_desc.bEndpointAddress =
fs_ecm_notify_desc.bEndpointAddress;
status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,
ecm_ss_function, ecm_ss_function); if (status) return status;
/* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code * until we're activated via set_alt().
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.