diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2008-06-30 11:21:27 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-06-30 11:21:27 -0700 |
| commit | 7b620c1ffab95e1cac06f0a5e0c292374249d184 (patch) | |
| tree | f9d4e3d65fc9ee092cc22ecbf2256e101a9b4c11 /usb | |
| parent | 33f43d082d6ea41c8c90e3b64f99b1b072180fad (diff) | |
| download | patches-7b620c1ffab95e1cac06f0a5e0c292374249d184.tar.gz | |
rework the usb gadget patches from David
Diffstat (limited to 'usb')
| -rw-r--r-- | usb/usb-composite-support-16-interfaces-by-default.patch | 31 | ||||
| -rw-r--r-- | usb/usb-ethernet-gadget-split-cdc-ethernet-function.patch | 946 | ||||
| -rw-r--r-- | usb/usb-ethernet-gadget-split-cdc-subset-function.patch | 471 | ||||
| -rw-r--r-- | usb/usb-ethernet-gadget-split-out-network-core.patch | 1103 | ||||
| -rw-r--r-- | usb/usb-ethernet-gadget-split-rndis-function.patch | 906 | ||||
| -rw-r--r-- | usb/usb-gadget-composite-gadget-core.patch (renamed from usb/usb-gadget-composite-gadget-framework.patch) | 27 | ||||
| -rw-r--r-- | usb/usb-gadget-descriptor-copying-support.patch (renamed from usb/usb-gadget-support-descriptor-copying.patch) | 34 | ||||
| -rw-r--r-- | usb/usb-gadget-new-cdc-composite-gadget-driver.patch | 344 | ||||
| -rw-r--r-- | usb/usb-gadget-rndis-cleanups.patch | 796 | ||||
| -rw-r--r-- | usb/usb-gadget-serial-split-out-cdc-acm-function.patch (renamed from usb/usb-serial-gadget-cdc-acm-function-driver.patch) | 40 | ||||
| -rw-r--r-- | usb/usb-gadget-serial-split-out-generic-serial-function.patch (renamed from usb/usb-serial-gadget-generic-serial-function-driver.patch) | 26 | ||||
| -rw-r--r-- | usb/usb-gadget-serial-use-composite-gadget-framework.patch (renamed from usb/usb-serial-gadget-use-updated-framework.patch) | 67 | ||||
| -rw-r--r-- | usb/usb-gadget-split-out-serial-core.patch (renamed from usb/usb-serial-gadget-modular-tty-glue.patch) | 88 | ||||
| -rw-r--r-- | usb/usb-gadget-use-new-serial-core.patch (renamed from usb/usb-serial-gadget-use-new-tty-glue.patch) | 12 | ||||
| -rw-r--r-- | usb/usb-gadget-zero-split-out-loopback-config.patch (renamed from usb/usb-gadget-zero-loopback-function-driver.patch) | 24 | ||||
| -rw-r--r-- | usb/usb-gadget-zero-split-out-source-sink-config.patch (renamed from usb/usb-gadget-zero-sourcesink-config-driver.patch) | 29 | ||||
| -rw-r--r-- | usb/usb-gadget-zero-use-composite-gadget-framework.patch (renamed from usb/usb-gadget-zero-use-updated-framework.patch) | 29 |
17 files changed, 4761 insertions, 212 deletions
diff --git a/usb/usb-composite-support-16-interfaces-by-default.patch b/usb/usb-composite-support-16-interfaces-by-default.patch deleted file mode 100644 index 30070e710c8891..00000000000000 --- a/usb/usb-composite-support-16-interfaces-by-default.patch +++ /dev/null @@ -1,31 +0,0 @@ -From felipe.balbi@nokia.com Fri Jun 6 15:12:15 2008 -From: Felipe Balbi <felipe.balbi@nokia.com> -Date: Thu, 22 May 2008 02:45:11 +0300 -Subject: USB: COMPOSITE: Support 16 interfaces by default -To: linux-usb@vger.kernel.org -Cc: David Brownell <dbrownell@sourceforge.users.net>, Felipe Balbi <felipe.balbi@nokia.com> -Message-ID: <1211413513-1320-2-git-send-email-felipe.balbi@nokia.com> - - -8 interfaces is not enough if we try to use -3 instances of obex function driver. - -Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> -Acked-by: David Brownell <dbrownell@users.sourceforge.net> -Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> - ---- - include/linux/usb/composite.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/include/linux/usb/composite.h -+++ b/include/linux/usb/composite.h -@@ -147,7 +147,7 @@ ep_choose(struct usb_gadget *g, struct u - return fs; - } - --#define MAX_CONFIG_INTERFACES 8 /* arbitrary; max 255 */ -+#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */ - - /** - * struct usb_configuration - represents one gadget configuration diff --git a/usb/usb-ethernet-gadget-split-cdc-ethernet-function.patch b/usb/usb-ethernet-gadget-split-cdc-ethernet-function.patch new file mode 100644 index 00000000000000..53651beee21b6d --- /dev/null +++ b/usb/usb-ethernet-gadget-split-cdc-ethernet-function.patch @@ -0,0 +1,946 @@ +From david-b@pacbell.net Mon Jun 30 11:13:17 2008 +From: David Brownell <david-b@pacbell.net> +Date: Thu, 19 Jun 2008 18:19:46 -0700 +Subject: usb ethernet gadget: split CDC Ethernet function +To: linux-usb@vger.kernel.org +Cc: Greg KH <greg@kroah.com> +Message-ID: <200806191819.46955.david-b@pacbell.net> +Content-Disposition: inline + + +From: David Brownell <dbrownell@users.sourceforge.net> + +This is a "CDC Ethernet" (ECM) function driver, extracted from the +all-in-one Ethernet gadget driver. + +This is a good example of how to implement interface altsettings. +In fact it's currently the only such example in the gadget stack, +pending addition of OBEX support. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_ecm.c | 833 ++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/gadget_chips.h | 23 + + drivers/usb/gadget/u_ether.h | 24 + + 4 files changed, 881 insertions(+), 1 deletion(-) + +--- /dev/null ++++ b/drivers/usb/gadget/f_ecm.c +@@ -0,0 +1,833 @@ ++/* ++ * f_ecm.c -- USB CDC Ethernet (ECM) link function driver ++ * ++ * Copyright (C) 2003-2005,2008 David Brownell ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/etherdevice.h> ++ ++#include "u_ether.h" ++ ++ ++/* ++ * 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. ++ */ ++ ++struct ecm_ep_descs { ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++ struct usb_endpoint_descriptor *notify; ++}; ++ ++enum ecm_notify_state { ++ ECM_NOTIFY_NONE, /* don't notify */ ++ ECM_NOTIFY_CONNECT, /* issue CONNECT next */ ++ ECM_NOTIFY_SPEED, /* issue SPEED_CHANGE next */ ++}; ++ ++struct f_ecm { ++ struct gether port; ++ u8 ctrl_id, data_id; ++ ++ char ethaddr[14]; ++ ++ struct usb_descriptor_header **fs_function; ++ struct ecm_ep_descs fs; ++ struct usb_descriptor_header **hs_function; ++ struct ecm_ep_descs hs; ++ ++ struct usb_ep *notify; ++ struct usb_endpoint_descriptor *notify_desc; ++ struct usb_request *notify_req; ++ u8 notify_state; ++ bool is_open; ++ ++ /* FIXME is_open needs some irq-ish locking ++ * ... possibly the same as port.ioport ++ */ ++}; ++ ++static inline struct f_ecm *func_to_ecm(struct usb_function *f) ++{ ++ return container_of(f, struct f_ecm, port.func); ++} ++ ++/* peak (theoretical) bulk transfer rate in bits-per-second */ ++static inline unsigned bitrate(struct usb_gadget *g) ++{ ++ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) ++ return 13 * 512 * 8 * 1000 * 8; ++ else ++ return 19 * 64 * 1 * 1000 * 8; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * 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). ++ */ ++ ++#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ ++#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ ++ ++ ++/* interface descriptor: */ ++ ++static struct usb_interface_descriptor ecm_control_intf __initdata = { ++ .bLength = sizeof ecm_control_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ /* .bInterfaceNumber = DYNAMIC */ ++ /* status endpoint is optional; this could be patched later */ ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_COMM, ++ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, ++ .bInterfaceProtocol = USB_CDC_PROTO_NONE, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++static struct usb_cdc_header_desc header_desc __initdata = { ++ .bLength = sizeof header_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_HEADER_TYPE, ++ ++ .bcdCDC = __constant_cpu_to_le16(0x0110), ++}; ++ ++static struct usb_cdc_union_desc ecm_union_desc __initdata = { ++ .bLength = sizeof(ecm_union_desc), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_UNION_TYPE, ++ /* .bMasterInterface0 = DYNAMIC */ ++ /* .bSlaveInterface0 = DYNAMIC */ ++}; ++ ++static struct usb_cdc_ether_desc ether_desc __initdata = { ++ .bLength = sizeof ether_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, ++ ++ /* this descriptor actually adds value, surprise! */ ++ /* .iMACAddress = DYNAMIC */ ++ .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */ ++ .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN), ++ .wNumberMCFilters = __constant_cpu_to_le16(0), ++ .bNumberPowerFilters = 0, ++}; ++ ++/* the default data interface has no endpoints ... */ ++ ++static struct usb_interface_descriptor ecm_data_nop_intf __initdata = { ++ .bLength = sizeof ecm_data_nop_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ .bInterfaceNumber = 1, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_CDC_DATA, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* ... but the "real" data interface has two bulk endpoints */ ++ ++static struct usb_interface_descriptor ecm_data_intf __initdata = { ++ .bLength = sizeof ecm_data_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ .bInterfaceNumber = 1, ++ .bAlternateSetting = 1, ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_CDC_DATA, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor fs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), ++ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, ++}; ++ ++static struct usb_endpoint_descriptor fs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor fs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *eth_fs_function[] __initdata = { ++ /* CDC ECM control descriptors */ ++ (struct usb_descriptor_header *) &ecm_control_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &ecm_union_desc, ++ (struct usb_descriptor_header *) ðer_desc, ++ /* NOTE: status endpoint might need to be removed */ ++ (struct usb_descriptor_header *) &fs_notify_desc, ++ /* data interface, altsettings 0 and 1 */ ++ (struct usb_descriptor_header *) &ecm_data_nop_intf, ++ (struct usb_descriptor_header *) &ecm_data_intf, ++ (struct usb_descriptor_header *) &fs_in_desc, ++ (struct usb_descriptor_header *) &fs_out_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor hs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), ++ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, ++}; ++static struct usb_endpoint_descriptor hs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor hs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *eth_hs_function[] __initdata = { ++ /* CDC ECM control descriptors */ ++ (struct usb_descriptor_header *) &ecm_control_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &ecm_union_desc, ++ (struct usb_descriptor_header *) ðer_desc, ++ /* NOTE: status endpoint might need to be removed */ ++ (struct usb_descriptor_header *) &hs_notify_desc, ++ /* data interface, altsettings 0 and 1 */ ++ (struct usb_descriptor_header *) &ecm_data_nop_intf, ++ (struct usb_descriptor_header *) &ecm_data_intf, ++ (struct usb_descriptor_header *) &hs_in_desc, ++ (struct usb_descriptor_header *) &hs_out_desc, ++ NULL, ++}; ++ ++/* string descriptors: */ ++ ++static struct usb_string ecm_string_defs[] = { ++ [0].s = "CDC Ethernet Control Model (ECM)", ++ [1].s = NULL /* DYNAMIC */, ++ [2].s = "CDC Ethernet Data", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings ecm_string_table = { ++ .language = 0x0409, /* en-us */ ++ .strings = ecm_string_defs, ++}; ++ ++static struct usb_gadget_strings *ecm_strings[] = { ++ &ecm_string_table, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void ecm_do_notify(struct f_ecm *ecm) ++{ ++ struct usb_request *req = ecm->notify_req; ++ struct usb_cdc_notification *event; ++ struct usb_composite_dev *cdev = ecm->port.func.config->cdev; ++ __le32 *data; ++ int status; ++ ++ /* notification already in flight? */ ++ if (!req) ++ return; ++ ++ event = req->buf; ++ switch (ecm->notify_state) { ++ case ECM_NOTIFY_NONE: ++ return; ++ ++ case ECM_NOTIFY_CONNECT: ++ event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; ++ if (ecm->is_open) ++ event->wValue = cpu_to_le16(1); ++ else ++ event->wValue = cpu_to_le16(0); ++ event->wLength = 0; ++ req->length = sizeof *event; ++ ++ DBG(cdev, "notify connect %s\n", ++ ecm->is_open ? "true" : "false"); ++ ecm->notify_state = ECM_NOTIFY_SPEED; ++ break; ++ ++ case ECM_NOTIFY_SPEED: ++ event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; ++ event->wValue = cpu_to_le16(0); ++ event->wLength = cpu_to_le16(8); ++ req->length = STATUS_BYTECOUNT; ++ ++ /* SPEED_CHANGE data is up/down speeds in bits/sec */ ++ data = req->buf + sizeof *event; ++ data[0] = cpu_to_le32(bitrate(cdev->gadget)); ++ data[1] = data[0]; ++ ++ DBG(cdev, "notify speed %d\n", bitrate(cdev->gadget)); ++ ecm->notify_state = ECM_NOTIFY_NONE; ++ break; ++ } ++ event->bmRequestType = 0xA1; ++ event->wIndex = cpu_to_le16(ecm->ctrl_id); ++ ++ ecm->notify_req = NULL; ++ status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC); ++ if (status < 0) { ++ ecm->notify_req = req; ++ DBG(cdev, "notify --> %d\n", status); ++ } ++} ++ ++static void 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); ++} ++ ++static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_ecm *ecm = req->context; ++ struct usb_composite_dev *cdev = ecm->port.func.config->cdev; ++ struct usb_cdc_notification *event = req->buf; ++ ++ switch (req->status) { ++ case 0: ++ /* no fault */ ++ break; ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ ecm->notify_state = ECM_NOTIFY_NONE; ++ break; ++ default: ++ DBG(cdev, "event %02x --> %d\n", ++ event->bNotificationType, req->status); ++ break; ++ } ++ ecm->notify_req = req; ++ ecm_do_notify(ecm); ++} ++ ++static int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) ++{ ++ struct f_ecm *ecm = func_to_ecm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct usb_request *req = cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ /* 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: ++ */ ++ ++ default: ++invalid: ++ DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(cdev, "ecm req %02x.%02x response err %d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ value); ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++ ++static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct f_ecm *ecm = func_to_ecm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* Control interface has only altsetting 0 */ ++ if (intf == ecm->ctrl_id) { ++ if (alt != 0) ++ goto fail; ++ ++ if (ecm->notify->driver_data) { ++ VDBG(cdev, "reset ecm control %d\n", intf); ++ usb_ep_disable(ecm->notify); ++ } else { ++ VDBG(cdev, "init ecm ctrl %d\n", intf); ++ ecm->notify_desc = ep_choose(cdev->gadget, ++ ecm->hs.notify, ++ ecm->fs.notify); ++ } ++ usb_ep_enable(ecm->notify, ecm->notify_desc); ++ ecm->notify->driver_data = ecm; ++ ++ /* Data interface has two altsettings, 0 and 1 */ ++ } else if (intf == ecm->data_id) { ++ if (alt > 1) ++ goto fail; ++ ++ if (ecm->port.in_ep->driver_data) { ++ DBG(cdev, "reset ecm\n"); ++ gether_disconnect(&ecm->port); ++ } ++ ++ if (!ecm->port.in) { ++ DBG(cdev, "init ecm\n"); ++ ecm->port.in = ep_choose(cdev->gadget, ++ ecm->hs.in, ecm->fs.in); ++ ecm->port.out = ep_choose(cdev->gadget, ++ ecm->hs.out, ecm->fs.out); ++ } ++ ++ /* 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) ++ * and sa1100 (can't). ++ */ ++ ecm->port.is_zlp_ok = !( ++ gadget_is_sa1100(cdev->gadget) ++ || gadget_is_musbhdrc(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. ++ */ ++static int ecm_get_alt(struct usb_function *f, unsigned intf) ++{ ++ struct f_ecm *ecm = func_to_ecm(f); ++ ++ if (intf == ecm->ctrl_id) ++ return 0; ++ return ecm->port.in_ep->driver_data ? 1 : 0; ++} ++ ++static void ecm_disable(struct usb_function *f) ++{ ++ struct f_ecm *ecm = func_to_ecm(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ DBG(cdev, "ecm deactivated\n"); ++ ++ if (ecm->port.in_ep->driver_data) ++ gether_disconnect(&ecm->port); ++ ++ if (ecm->notify->driver_data) { ++ usb_ep_disable(ecm->notify); ++ ecm->notify->driver_data = NULL; ++ ecm->notify_desc = NULL; ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * 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. ++ */ ++ ++static void ecm_open(struct gether *geth) ++{ ++ struct f_ecm *ecm = func_to_ecm(&geth->func); ++ ++ DBG(ecm->port.func.config->cdev, "%s\n", __func__); ++ ++ ecm->is_open = true; ++ ecm_notify(ecm); ++} ++ ++static void ecm_close(struct gether *geth) ++{ ++ struct f_ecm *ecm = func_to_ecm(&geth->func); ++ ++ DBG(ecm->port.func.config->cdev, "%s\n", __func__); ++ ++ ecm->is_open = false; ++ ecm_notify(ecm); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ethernet function driver setup/binding */ ++ ++static int __init ++ecm_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_ecm *ecm = func_to_ecm(f); ++ int status; ++ struct usb_ep *ep; ++ ++ /* allocate instance-specific interface IDs */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ ecm->ctrl_id = status; ++ ++ ecm_control_intf.bInterfaceNumber = status; ++ ecm_union_desc.bMasterInterface0 = status; ++ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ ecm->data_id = status; ++ ++ ecm_data_nop_intf.bInterfaceNumber = status; ++ ecm_data_intf.bInterfaceNumber = status; ++ ecm_union_desc.bSlaveInterface0 = status; ++ ++ status = -ENODEV; ++ ++ /* allocate instance-specific endpoints */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); ++ if (!ep) ++ goto fail; ++ ecm->port.in_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); ++ if (!ep) ++ goto fail; ++ ecm->port.out_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ /* 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_notify_desc); ++ if (!ep) ++ goto fail; ++ ecm->notify = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ status = -ENOMEM; ++ ++ /* allocate notification request and buffer */ ++ ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!ecm->notify_req) ++ goto fail; ++ ecm->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); ++ if (!ecm->notify_req->buf) ++ goto fail; ++ ecm->notify_req->context = ecm; ++ ecm->notify_req->complete = ecm_notify_complete; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->descriptors = usb_copy_descriptors(eth_fs_function); ++ if (!f->descriptors) ++ goto fail; ++ ++ ecm->fs.in = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_in_desc); ++ ecm->fs.out = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_out_desc); ++ ecm->fs.notify = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_notify_desc); ++ ++ /* support all relevant hardware speeds... we expect that when ++ * hardware is dual speed, all bulk-capable endpoints work at ++ * both speeds ++ */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ hs_in_desc.bEndpointAddress = ++ fs_in_desc.bEndpointAddress; ++ hs_out_desc.bEndpointAddress = ++ fs_out_desc.bEndpointAddress; ++ hs_notify_desc.bEndpointAddress = ++ fs_notify_desc.bEndpointAddress; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->hs_descriptors = usb_copy_descriptors(eth_hs_function); ++ if (!f->hs_descriptors) ++ goto fail; ++ ++ ecm->hs.in = usb_find_endpoint(eth_hs_function, ++ f->hs_descriptors, &hs_in_desc); ++ ecm->hs.out = usb_find_endpoint(eth_hs_function, ++ f->hs_descriptors, &hs_out_desc); ++ ecm->hs.notify = usb_find_endpoint(eth_hs_function, ++ f->hs_descriptors, &hs_notify_desc); ++ } ++ ++ /* 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(). ++ */ ++ ++ ecm->port.open = ecm_open; ++ ecm->port.close = ecm_close; ++ ++ DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ ecm->port.in_ep->name, ecm->port.out_ep->name, ++ ecm->notify->name); ++ return 0; ++ ++fail: ++ if (f->descriptors) ++ usb_free_descriptors(f->descriptors); ++ ++ if (ecm->notify_req) { ++ kfree(ecm->notify_req->buf); ++ usb_ep_free_request(ecm->notify, ecm->notify_req); ++ } ++ ++ /* we might as well release our claims on endpoints */ ++ if (ecm->notify) ++ ecm->notify->driver_data = NULL; ++ if (ecm->port.out) ++ ecm->port.out_ep->driver_data = NULL; ++ if (ecm->port.in) ++ ecm->port.in_ep->driver_data = NULL; ++ ++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); ++ ++ return status; ++} ++ ++static void ++ecm_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct f_ecm *ecm = func_to_ecm(f); ++ ++ DBG(c->cdev, "ecm unbind\n"); ++ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ usb_free_descriptors(f->hs_descriptors); ++ usb_free_descriptors(f->descriptors); ++ ++ kfree(ecm->notify_req->buf); ++ usb_ep_free_request(ecm->notify, ecm->notify_req); ++ ++ ecm_string_defs[1].s = NULL; ++ kfree(ecm); ++} ++ ++/** ++ * ecm_bind_config - add CDC Ethernet network link to a configuration ++ * @c: the configuration to support the network link ++ * @ethaddr: a buffer in which the ethernet address of the host side ++ * side of the link was recorded ++ * Context: single threaded during gadget setup ++ * ++ * Returns zero on success, else negative errno. ++ * ++ * Caller must have called @gether_setup(). Caller is also responsible ++ * for calling @gether_cleanup() before module unload. ++ */ ++int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) ++{ ++ struct f_ecm *ecm; ++ int status; ++ ++ if (!can_support_ecm(c->cdev->gadget) || !ethaddr) ++ return -EINVAL; ++ ++ /* maybe allocate device-global string IDs */ ++ if (ecm_string_defs[0].id == 0) { ++ ++ /* control interface label */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ ecm_string_defs[0].id = status; ++ ecm_control_intf.iInterface = status; ++ ++ /* data interface label */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ ecm_string_defs[2].id = status; ++ ecm_data_intf.iInterface = status; ++ ++ /* MAC address */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ ecm_string_defs[1].id = status; ++ ether_desc.iMACAddress = status; ++ } ++ ++ /* allocate and initialize one new instance */ ++ ecm = kzalloc(sizeof *ecm, GFP_KERNEL); ++ if (!ecm) ++ return -ENOMEM; ++ ++ /* export host's Ethernet address in CDC format */ ++ snprintf(ecm->ethaddr, sizeof ecm->ethaddr, ++ "%02X%02X%02X%02X%02X%02X", ++ ethaddr[0], ethaddr[1], ethaddr[2], ++ ethaddr[3], ethaddr[4], ethaddr[5]); ++ ecm_string_defs[1].s = ecm->ethaddr; ++ ++ ecm->port.cdc_filter = DEFAULT_FILTER; ++ ++ ecm->port.func.name = "cdc_ethernet"; ++ ecm->port.func.strings = ecm_strings; ++ /* descriptors are per-instance copies */ ++ ecm->port.func.bind = ecm_bind; ++ ecm->port.func.unbind = ecm_unbind; ++ ecm->port.func.set_alt = ecm_set_alt; ++ ecm->port.func.get_alt = ecm_get_alt; ++ ecm->port.func.setup = ecm_setup; ++ ecm->port.func.disable = ecm_disable; ++ ++ status = usb_add_function(c, &ecm->port.func); ++ if (status) { ++ ecm_string_defs[1].s = NULL; ++ kfree(ecm); ++ } ++ return status; ++} +--- a/drivers/usb/gadget/gadget_chips.h ++++ b/drivers/usb/gadget/gadget_chips.h +@@ -214,3 +214,26 @@ static inline int usb_gadget_controller_ + return 0x21; + return -ENOENT; + } ++ ++ ++/** ++ * gadget_supports_altsettings - return true if altsettings work ++ * @gadget: the gadget in question ++ */ ++static inline bool gadget_supports_altsettings(struct usb_gadget *gadget) ++{ ++ /* PXA 21x/25x/26x has no altsettings at all */ ++ if (gadget_is_pxa(gadget)) ++ return false; ++ ++ /* PXA 27x and 3xx have *broken* altsetting support */ ++ if (gadget_is_pxa27x(gadget)) ++ return false; ++ ++ /* SH3 hardware just doesn't do altsettings */ ++ if (gadget_is_sh(gadget)) ++ return false; ++ ++ /* Everything else is *presumably* fine ... */ ++ return true; ++} +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -25,7 +25,7 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o + C_UTILS = composite.o usbstring.o config.o epautoconf.o + + g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS) +-g_ether-objs := ether.o u_ether.o f_subset.o $(C_UTILS) ++g_ether-objs := ether.o u_ether.o f_subset.o f_ecm.o $(C_UTILS) + g_serial-objs := serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS) + g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o + gadgetfs-objs := inode.o +--- a/drivers/usb/gadget/u_ether.h ++++ b/drivers/usb/gadget/u_ether.h +@@ -28,6 +28,9 @@ + #include <linux/usb/composite.h> + #include <linux/usb/cdc.h> + ++#include "gadget_chips.h" ++ ++ + /* + * This represents the USB side of an "ethernet" link, managed by a USB + * function which provides control and (maybe) framing. Two functions +@@ -80,7 +83,28 @@ void gether_cleanup(void); + struct net_device *gether_connect(struct gether *); + void gether_disconnect(struct gether *); + ++/* Some controllers can't support CDC Ethernet (ECM) ... */ ++static inline bool can_support_ecm(struct usb_gadget *gadget) ++{ ++ if (!gadget_supports_altsettings(gadget)) ++ return false; ++ ++ /* SA1100 can do ECM, *without* status endpoint ... but we'll ++ * only use it in non-ECM mode for backwards compatibility ++ * (and since we currently require a status endpoint) ++ */ ++ if (gadget_is_sa1100(gadget)) ++ return false; ++ ++ /* Everything else is *presumably* fine ... but this is a bit ++ * chancy, so be **CERTAIN** there are no hardware issues with ++ * your controller. Add it above if it can't handle CDC. ++ */ ++ return true; ++} ++ + /* each configuration may bind one instance of an ethernet link */ + int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); ++int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); + + #endif /* __U_ETHER_H */ diff --git a/usb/usb-ethernet-gadget-split-cdc-subset-function.patch b/usb/usb-ethernet-gadget-split-cdc-subset-function.patch new file mode 100644 index 00000000000000..1c31ca10a3e22e --- /dev/null +++ b/usb/usb-ethernet-gadget-split-cdc-subset-function.patch @@ -0,0 +1,471 @@ +From david-b@pacbell.net Mon Jun 30 11:11:58 2008 +From: David Brownell <david-b@pacbell.net> +Date: Thu, 19 Jun 2008 18:19:32 -0700 +Subject: usb ethernet gadget: split CDC Subset function +To: linux-usb@vger.kernel.org +Cc: Greg KH <greg@kroah.com> +Message-ID: <200806191819.32934.david-b@pacbell.net> +Content-Disposition: inline + + +From: David Brownell <dbrownell@users.sourceforge.net> + +This is a simple "CDC Subset" (and MCCI "SAFE") function driver, extracted +from the all-in-one Ethernet gadget driver. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_subset.c | 423 ++++++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/u_ether.h | 3 + 3 files changed, 427 insertions(+), 1 deletion(-) + +--- /dev/null ++++ b/drivers/usb/gadget/f_subset.c +@@ -0,0 +1,423 @@ ++/* ++ * f_subset.c -- "CDC Subset" Ethernet link function driver ++ * ++ * Copyright (C) 2003-2005,2008 David Brownell ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/etherdevice.h> ++ ++#include "u_ether.h" ++ ++ ++/* ++ * This function packages a simple "CDC Subset" Ethernet port with no real ++ * control mechanisms; just raw data transfer over two bulk endpoints. ++ * The data transfer model is exactly that of CDC Ethernet, which is ++ * why we call it the "CDC Subset". ++ * ++ * Because it's not standardized, this has some interoperability issues. ++ * They mostly relate to driver binding, since the data transfer model is ++ * so simple (CDC Ethernet). The original versions of this protocol used ++ * specific product/vendor IDs: byteswapped IDs for Digital Equipment's ++ * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported ++ * daughtercards with USB peripheral connectors. (It was used more often ++ * with other boards, using the Itsy identifiers.) Linux hosts recognized ++ * this with CONFIG_USB_ARMLINUX; these devices have only one configuration ++ * and one interface. ++ * ++ * At some point, MCCI defined a (nonconformant) CDC MDLM variant called ++ * "SAFE", which happens to have a mode which is identical to the "CDC ++ * Subset" in terms of data transfer and lack of control model. This was ++ * adopted by later Sharp Zaurus models, and by some other software which ++ * Linux hosts recognize with CONFIG_USB_NET_ZAURUS. ++ * ++ * Because Microsoft's RNDIS drivers are far from robust, we added a few ++ * descriptors to the CDC Subset code, making this code look like a SAFE ++ * implementation. This lets you use MCCI's host side MS-Windows drivers ++ * if you get fed up with RNDIS. It also makes it easier for composite ++ * drivers to work, since they can use class based binding instead of ++ * caring about specific product and vendor IDs. ++ */ ++ ++struct geth_descs { ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++}; ++ ++struct f_gether { ++ struct gether port; ++ ++ char ethaddr[14]; ++ ++ struct usb_descriptor_header **fs_function; ++ struct geth_descs fs; ++ struct usb_descriptor_header **hs_function; ++ struct geth_descs hs; ++}; ++ ++static inline struct f_gether *func_to_geth(struct usb_function *f) ++{ ++ return container_of(f, struct f_gether, port.func); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * "Simple" CDC-subset option is a simple vendor-neutral model that most ++ * full speed controllers can handle: one interface, two bulk endpoints. ++ * To assist host side drivers, we fancy it up a bit, and add descriptors so ++ * some host side drivers will understand it as a "SAFE" variant. ++ * ++ * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways. ++ * Data endpoints live in the control interface, there's no data interface. ++ * And it's not used to talk to a cell phone radio. ++ */ ++ ++/* interface descriptor: */ ++ ++static struct usb_interface_descriptor subset_data_intf __initdata = { ++ .bLength = sizeof subset_data_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ /* .bInterfaceNumber = DYNAMIC */ ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_COMM, ++ .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++static struct usb_cdc_header_desc header_desc __initdata = { ++ .bLength = sizeof header_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_HEADER_TYPE, ++ ++ .bcdCDC = __constant_cpu_to_le16(0x0110), ++}; ++ ++static struct usb_cdc_mdlm_desc mdlm_desc __initdata = { ++ .bLength = sizeof mdlm_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_MDLM_TYPE, ++ ++ .bcdVersion = __constant_cpu_to_le16(0x0100), ++ .bGUID = { ++ 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, ++ 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, ++ }, ++}; ++ ++/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we ++ * can't really use its struct. All we do here is say that we're using ++ * the submode of "SAFE" which directly matches the CDC Subset. ++ */ ++static u8 mdlm_detail_desc[] __initdata = { ++ 6, ++ USB_DT_CS_INTERFACE, ++ USB_CDC_MDLM_DETAIL_TYPE, ++ ++ 0, /* "SAFE" */ ++ 0, /* network control capabilities (none) */ ++ 0, /* network data capabilities ("raw" encapsulation) */ ++}; ++ ++static struct usb_cdc_ether_desc ether_desc __initdata = { ++ .bLength = sizeof ether_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, ++ ++ /* this descriptor actually adds value, surprise! */ ++ /* .iMACAddress = DYNAMIC */ ++ .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */ ++ .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN), ++ .wNumberMCFilters = __constant_cpu_to_le16(0), ++ .bNumberPowerFilters = 0, ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor fs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor fs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *fs_eth_function[] __initdata = { ++ (struct usb_descriptor_header *) &subset_data_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &mdlm_desc, ++ (struct usb_descriptor_header *) &mdlm_detail_desc, ++ (struct usb_descriptor_header *) ðer_desc, ++ (struct usb_descriptor_header *) &fs_in_desc, ++ (struct usb_descriptor_header *) &fs_out_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor hs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor hs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *hs_eth_function[] __initdata = { ++ (struct usb_descriptor_header *) &subset_data_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &mdlm_desc, ++ (struct usb_descriptor_header *) &mdlm_detail_desc, ++ (struct usb_descriptor_header *) ðer_desc, ++ (struct usb_descriptor_header *) &hs_in_desc, ++ (struct usb_descriptor_header *) &hs_out_desc, ++ NULL, ++}; ++ ++/* string descriptors: */ ++ ++static struct usb_string geth_string_defs[] = { ++ [0].s = "CDC Ethernet Subset/SAFE", ++ [1].s = NULL /* DYNAMIC */, ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings geth_string_table = { ++ .language = 0x0409, /* en-us */ ++ .strings = geth_string_defs, ++}; ++ ++static struct usb_gadget_strings *geth_strings[] = { ++ &geth_string_table, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct f_gether *geth = func_to_geth(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct net_device *net; ++ ++ /* we know alt == 0, so this is an activation or a reset */ ++ ++ if (geth->port.in_ep->driver_data) { ++ DBG(cdev, "reset cdc subset\n"); ++ gether_disconnect(&geth->port); ++ } ++ ++ DBG(cdev, "init + activate cdc subset\n"); ++ geth->port.in = ep_choose(cdev->gadget, ++ geth->hs.in, geth->fs.in); ++ geth->port.out = ep_choose(cdev->gadget, ++ geth->hs.out, geth->fs.out); ++ ++ net = gether_connect(&geth->port); ++ return IS_ERR(net) ? PTR_ERR(net) : 0; ++} ++ ++static void geth_disable(struct usb_function *f) ++{ ++ struct f_gether *geth = func_to_geth(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ DBG(cdev, "net deactivated\n"); ++ gether_disconnect(&geth->port); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* serial function driver setup/binding */ ++ ++static int __init ++geth_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_gether *geth = func_to_geth(f); ++ int status; ++ struct usb_ep *ep; ++ ++ /* allocate instance-specific interface IDs */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ subset_data_intf.bInterfaceNumber = status; ++ ++ status = -ENODEV; ++ ++ /* allocate instance-specific endpoints */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); ++ if (!ep) ++ goto fail; ++ geth->port.in_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); ++ if (!ep) ++ goto fail; ++ geth->port.out_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->descriptors = usb_copy_descriptors(fs_eth_function); ++ ++ geth->fs.in = usb_find_endpoint(fs_eth_function, ++ f->descriptors, &fs_in_desc); ++ geth->fs.out = usb_find_endpoint(fs_eth_function, ++ f->descriptors, &fs_out_desc); ++ ++ ++ /* support all relevant hardware speeds... we expect that when ++ * hardware is dual speed, all bulk-capable endpoints work at ++ * both speeds ++ */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ hs_in_desc.bEndpointAddress = ++ fs_in_desc.bEndpointAddress; ++ hs_out_desc.bEndpointAddress = ++ fs_out_desc.bEndpointAddress; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->hs_descriptors = usb_copy_descriptors(hs_eth_function); ++ ++ geth->hs.in = usb_find_endpoint(hs_eth_function, ++ f->hs_descriptors, &hs_in_desc); ++ geth->hs.out = usb_find_endpoint(hs_eth_function, ++ f->hs_descriptors, &hs_out_desc); ++ } ++ ++ /* 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(). ++ */ ++ ++ DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ geth->port.in_ep->name, geth->port.out_ep->name); ++ return 0; ++ ++fail: ++ /* we might as well release our claims on endpoints */ ++ if (geth->port.out) ++ geth->port.out_ep->driver_data = NULL; ++ if (geth->port.in) ++ geth->port.in_ep->driver_data = NULL; ++ ++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); ++ ++ return status; ++} ++ ++static void ++geth_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ usb_free_descriptors(f->hs_descriptors); ++ usb_free_descriptors(f->descriptors); ++ geth_string_defs[1].s = NULL; ++ kfree(func_to_geth(f)); ++} ++ ++/** ++ * geth_bind_config - add CDC Subset network link to a configuration ++ * @c: the configuration to support the network link ++ * @ethaddr: a buffer in which the ethernet address of the host side ++ * side of the link was recorded ++ * Context: single threaded during gadget setup ++ * ++ * Returns zero on success, else negative errno. ++ * ++ * Caller must have called @gether_setup(). Caller is also responsible ++ * for calling @gether_cleanup() before module unload. ++ */ ++int __init geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) ++{ ++ struct f_gether *geth; ++ int status; ++ ++ if (!ethaddr) ++ return -EINVAL; ++ ++ /* maybe allocate device-global string IDs */ ++ if (geth_string_defs[0].id == 0) { ++ ++ /* interface label */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ geth_string_defs[0].id = status; ++ subset_data_intf.iInterface = status; ++ ++ /* MAC address */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ geth_string_defs[1].id = status; ++ ether_desc.iMACAddress = status; ++ } ++ ++ /* allocate and initialize one new instance */ ++ geth = kzalloc(sizeof *geth, GFP_KERNEL); ++ if (!geth) ++ return -ENOMEM; ++ ++ /* export host's Ethernet address in CDC format */ ++ snprintf(geth->ethaddr, sizeof geth->ethaddr, ++ "%02X%02X%02X%02X%02X%02X", ++ ethaddr[0], ethaddr[1], ethaddr[2], ++ ethaddr[3], ethaddr[4], ethaddr[5]); ++ geth_string_defs[1].s = geth->ethaddr; ++ ++ geth->port.cdc_filter = DEFAULT_FILTER; ++ ++ geth->port.func.name = "cdc_subset"; ++ geth->port.func.strings = geth_strings; ++ geth->port.func.bind = geth_bind; ++ geth->port.func.unbind = geth_unbind; ++ geth->port.func.set_alt = geth_set_alt; ++ geth->port.func.disable = geth_disable; ++ ++ status = usb_add_function(c, &geth->port.func); ++ if (status) { ++ geth_string_defs[1].s = NULL; ++ kfree(geth); ++ } ++ return status; ++} +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -25,7 +25,7 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o + C_UTILS = composite.o usbstring.o config.o epautoconf.o + + g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS) +-g_ether-objs := ether.o u_ether.o $(C_UTILS) ++g_ether-objs := ether.o u_ether.o f_subset.o $(C_UTILS) + g_serial-objs := serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS) + g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o + gadgetfs-objs := inode.o +--- a/drivers/usb/gadget/u_ether.h ++++ b/drivers/usb/gadget/u_ether.h +@@ -80,4 +80,7 @@ void gether_cleanup(void); + struct net_device *gether_connect(struct gether *); + void gether_disconnect(struct gether *); + ++/* each configuration may bind one instance of an ethernet link */ ++int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); ++ + #endif /* __U_ETHER_H */ diff --git a/usb/usb-ethernet-gadget-split-out-network-core.patch b/usb/usb-ethernet-gadget-split-out-network-core.patch new file mode 100644 index 00000000000000..dfae705bd0ade9 --- /dev/null +++ b/usb/usb-ethernet-gadget-split-out-network-core.patch @@ -0,0 +1,1103 @@ +From david-b@pacbell.net Mon Jun 30 11:11:42 2008 +From: David Brownell <david-b@pacbell.net> +Date: Thu, 19 Jun 2008 18:19:28 -0700 +Subject: usb ethernet gadget: split out network core +To: linux-usb@vger.kernel.org +Cc: Greg KH <greg@kroah.com> +Message-ID: <200806191819.28672.david-b@pacbell.net> +Content-Disposition: inline + + +From: David Brownell <dbrownell@users.sourceforge.net> + +Abstract the peripheral side Ethernet-over-USB link layer code from +the all-in-one Ethernet gadget driver into a component that can be +called by various functions, so the various flavors can be split +apart and selectively reused. + +A notable difference from the approach taken with the serial link +layer code (beyond talking to NET not TTY) is that because of the +initialization requirements, this only supports one network link. +(And one set of Ethernet link addresses.) + +That is, each configuration may have only one instance of a network +function. This doesn't change behavior; the current code has that +same restriction. If you want multiple logical links, that can +easily be done using network layer tools. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/u_ether.c | 967 +++++++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/u_ether.h | 83 +++ + 3 files changed, 1051 insertions(+), 1 deletion(-) + +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -25,7 +25,7 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o + C_UTILS = composite.o usbstring.o config.o epautoconf.o + + g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS) +-g_ether-objs := ether.o usbstring.o config.o epautoconf.o ++g_ether-objs := ether.o u_ether.o $(C_UTILS) + g_serial-objs := serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS) + g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o + gadgetfs-objs := inode.o +--- /dev/null ++++ b/drivers/usb/gadget/u_ether.c +@@ -0,0 +1,967 @@ ++/* ++ * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack ++ * ++ * Copyright (C) 2003-2005,2008 David Brownell ++ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++#include <linux/device.h> ++#include <linux/ctype.h> ++#include <linux/etherdevice.h> ++#include <linux/ethtool.h> ++ ++#include "u_ether.h" ++ ++ ++/* ++ * This component encapsulates the Ethernet link glue needed to provide ++ * one (!) network link through the USB gadget stack, normally "usb0". ++ * ++ * The control and data models are handled by the function driver which ++ * connects to this code; such as CDC Ethernet, "CDC Subset", or RNDIS. ++ * That includes all descriptor and endpoint management. ++ * ++ * Link level addressing is handled by this component using module ++ * parameters; if no such parameters are provided, random link level ++ * addresses are used. Each end of the link uses one address. The ++ * host end address is exported in various ways, and is often recorded ++ * in configuration databases. ++ * ++ * The driver which assembles each configuration using such a link is ++ * responsible for ensuring that each configuration includes at most one ++ * instance of is network link. (The network layer provides ways for ++ * this single "physical" link to be used by multiple virtual links.) ++ */ ++ ++#define DRIVER_VERSION "29-May-2008" ++ ++struct eth_dev { ++ /* lock is held while accessing port_usb ++ * or updating its backlink port_usb->ioport ++ */ ++ spinlock_t lock; ++ struct gether *port_usb; ++ ++ struct net_device *net; ++ struct usb_gadget *gadget; ++ ++ spinlock_t req_lock; /* guard {rx,tx}_reqs */ ++ struct list_head tx_reqs, rx_reqs; ++ atomic_t tx_qlen; ++ ++ unsigned header_len; ++ struct sk_buff *(*wrap)(struct sk_buff *skb); ++ int (*unwrap)(struct sk_buff *skb); ++ ++ struct work_struct work; ++ ++ unsigned long todo; ++#define WORK_RX_MEMORY 0 ++ ++ bool zlp; ++ u8 host_mac[ETH_ALEN]; ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define RX_EXTRA 20 /* bytes guarding against rx overflows */ ++ ++#define DEFAULT_QLEN 2 /* double buffering by default */ ++ ++ ++#ifdef CONFIG_USB_GADGET_DUALSPEED ++ ++static unsigned qmult = 5; ++module_param(qmult, uint, S_IRUGO|S_IWUSR); ++MODULE_PARM_DESC(qmult, "queue length multiplier at high speed"); ++ ++#else /* full speed (low speed doesn't do bulk) */ ++#define qmult 1 ++#endif ++ ++/* for dual-speed hardware, use deeper queues at highspeed */ ++static inline int qlen(struct usb_gadget *gadget) ++{ ++ if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) ++ return qmult * DEFAULT_QLEN; ++ else ++ return DEFAULT_QLEN; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* REVISIT there must be a better way than having two sets ++ * of debug calls ... ++ */ ++ ++#undef DBG ++#undef VDBG ++#undef ERROR ++#undef WARN ++#undef INFO ++ ++#define xprintk(d, level, fmt, args...) \ ++ printk(level "%s: " fmt , (d)->net->name , ## args) ++ ++#ifdef DEBUG ++#undef DEBUG ++#define DBG(dev, fmt, args...) \ ++ xprintk(dev , KERN_DEBUG , fmt , ## args) ++#else ++#define DBG(dev, fmt, args...) \ ++ do { } while (0) ++#endif /* DEBUG */ ++ ++#ifdef VERBOSE_DEBUG ++#define VDBG DBG ++#else ++#define VDBG(dev, fmt, args...) \ ++ do { } while (0) ++#endif /* DEBUG */ ++ ++#define ERROR(dev, fmt, args...) \ ++ xprintk(dev , KERN_ERR , fmt , ## args) ++#define WARN(dev, fmt, args...) \ ++ xprintk(dev , KERN_WARNING , fmt , ## args) ++#define INFO(dev, fmt, args...) \ ++ xprintk(dev , KERN_INFO , fmt , ## args) ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* 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); ++ unsigned long flags; ++ int status = 0; ++ ++ /* don't change MTU on "live" link (peer won't know) */ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (dev->port_usb) ++ status = -EBUSY; ++ else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) ++ status = -ERANGE; ++ else ++ net->mtu = new_mtu; ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return status; ++} ++ ++static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) ++{ ++ struct eth_dev *dev = netdev_priv(net); ++ ++ strlcpy(p->driver, "g_ether", 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; ++} ++ ++/* REVISIT can also support: ++ * - WOL (by tracking suspends and issuing remote wakeup) ++ * - msglevel (implies updated messaging) ++ * - ... probably more ethtool ops ++ */ ++ ++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 ++ DBG(dev, "kevent %d scheduled\n", flag); ++} ++ ++static void rx_complete(struct usb_ep *ep, struct usb_request *req); ++ ++static int ++rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) ++{ ++ struct sk_buff *skb; ++ int retval = -ENOMEM; ++ size_t size = 0; ++ struct usb_ep *out; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (dev->port_usb) ++ out = dev->port_usb->out_ep; ++ else ++ out = NULL; ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ if (!out) ++ return -ENOTCONN; ++ ++ ++ /* 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 lost synch on their own (because ++ * new packets don't only start after a short RX). ++ */ ++ size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA; ++ size += dev->port_usb->header_len; ++ size += out->maxpacket - 1; ++ size -= size % out->maxpacket; ++ ++ skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags); ++ if (skb == NULL) { ++ DBG(dev, "no rx skb\n"); ++ goto enomem; ++ } ++ ++ /* Some platforms perform better when IP packets are aligned, ++ * but on at least one, checksumming fails otherwise. Note: ++ * RNDIS headers involve variable numbers of LE32 values. ++ */ ++ skb_reserve(skb, NET_IP_ALIGN); ++ ++ req->buf = skb->data; ++ req->length = size; ++ req->complete = rx_complete; ++ req->context = skb; ++ ++ retval = usb_ep_queue(out, req, gfp_flags); ++ if (retval == -ENOMEM) ++enomem: ++ defer_kevent(dev, WORK_RX_MEMORY); ++ if (retval) { ++ DBG(dev, "rx submit --> %d\n", retval); ++ if (skb) ++ dev_kfree_skb_any(skb); ++ spin_lock_irqsave(&dev->req_lock, flags); ++ list_add(&req->list, &dev->rx_reqs); ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++ } ++ 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); ++ if (dev->unwrap) ++ status = dev->unwrap(skb); ++ if (status < 0 ++ || ETH_HLEN > skb->len ++ || skb->len > ETH_FRAME_LEN) { ++ dev->net->stats.rx_errors++; ++ dev->net->stats.rx_length_errors++; ++ DBG(dev, "rx length %d\n", skb->len); ++ break; ++ } ++ ++ skb->protocol = eth_type_trans(skb, dev->net); ++ dev->net->stats.rx_packets++; ++ dev->net->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 */ ++ VDBG(dev, "rx shutdown, code %d\n", status); ++ goto quiesce; ++ ++ /* for hardware automagic (such as pxa) */ ++ case -ECONNABORTED: /* endpoint reset */ ++ DBG(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->net->stats.rx_over_errors++; ++ /* FALLTHROUGH */ ++ ++ default: ++ dev->net->stats.rx_errors++; ++ DBG(dev, "rx status %d\n", status); ++ break; ++ } ++ ++ if (skb) ++ dev_kfree_skb_any(skb); ++ if (!netif_running(dev->net)) { ++clean: ++ spin_lock(&dev->req_lock); ++ list_add(&req->list, &dev->rx_reqs); ++ spin_unlock(&dev->req_lock); ++ req = NULL; ++ } ++ if (req) ++ rx_submit(dev, req, GFP_ATOMIC); ++} ++ ++static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n) ++{ ++ 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_ATOMIC); ++ 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, struct gether *link, unsigned n) ++{ ++ int status; ++ ++ spin_lock(&dev->req_lock); ++ status = prealloc(&dev->tx_reqs, link->in_ep, n); ++ if (status < 0) ++ goto fail; ++ status = prealloc(&dev->rx_reqs, link->out_ep, n); ++ if (status < 0) ++ goto fail; ++ goto done; ++fail: ++ DBG(dev, "can't alloc requests\n"); ++done: ++ spin_unlock(&dev->req_lock); ++ return status; ++} ++ ++static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) ++{ ++ struct usb_request *req; ++ unsigned long flags; ++ ++ /* fill unused rxq slots with some skb */ ++ spin_lock_irqsave(&dev->req_lock, flags); ++ while (!list_empty(&dev->rx_reqs)) { ++ req = container_of(dev->rx_reqs.next, ++ struct usb_request, list); ++ list_del_init(&req->list); ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++ ++ if (rx_submit(dev, req, gfp_flags) < 0) { ++ defer_kevent(dev, WORK_RX_MEMORY); ++ return; ++ } ++ ++ spin_lock_irqsave(&dev->req_lock, flags); ++ } ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++} ++ ++static void eth_work(struct work_struct *work) ++{ ++ struct eth_dev *dev = container_of(work, struct eth_dev, work); ++ ++ if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) { ++ if (netif_running(dev->net)) ++ rx_fill(dev, GFP_KERNEL); ++ } ++ ++ if (dev->todo) ++ DBG(dev, "work done, flags = 0x%lx\n", dev->todo); ++} ++ ++static void tx_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct sk_buff *skb = req->context; ++ struct eth_dev *dev = ep->driver_data; ++ ++ switch (req->status) { ++ default: ++ dev->net->stats.tx_errors++; ++ VDBG(dev, "tx err %d\n", req->status); ++ /* FALLTHROUGH */ ++ case -ECONNRESET: /* unlink */ ++ case -ESHUTDOWN: /* disconnect etc */ ++ break; ++ case 0: ++ dev->net->stats.tx_bytes += skb->len; ++ } ++ dev->net->stats.tx_packets++; ++ ++ spin_lock(&dev->req_lock); ++ list_add(&req->list, &dev->tx_reqs); ++ spin_unlock(&dev->req_lock); ++ dev_kfree_skb_any(skb); ++ ++ atomic_dec(&dev->tx_qlen); ++ if (netif_carrier_ok(dev->net)) ++ netif_wake_queue(dev->net); ++} ++ ++static inline int is_promisc(u16 cdc_filter) ++{ ++ return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; ++} ++ ++static int eth_start_xmit(struct sk_buff *skb, struct net_device *net) ++{ ++ struct eth_dev *dev = netdev_priv(net); ++ int length = skb->len; ++ int retval; ++ struct usb_request *req = NULL; ++ unsigned long flags; ++ struct usb_ep *in; ++ u16 cdc_filter; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (dev->port_usb) { ++ in = dev->port_usb->in_ep; ++ cdc_filter = dev->port_usb->cdc_filter; ++ } else { ++ in = NULL; ++ cdc_filter = 0; ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ if (!in) { ++ dev_kfree_skb_any(skb); ++ return 0; ++ } ++ ++ /* apply outgoing CDC or RNDIS filters */ ++ if (!is_promisc(cdc_filter)) { ++ u8 *dest = skb->data; ++ ++ if (is_multicast_ether_addr(dest)) { ++ u16 type; ++ ++ /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host ++ * SET_ETHERNET_MULTICAST_FILTERS requests ++ */ ++ if (is_broadcast_ether_addr(dest)) ++ type = USB_CDC_PACKET_TYPE_BROADCAST; ++ else ++ type = USB_CDC_PACKET_TYPE_ALL_MULTICAST; ++ if (!(cdc_filter & type)) { ++ dev_kfree_skb_any(skb); ++ return 0; ++ } ++ } ++ /* ignores USB_CDC_PACKET_TYPE_DIRECTED */ ++ } ++ ++ spin_lock_irqsave(&dev->req_lock, flags); ++ /* ++ * this freelist can be empty if an interrupt triggered disconnect() ++ * and reconfigured the gadget (shutting down this queue) after the ++ * network stack decided to xmit but before we got the spinlock. ++ */ ++ if (list_empty(&dev->tx_reqs)) { ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++ return 1; ++ } ++ ++ req = container_of(dev->tx_reqs.next, struct usb_request, list); ++ list_del(&req->list); ++ ++ /* temporarily stop TX queue when the freelist empties */ ++ if (list_empty(&dev->tx_reqs)) ++ netif_stop_queue(net); ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++ ++ /* no buffer copies needed, unless the network stack did it ++ * or the hardware can't use skb buffers. ++ * or there's not enough space for extra headers we need ++ */ ++ if (dev->wrap) { ++ struct sk_buff *skb_new; ++ ++ skb_new = dev->wrap(skb); ++ if (!skb_new) ++ goto drop; ++ ++ dev_kfree_skb_any(skb); ++ skb = skb_new; ++ length = skb->len; ++ } ++ req->buf = skb->data; ++ req->context = skb; ++ req->complete = tx_complete; ++ ++ /* use zlp framing on tx for strict CDC-Ether conformance, ++ * though any robust network rx path ignores extra padding. ++ * and some hardware doesn't like to write zlps. ++ */ ++ req->zero = 1; ++ if (!dev->zlp && (length % in->maxpacket) == 0) ++ length++; ++ ++ req->length = length; ++ ++ /* throttle highspeed IRQ rate back slightly */ ++ if (gadget_is_dualspeed(dev->gadget)) ++ req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) ++ ? ((atomic_read(&dev->tx_qlen) % qmult) != 0) ++ : 0; ++ ++ retval = usb_ep_queue(in, req, GFP_ATOMIC); ++ switch (retval) { ++ default: ++ DBG(dev, "tx queue err %d\n", retval); ++ break; ++ case 0: ++ net->trans_start = jiffies; ++ atomic_inc(&dev->tx_qlen); ++ } ++ ++ if (retval) { ++drop: ++ dev->net->stats.tx_dropped++; ++ dev_kfree_skb_any(skb); ++ spin_lock_irqsave(&dev->req_lock, flags); ++ if (list_empty(&dev->tx_reqs)) ++ netif_start_queue(net); ++ list_add(&req->list, &dev->tx_reqs); ++ spin_unlock_irqrestore(&dev->req_lock, flags); ++ } ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) ++{ ++ DBG(dev, "%s\n", __func__); ++ ++ /* fill the rx queue */ ++ rx_fill(dev, gfp_flags); ++ ++ /* and open the tx floodgates */ ++ atomic_set(&dev->tx_qlen, 0); ++ netif_wake_queue(dev->net); ++} ++ ++static int eth_open(struct net_device *net) ++{ ++ struct eth_dev *dev = netdev_priv(net); ++ struct gether *link; ++ ++ DBG(dev, "%s\n", __func__); ++ if (netif_carrier_ok(dev->net)) ++ eth_start(dev, GFP_KERNEL); ++ ++ spin_lock_irq(&dev->lock); ++ link = dev->port_usb; ++ if (link && link->open) ++ link->open(link); ++ spin_unlock_irq(&dev->lock); ++ ++ return 0; ++} ++ ++static int eth_stop(struct net_device *net) ++{ ++ struct eth_dev *dev = netdev_priv(net); ++ unsigned long flags; ++ ++ VDBG(dev, "%s\n", __func__); ++ netif_stop_queue(net); ++ ++ DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", ++ dev->net->stats.rx_packets, dev->net->stats.tx_packets, ++ dev->net->stats.rx_errors, dev->net->stats.tx_errors ++ ); ++ ++ /* ensure there are no more active requests */ ++ spin_lock_irqsave(&dev->lock, flags); ++ if (dev->port_usb) { ++ struct gether *link = dev->port_usb; ++ ++ if (link->close) ++ link->close(link); ++ ++ /* NOTE: we have no abort-queue primitive we could use ++ * to cancel all pending I/O. Instead, we disable then ++ * reenable the endpoints ... this idiom may leave toggle ++ * wrong, but that's a self-correcting error. ++ * ++ * REVISIT: we *COULD* just let the transfers complete at ++ * their own pace; the network stack can handle old packets. ++ * For the moment we leave this here, since it works. ++ */ ++ usb_ep_disable(link->in_ep); ++ usb_ep_disable(link->out_ep); ++ if (netif_carrier_ok(net)) { ++ DBG(dev, "host still using in/out endpoints\n"); ++ usb_ep_enable(link->in_ep, link->in); ++ usb_ep_enable(link->out_ep, link->out); ++ } ++ } ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ ++static char *dev_addr; ++module_param(dev_addr, charp, S_IRUGO); ++MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); ++ ++/* this address is invisible to ifconfig */ ++static char *host_addr; ++module_param(host_addr, charp, S_IRUGO); ++MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); ++ ++ ++static u8 __init nibble(unsigned char c) ++{ ++ if (isdigit(c)) ++ return c - '0'; ++ c = toupper(c); ++ if (isxdigit(c)) ++ return 10 + c - 'A'; ++ return 0; ++} ++ ++static int __init get_ether_addr(const char *str, u8 *dev_addr) ++{ ++ if (str) { ++ unsigned i; ++ ++ for (i = 0; i < 6; i++) { ++ unsigned char num; ++ ++ if ((*str == '.') || (*str == ':')) ++ str++; ++ num = nibble(*str++) << 4; ++ num |= (nibble(*str++)); ++ dev_addr [i] = num; ++ } ++ if (is_valid_ether_addr(dev_addr)) ++ return 0; ++ } ++ random_ether_addr(dev_addr); ++ return 1; ++} ++ ++static struct eth_dev *the_dev; ++ ++ ++/** ++ * gether_setup - initialize one ethernet-over-usb link ++ * @g: gadget to associated with these links ++ * @ethaddr: NULL, or a buffer in which the ethernet address of the ++ * host side of the link is recorded ++ * Context: may sleep ++ * ++ * This sets up the single network link that may be exported by a ++ * gadget driver using this framework. The link layer addresses are ++ * set up using module parameters. ++ * ++ * Returns negative errno, or zero on success ++ */ ++int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) ++{ ++ struct eth_dev *dev; ++ struct net_device *net; ++ int status; ++ ++ if (the_dev) ++ return -EBUSY; ++ ++ net = alloc_etherdev(sizeof *dev); ++ if (!net) ++ return -ENOMEM; ++ ++ dev = netdev_priv(net); ++ spin_lock_init(&dev->lock); ++ spin_lock_init(&dev->req_lock); ++ INIT_WORK(&dev->work, eth_work); ++ INIT_LIST_HEAD(&dev->tx_reqs); ++ INIT_LIST_HEAD(&dev->rx_reqs); ++ ++ /* network device setup */ ++ dev->net = net; ++ strcpy(net->name, "usb%d"); ++ ++ if (get_ether_addr(dev_addr, net->dev_addr)) ++ dev_warn(&g->dev, ++ "using random %s ethernet address\n", "self"); ++ if (get_ether_addr(host_addr, dev->host_mac)) ++ dev_warn(&g->dev, ++ "using random %s ethernet address\n", "host"); ++ ++ if (ethaddr) ++ memcpy(ethaddr, dev->host_mac, ETH_ALEN); ++ ++ net->change_mtu = eth_change_mtu; ++ net->hard_start_xmit = eth_start_xmit; ++ net->open = eth_open; ++ net->stop = eth_stop; ++ /* watchdog_timeo, tx_timeout ... */ ++ /* set_multicast_list */ ++ SET_ETHTOOL_OPS(net, &ops); ++ ++ /* two kinds of host-initiated state changes: ++ * - iff DATA transfer is active, carrier is "on" ++ * - tx queueing enabled if open *and* carrier is "on" ++ */ ++ netif_stop_queue(net); ++ netif_carrier_off(net); ++ ++ dev->gadget = g; ++ SET_NETDEV_DEV(net, &g->dev); ++ ++ status = register_netdev(net); ++ if (status < 0) { ++ dev_dbg(&g->dev, "register_netdev failed, %d\n", status); ++ free_netdev(net); ++ } else { ++ DECLARE_MAC_BUF(tmp); ++ ++ INFO(dev, "MAC %s\n", print_mac(tmp, net->dev_addr)); ++ INFO(dev, "HOST MAC %s\n", print_mac(tmp, dev->host_mac)); ++ ++ the_dev = dev; ++ } ++ ++ return status; ++} ++ ++/** ++ * gether_cleanup - remove Ethernet-over-USB device ++ * Context: may sleep ++ * ++ * This is called to free all resources allocated by @gether_setup(). ++ */ ++void gether_cleanup(void) ++{ ++ if (!the_dev) ++ return; ++ ++ unregister_netdev(the_dev->net); ++ free_netdev(the_dev->net); ++ ++ /* assuming we used keventd, it must quiesce too */ ++ flush_scheduled_work(); ++ ++ the_dev = NULL; ++} ++ ++ ++/** ++ * gether_connect - notify network layer that USB link is active ++ * @link: the USB link, set up with endpoints, descriptors matching ++ * current device speed, and any framing wrapper(s) set up. ++ * Context: irqs blocked ++ * ++ * This is called to activate endpoints and let the network layer know ++ * the connection is active ("carrier detect"). It may cause the I/O ++ * queues to open and start letting network packets flow, but will in ++ * any case activate the endpoints so that they respond properly to the ++ * USB host. ++ * ++ * Verify net_device pointer returned using IS_ERR(). If it doesn't ++ * indicate some error code (negative errno), ep->driver_data values ++ * have been overwritten. ++ */ ++struct net_device *gether_connect(struct gether *link) ++{ ++ struct eth_dev *dev = the_dev; ++ int result = 0; ++ ++ if (!dev) ++ return ERR_PTR(-EINVAL); ++ ++ link->in_ep->driver_data = dev; ++ result = usb_ep_enable(link->in_ep, link->in); ++ if (result != 0) { ++ DBG(dev, "enable %s --> %d\n", ++ link->in_ep->name, result); ++ goto fail0; ++ } ++ ++ link->out_ep->driver_data = dev; ++ result = usb_ep_enable(link->out_ep, link->out); ++ if (result != 0) { ++ DBG(dev, "enable %s --> %d\n", ++ link->out_ep->name, result); ++ goto fail1; ++ } ++ ++ if (result == 0) ++ result = alloc_requests(dev, link, qlen(dev->gadget)); ++ ++ if (result == 0) { ++ dev->zlp = link->is_zlp_ok; ++ DBG(dev, "qlen %d\n", qlen(dev->gadget)); ++ ++ dev->header_len = link->header_len; ++ dev->unwrap = link->unwrap; ++ dev->wrap = link->wrap; ++ ++ spin_lock(&dev->lock); ++ dev->port_usb = link; ++ link->ioport = dev; ++ spin_unlock(&dev->lock); ++ ++ netif_carrier_on(dev->net); ++ if (netif_running(dev->net)) ++ eth_start(dev, GFP_ATOMIC); ++ ++ /* on error, disable any endpoints */ ++ } else { ++ (void) usb_ep_disable(link->out_ep); ++fail1: ++ (void) usb_ep_disable(link->in_ep); ++ } ++fail0: ++ /* caller is responsible for cleanup on error */ ++ if (result < 0) ++ return ERR_PTR(result); ++ return dev->net; ++} ++ ++/** ++ * gether_disconnect - notify network layer that USB link is inactive ++ * @link: the USB link, on which gether_connect() was called ++ * Context: irqs blocked ++ * ++ * This is called to deactivate endpoints and let the network layer know ++ * the connection went inactive ("no carrier"). ++ * ++ * On return, the state is as if gether_connect() had never been called. ++ * The endpoints are inactive, and accordingly without active USB I/O. ++ * Pointers to endpoint descriptors and endpoint private data are nulled. ++ */ ++void gether_disconnect(struct gether *link) ++{ ++ struct eth_dev *dev = link->ioport; ++ struct usb_request *req; ++ ++ WARN_ON(!dev); ++ if (!dev) ++ return; ++ ++ DBG(dev, "%s\n", __func__); ++ ++ netif_stop_queue(dev->net); ++ netif_carrier_off(dev->net); ++ ++ /* disable endpoints, forcing (synchronous) completion ++ * of all pending i/o. then free the request objects ++ * and forget about the endpoints. ++ */ ++ usb_ep_disable(link->in_ep); ++ spin_lock(&dev->req_lock); ++ while (!list_empty(&dev->tx_reqs)) { ++ req = container_of(dev->tx_reqs.next, ++ struct usb_request, list); ++ list_del(&req->list); ++ ++ spin_unlock(&dev->req_lock); ++ usb_ep_free_request(link->in_ep, req); ++ spin_lock(&dev->req_lock); ++ } ++ spin_unlock(&dev->req_lock); ++ link->in_ep->driver_data = NULL; ++ link->in = NULL; ++ ++ usb_ep_disable(link->out_ep); ++ spin_lock(&dev->req_lock); ++ while (!list_empty(&dev->rx_reqs)) { ++ req = container_of(dev->rx_reqs.next, ++ struct usb_request, list); ++ list_del(&req->list); ++ ++ spin_unlock(&dev->req_lock); ++ usb_ep_free_request(link->out_ep, req); ++ spin_lock(&dev->req_lock); ++ } ++ spin_unlock(&dev->req_lock); ++ link->out_ep->driver_data = NULL; ++ link->out = NULL; ++ ++ /* finish forgetting about this USB link episode */ ++ dev->header_len = 0; ++ dev->unwrap = NULL; ++ dev->wrap = NULL; ++ ++ spin_lock(&dev->lock); ++ dev->port_usb = NULL; ++ link->ioport = NULL; ++ spin_unlock(&dev->lock); ++} +--- /dev/null ++++ b/drivers/usb/gadget/u_ether.h +@@ -0,0 +1,83 @@ ++/* ++ * u_ether.h -- interface to USB gadget "ethernet link" utilities ++ * ++ * Copyright (C) 2003-2005,2008 David Brownell ++ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __U_ETHER_H ++#define __U_ETHER_H ++ ++#include <linux/err.h> ++#include <linux/if_ether.h> ++#include <linux/usb/composite.h> ++#include <linux/usb/cdc.h> ++ ++/* ++ * This represents the USB side of an "ethernet" link, managed by a USB ++ * function which provides control and (maybe) framing. Two functions ++ * in different configurations could share the same ethernet link/netdev. ++ * ++ * There is currently a limitation that one instance of this function ++ * may be present in any given configuration. ++ */ ++struct gether { ++ struct usb_function func; ++ ++ /* updated by gether_{connect,disconnect} */ ++ struct eth_dev *ioport; ++ ++ /* endpoints handle full and/or high speeds */ ++ struct usb_ep *in_ep; ++ struct usb_ep *out_ep; ++ ++ /* descriptors match device speed at gether_connect() time */ ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++ ++ bool is_zlp_ok; ++ ++ u16 cdc_filter; ++ ++ /* hooks for added framing, as needed for RNDIS and EEM. ++ * we currently don't support multiple frames per SKB. ++ */ ++ u32 header_len; ++ struct sk_buff *(*wrap)(struct sk_buff *skb); ++ int (*unwrap)(struct sk_buff *skb); ++ ++ /* called on network open/close */ ++ void (*open)(struct gether *); ++ void (*close)(struct gether *); ++}; ++ ++#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ ++ |USB_CDC_PACKET_TYPE_ALL_MULTICAST \ ++ |USB_CDC_PACKET_TYPE_PROMISCUOUS \ ++ |USB_CDC_PACKET_TYPE_DIRECTED) ++ ++ ++/* netdev setup/teardown as directed by the gadget driver */ ++int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]); ++void gether_cleanup(void); ++ ++/* connect/disconnect is handled by individual functions */ ++struct net_device *gether_connect(struct gether *); ++void gether_disconnect(struct gether *); ++ ++#endif /* __U_ETHER_H */ diff --git a/usb/usb-ethernet-gadget-split-rndis-function.patch b/usb/usb-ethernet-gadget-split-rndis-function.patch new file mode 100644 index 00000000000000..16416b4158b444 --- /dev/null +++ b/usb/usb-ethernet-gadget-split-rndis-function.patch @@ -0,0 +1,906 @@ +From david-b@pacbell.net Mon Jun 30 11:13:33 2008 +From: David Brownell <david-b@pacbell.net> +Date: Thu, 19 Jun 2008 18:20:04 -0700 +Subject: usb ethernet gadget: split RNDIS function +To: linux-usb@vger.kernel.org +Cc: Greg KH <greg@kroah.com> +Message-ID: <200806191820.04807.david-b@pacbell.net> +Content-Disposition: inline + + +From: David Brownell <dbrownell@users.sourceforge.net> + +This is a RNDIS function driver, extracted from the all-in-one +Ethernet gadget driver. + +Lightly tested ... there seems to be a pre-existing problem when +talking to Windows XP SP2, not quite sure what's up with that yet. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_rndis.c | 827 +++++++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/u_ether.h | 23 + + 3 files changed, 848 insertions(+), 4 deletions(-) + +--- /dev/null ++++ b/drivers/usb/gadget/f_rndis.c +@@ -0,0 +1,827 @@ ++/* ++ * f_rndis.c -- RNDIS link function driver ++ * ++ * Copyright (C) 2003-2005,2008 David Brownell ++ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* #define VERBOSE_DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/etherdevice.h> ++ ++#include <asm/atomic.h> ++ ++#include "u_ether.h" ++#include "rndis.h" ++ ++ ++/* ++ * This function is an RNDIS Ethernet port -- a Microsoft protocol that's ++ * been promoted instead of the standard CDC Ethernet. The published RNDIS ++ * spec is ambiguous, incomplete, and needlessly complex. Variants such as ++ * ActiveSync have even worse status in terms of specification. ++ * ++ * In short: it's a protocol controlled by (and for) Microsoft, not for an ++ * Open ecosystem or markets. Linux supports it *only* because Microsoft ++ * doesn't support the CDC Ethernet standard. ++ * ++ * The RNDIS data transfer model is complex, with multiple Ethernet packets ++ * per USB message, and out of band data. The control model is built around ++ * what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM ++ * (modem, not Ethernet) veneer, with those ACM descriptors being entirely ++ * useless (they're ignored). RNDIS expects to be the only function in its ++ * configuration, so it's no real help if you need composite devices; and ++ * it expects to be the first configuration too. ++ * ++ * There is a single technical advantage of RNDIS over CDC Ethernet, if you ++ * discount the fluff that its RPC can be made to deliver: it doesn't need ++ * a NOP altsetting for the data interface. That lets it work on some of the ++ * "so smart it's stupid" hardware which takes over configuration changes ++ * from the software, and adds restrictions like "no altsettings". ++ * ++ * Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and ++ * have all sorts of contrary-to-specification oddities that can prevent ++ * them from working sanely. Since bugfixes (or accurate specs, letting ++ * Linux work around those bugs) are unlikely to ever come from MSFT, you ++ * may want to avoid using RNDIS on purely operational grounds. ++ * ++ * Omissions from the RNDIS 1.0 specification include: ++ * ++ * - Power management ... references data that's scattered around lots ++ * of other documentation, which is incorrect/incomplete there too. ++ * ++ * - There are various undocumented protocol requirements, like the need ++ * to send garbage in some control-OUT messages. ++ * ++ * - MS-Windows drivers sometimes emit undocumented requests. ++ */ ++ ++struct rndis_ep_descs { ++ struct usb_endpoint_descriptor *in; ++ struct usb_endpoint_descriptor *out; ++ struct usb_endpoint_descriptor *notify; ++}; ++ ++struct f_rndis { ++ struct gether port; ++ u8 ctrl_id, data_id; ++ u8 ethaddr[ETH_ALEN]; ++ int config; ++ ++ struct usb_descriptor_header **fs_function; ++ struct rndis_ep_descs fs; ++ struct usb_descriptor_header **hs_function; ++ struct rndis_ep_descs hs; ++ ++ struct usb_ep *notify; ++ struct usb_endpoint_descriptor *notify_desc; ++ struct usb_request *notify_req; ++ atomic_t notify_count; ++}; ++ ++static inline struct f_rndis *func_to_rndis(struct usb_function *f) ++{ ++ return container_of(f, struct f_rndis, port.func); ++} ++ ++/* peak (theoretical) bulk transfer rate in bits-per-second */ ++static unsigned int bitrate(struct usb_gadget *g) ++{ ++ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) ++ return 13 * 512 * 8 * 1000 * 8; ++ else ++ return 19 * 64 * 1 * 1000 * 8; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ */ ++ ++#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ ++#define STATUS_BYTECOUNT 8 /* 8 bytes data */ ++ ++ ++/* interface descriptor: */ ++ ++static struct usb_interface_descriptor rndis_control_intf __initdata = { ++ .bLength = sizeof rndis_control_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ /* .bInterfaceNumber = DYNAMIC */ ++ /* status endpoint is optional; this could be patched later */ ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_COMM, ++ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, ++ .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++static struct usb_cdc_header_desc header_desc __initdata = { ++ .bLength = sizeof header_desc, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_HEADER_TYPE, ++ ++ .bcdCDC = __constant_cpu_to_le16(0x0110), ++}; ++ ++static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = { ++ .bLength = sizeof call_mgmt_descriptor, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, ++ ++ .bmCapabilities = 0x00, ++ .bDataInterface = 0x01, ++}; ++ ++static struct usb_cdc_acm_descriptor acm_descriptor __initdata = { ++ .bLength = sizeof acm_descriptor, ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_ACM_TYPE, ++ ++ .bmCapabilities = 0x00, ++}; ++ ++static struct usb_cdc_union_desc rndis_union_desc __initdata = { ++ .bLength = sizeof(rndis_union_desc), ++ .bDescriptorType = USB_DT_CS_INTERFACE, ++ .bDescriptorSubType = USB_CDC_UNION_TYPE, ++ /* .bMasterInterface0 = DYNAMIC */ ++ /* .bSlaveInterface0 = DYNAMIC */ ++}; ++ ++/* the data interface has two bulk endpoints */ ++ ++static struct usb_interface_descriptor rndis_data_intf __initdata = { ++ .bLength = sizeof rndis_data_intf, ++ .bDescriptorType = USB_DT_INTERFACE, ++ ++ /* .bInterfaceNumber = DYNAMIC */ ++ .bAlternateSetting = 1, ++ .bNumEndpoints = 2, ++ .bInterfaceClass = USB_CLASS_CDC_DATA, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ /* .iInterface = DYNAMIC */ ++}; ++ ++/* full speed support: */ ++ ++static struct usb_endpoint_descriptor fs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), ++ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, ++}; ++ ++static struct usb_endpoint_descriptor fs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_endpoint_descriptor fs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++}; ++ ++static struct usb_descriptor_header *eth_fs_function[] __initdata = { ++ /* control interface matches ACM, not Ethernet */ ++ (struct usb_descriptor_header *) &rndis_control_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &call_mgmt_descriptor, ++ (struct usb_descriptor_header *) &acm_descriptor, ++ (struct usb_descriptor_header *) &rndis_union_desc, ++ (struct usb_descriptor_header *) &fs_notify_desc, ++ /* data interface has no altsetting */ ++ (struct usb_descriptor_header *) &rndis_data_intf, ++ (struct usb_descriptor_header *) &fs_in_desc, ++ (struct usb_descriptor_header *) &fs_out_desc, ++ NULL, ++}; ++ ++/* high speed support: */ ++ ++static struct usb_endpoint_descriptor hs_notify_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT), ++ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, ++}; ++static struct usb_endpoint_descriptor hs_in_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_endpoint_descriptor hs_out_desc __initdata = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .wMaxPacketSize = __constant_cpu_to_le16(512), ++}; ++ ++static struct usb_descriptor_header *eth_hs_function[] __initdata = { ++ /* control interface matches ACM, not Ethernet */ ++ (struct usb_descriptor_header *) &rndis_control_intf, ++ (struct usb_descriptor_header *) &header_desc, ++ (struct usb_descriptor_header *) &call_mgmt_descriptor, ++ (struct usb_descriptor_header *) &acm_descriptor, ++ (struct usb_descriptor_header *) &rndis_union_desc, ++ (struct usb_descriptor_header *) &hs_notify_desc, ++ /* data interface has no altsetting */ ++ (struct usb_descriptor_header *) &rndis_data_intf, ++ (struct usb_descriptor_header *) &hs_in_desc, ++ (struct usb_descriptor_header *) &hs_out_desc, ++ NULL, ++}; ++ ++/* string descriptors: */ ++ ++static struct usb_string rndis_string_defs[] = { ++ [0].s = "RNDIS Communications Control", ++ [1].s = "RNDIS Ethernet Data", ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings rndis_string_table = { ++ .language = 0x0409, /* en-us */ ++ .strings = rndis_string_defs, ++}; ++ ++static struct usb_gadget_strings *rndis_strings[] = { ++ &rndis_string_table, ++ NULL, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct sk_buff *rndis_add_header(struct sk_buff *skb) ++{ ++ skb = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); ++ if (skb) ++ rndis_add_hdr(skb); ++ return skb; ++} ++ ++static void rndis_response_available(void *_rndis) ++{ ++ struct f_rndis *rndis = _rndis; ++ struct usb_request *req = rndis->notify_req; ++ struct usb_composite_dev *cdev = rndis->port.func.config->cdev; ++ __le32 *data = req->buf; ++ int status; ++ ++ if (atomic_inc_return(&rndis->notify_count)) ++ return; ++ ++ /* Send RNDIS RESPONSE_AVAILABLE notification; a ++ * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too ++ * ++ * This is the only notification defined by RNDIS. ++ */ ++ data[0] = cpu_to_le32(1); ++ data[1] = cpu_to_le32(0); ++ ++ status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); ++ if (status) { ++ atomic_dec(&rndis->notify_count); ++ DBG(cdev, "notify/0 --> %d\n", status); ++ } ++} ++ ++static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_rndis *rndis = req->context; ++ struct usb_composite_dev *cdev = rndis->port.func.config->cdev; ++ int status = req->status; ++ ++ /* after TX: ++ * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) ++ * - RNDIS_RESPONSE_AVAILABLE (status/irq) ++ */ ++ switch (status) { ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ /* connection gone */ ++ atomic_set(&rndis->notify_count, 0); ++ break; ++ default: ++ DBG(cdev, "RNDIS %s response error %d, %d/%d\n", ++ ep->name, status, ++ req->actual, req->length); ++ /* FALLTHROUGH */ ++ case 0: ++ if (ep != rndis->notify) ++ break; ++ ++ /* handle multiple pending RNDIS_RESPONSE_AVAILABLE ++ * notifications by resending until we're done ++ */ ++ if (atomic_dec_and_test(&rndis->notify_count)) ++ break; ++ status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); ++ if (status) { ++ atomic_dec(&rndis->notify_count); ++ DBG(cdev, "notify/1 --> %d\n", status); ++ } ++ break; ++ } ++} ++ ++static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) ++{ ++ struct f_rndis *rndis = req->context; ++ struct usb_composite_dev *cdev = rndis->port.func.config->cdev; ++ int status; ++ ++ /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ ++// spin_lock(&dev->lock); ++ status = rndis_msg_parser(rndis->config, (u8 *) req->buf); ++ if (status < 0) ++ ERROR(cdev, "RNDIS command error %d, %d/%d\n", ++ status, req->actual, req->length); ++// spin_unlock(&dev->lock); ++} ++ ++static int ++rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) ++{ ++ struct f_rndis *rndis = func_to_rndis(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ struct usb_request *req = cdev->req; ++ int value = -EOPNOTSUPP; ++ u16 w_index = le16_to_cpu(ctrl->wIndex); ++ u16 w_value = le16_to_cpu(ctrl->wValue); ++ u16 w_length = le16_to_cpu(ctrl->wLength); ++ ++ /* composite driver infrastructure handles everything except ++ * CDC class messages; interface activation uses set_alt(). ++ */ ++ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { ++ ++ /* RNDIS uses the CDC command encapsulation mechanism to implement ++ * an RPC scheme, with much getting/setting of attributes by OID. ++ */ ++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_SEND_ENCAPSULATED_COMMAND: ++ if (w_length > req->length || w_value ++ || w_index != rndis->ctrl_id) ++ goto invalid; ++ /* read the request; process it later */ ++ value = w_length; ++ req->complete = rndis_command_complete; ++ req->context = rndis; ++ /* later, rndis_response_available() sends a notification */ ++ break; ++ ++ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) ++ | USB_CDC_GET_ENCAPSULATED_RESPONSE: ++ if (w_value || w_index != rndis->ctrl_id) ++ goto invalid; ++ else { ++ u8 *buf; ++ u32 n; ++ ++ /* return the result */ ++ buf = rndis_get_next_response(rndis->config, &n); ++ if (buf) { ++ memcpy(req->buf, buf, n); ++ req->complete = rndis_response_complete; ++ rndis_free_response(rndis->config, buf); ++ value = n; ++ } ++ /* else stalls ... spec says to avoid that */ ++ } ++ break; ++ ++ default: ++invalid: ++ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ } ++ ++ /* respond with data transfer or status phase? */ ++ if (value >= 0) { ++ DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ w_value, w_index, w_length); ++ req->zero = 0; ++ req->length = value; ++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) ++ ERROR(cdev, "rndis response on err %d\n", value); ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++ ++static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) ++{ ++ struct f_rndis *rndis = func_to_rndis(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ /* we know alt == 0 */ ++ ++ if (intf == rndis->ctrl_id) { ++ if (rndis->notify->driver_data) { ++ VDBG(cdev, "reset rndis control %d\n", intf); ++ usb_ep_disable(rndis->notify); ++ } else { ++ VDBG(cdev, "init rndis ctrl %d\n", intf); ++ rndis->notify_desc = ep_choose(cdev->gadget, ++ rndis->hs.notify, ++ rndis->fs.notify); ++ } ++ usb_ep_enable(rndis->notify, rndis->notify_desc); ++ rndis->notify->driver_data = rndis; ++ ++ } else if (intf == rndis->data_id) { ++ struct net_device *net; ++ ++ if (rndis->port.in_ep->driver_data) { ++ DBG(cdev, "reset rndis\n"); ++ gether_disconnect(&rndis->port); ++ } else { ++ DBG(cdev, "init rndis\n"); ++ rndis->port.in = ep_choose(cdev->gadget, ++ rndis->hs.in, rndis->fs.in); ++ rndis->port.out = ep_choose(cdev->gadget, ++ rndis->hs.out, rndis->fs.out); ++ } ++ ++ /* Avoid ZLPs; they can be troublesome. */ ++ rndis->port.is_zlp_ok = false; ++ ++ /* RNDIS should be in the "RNDIS uninitialized" state, ++ * either never activated or after rndis_uninit(). ++ * ++ * We don't want data to flow here until a nonzero packet ++ * filter is set, at which point it enters "RNDIS data ++ * initialized" state ... but we do want the endpoints ++ * to be activated. It's a strange little state. ++ * ++ * REVISIT the RNDIS gadget code has done this wrong for a ++ * very long time. We need another call to the link layer ++ * code -- gether_updown(...bool) maybe -- to do it right. ++ */ ++ rndis->port.cdc_filter = 0; ++ ++ DBG(cdev, "RNDIS RX/TX early activation ... \n"); ++ net = gether_connect(&rndis->port); ++ if (IS_ERR(net)) ++ return PTR_ERR(net); ++ ++ rndis_set_param_dev(rndis->config, net, ++ &rndis->port.cdc_filter); ++ } else ++ goto fail; ++ ++ return 0; ++fail: ++ return -EINVAL; ++} ++ ++static void rndis_disable(struct usb_function *f) ++{ ++ struct f_rndis *rndis = func_to_rndis(f); ++ struct usb_composite_dev *cdev = f->config->cdev; ++ ++ if (!rndis->notify->driver_data) ++ return; ++ ++ DBG(cdev, "rndis deactivated\n"); ++ ++ rndis_uninit(rndis->config); ++ gether_disconnect(&rndis->port); ++ ++ usb_ep_disable(rndis->notify); ++ rndis->notify->driver_data = NULL; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * This isn't quite the same mechanism as CDC Ethernet, since the ++ * notification scheme passes less data, but the same set of link ++ * states must be tested. A key difference is that altsettings are ++ * not used to tell whether the link should send packets or not. ++ */ ++ ++static void rndis_open(struct gether *geth) ++{ ++ struct f_rndis *rndis = func_to_rndis(&geth->func); ++ struct usb_composite_dev *cdev = geth->func.config->cdev; ++ ++ DBG(cdev, "%s\n", __func__); ++ ++ rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, ++ bitrate(cdev->gadget) / 100); ++ rndis_signal_connect(rndis->config); ++} ++ ++static void rndis_close(struct gether *geth) ++{ ++ struct f_rndis *rndis = func_to_rndis(&geth->func); ++ ++ DBG(geth->func.config->cdev, "%s\n", __func__); ++ ++ rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); ++ rndis_signal_disconnect(rndis->config); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ethernet function driver setup/binding */ ++ ++static int __init ++rndis_bind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct usb_composite_dev *cdev = c->cdev; ++ struct f_rndis *rndis = func_to_rndis(f); ++ int status; ++ struct usb_ep *ep; ++ ++ /* allocate instance-specific interface IDs */ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ rndis->ctrl_id = status; ++ ++ rndis_control_intf.bInterfaceNumber = status; ++ rndis_union_desc.bMasterInterface0 = status; ++ ++ status = usb_interface_id(c, f); ++ if (status < 0) ++ goto fail; ++ rndis->data_id = status; ++ ++ rndis_data_intf.bInterfaceNumber = status; ++ rndis_union_desc.bSlaveInterface0 = status; ++ ++ status = -ENODEV; ++ ++ /* allocate instance-specific endpoints */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); ++ if (!ep) ++ goto fail; ++ rndis->port.in_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); ++ if (!ep) ++ goto fail; ++ rndis->port.out_ep = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ /* NOTE: a status/notification endpoint is, strictly speaking, ++ * optional. We don't treat it that way though! It's simpler, ++ * and some newer profiles don't treat it as optional. ++ */ ++ ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); ++ if (!ep) ++ goto fail; ++ rndis->notify = ep; ++ ep->driver_data = cdev; /* claim */ ++ ++ status = -ENOMEM; ++ ++ /* allocate notification request and buffer */ ++ rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); ++ if (!rndis->notify_req) ++ goto fail; ++ rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); ++ if (!rndis->notify_req->buf) ++ goto fail; ++ rndis->notify_req->length = STATUS_BYTECOUNT; ++ rndis->notify_req->context = rndis; ++ rndis->notify_req->complete = rndis_response_complete; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->descriptors = usb_copy_descriptors(eth_fs_function); ++ if (!f->descriptors) ++ goto fail; ++ ++ rndis->fs.in = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_in_desc); ++ rndis->fs.out = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_out_desc); ++ rndis->fs.notify = usb_find_endpoint(eth_fs_function, ++ f->descriptors, &fs_notify_desc); ++ ++ /* support all relevant hardware speeds... we expect that when ++ * hardware is dual speed, all bulk-capable endpoints work at ++ * both speeds ++ */ ++ if (gadget_is_dualspeed(c->cdev->gadget)) { ++ hs_in_desc.bEndpointAddress = ++ fs_in_desc.bEndpointAddress; ++ hs_out_desc.bEndpointAddress = ++ fs_out_desc.bEndpointAddress; ++ ++ /* copy descriptors, and track endpoint copies */ ++ f->hs_descriptors = usb_copy_descriptors(eth_hs_function); ++ ++ if (!f->hs_descriptors) ++ goto fail; ++ ++ rndis->hs.in = usb_find_endpoint(eth_hs_function, ++ f->hs_descriptors, &hs_in_desc); ++ rndis->hs.out = usb_find_endpoint(eth_hs_function, ++ f->hs_descriptors, &hs_out_desc); ++ } ++ ++ rndis->port.open = rndis_open; ++ rndis->port.close = rndis_close; ++ ++ status = rndis_register(rndis_response_available, rndis); ++ if (status < 0) ++ goto fail; ++ rndis->config = status; ++ ++ rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0); ++ rndis_set_host_mac(rndis->config, rndis->ethaddr); ++ ++#if 0 ++// FIXME ++ if (rndis_set_param_vendor(rndis->config, vendorID, ++ manufacturer)) ++ goto fail0; ++#endif ++ ++ /* 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(). ++ */ ++ ++ DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", ++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", ++ rndis->port.in_ep->name, rndis->port.out_ep->name, ++ rndis->notify->name); ++ return 0; ++ ++fail: ++ if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors) ++ usb_free_descriptors(f->hs_descriptors); ++ if (f->descriptors) ++ usb_free_descriptors(f->descriptors); ++ ++ if (rndis->notify_req) { ++ kfree(rndis->notify_req->buf); ++ usb_ep_free_request(rndis->notify, rndis->notify_req); ++ } ++ ++ /* we might as well release our claims on endpoints */ ++ if (rndis->notify) ++ rndis->notify->driver_data = NULL; ++ if (rndis->port.out) ++ rndis->port.out_ep->driver_data = NULL; ++ if (rndis->port.in) ++ rndis->port.in_ep->driver_data = NULL; ++ ++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); ++ ++ return status; ++} ++ ++static void ++rndis_unbind(struct usb_configuration *c, struct usb_function *f) ++{ ++ struct f_rndis *rndis = func_to_rndis(f); ++ ++ rndis_deregister(rndis->config); ++ rndis_exit(); ++ ++ if (gadget_is_dualspeed(c->cdev->gadget)) ++ usb_free_descriptors(f->hs_descriptors); ++ usb_free_descriptors(f->descriptors); ++ ++ kfree(rndis->notify_req->buf); ++ usb_ep_free_request(rndis->notify, rndis->notify_req); ++ ++ kfree(rndis); ++} ++ ++/* Some controllers can't support RNDIS ... */ ++static inline bool can_support_rndis(struct usb_configuration *c) ++{ ++ /* only two endpoints on sa1100 */ ++ if (gadget_is_sa1100(c->cdev->gadget)) ++ return false; ++ ++ /* everything else is *presumably* fine */ ++ return true; ++} ++ ++/** ++ * rndis_bind_config - add RNDIS network link to a configuration ++ * @c: the configuration to support the network link ++ * @ethaddr: a buffer in which the ethernet address of the host side ++ * side of the link was recorded ++ * Context: single threaded during gadget setup ++ * ++ * Returns zero on success, else negative errno. ++ * ++ * Caller must have called @gether_setup(). Caller is also responsible ++ * for calling @gether_cleanup() before module unload. ++ */ ++int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) ++{ ++ struct f_rndis *rndis; ++ int status; ++ ++ if (!can_support_rndis(c) || !ethaddr) ++ return -EINVAL; ++ ++ /* maybe allocate device-global string IDs */ ++ if (rndis_string_defs[0].id == 0) { ++ ++ /* ... and setup RNDIS itself */ ++ status = rndis_init(); ++ if (status < 0) ++ return status; ++ ++ /* control interface label */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ rndis_string_defs[0].id = status; ++ rndis_control_intf.iInterface = status; ++ ++ /* data interface label */ ++ status = usb_string_id(c->cdev); ++ if (status < 0) ++ return status; ++ rndis_string_defs[1].id = status; ++ rndis_data_intf.iInterface = status; ++ } ++ ++ /* allocate and initialize one new instance */ ++ status = -ENOMEM; ++ rndis = kzalloc(sizeof *rndis, GFP_KERNEL); ++ if (!rndis) ++ goto fail; ++ ++ memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); ++ ++ /* RNDIS activates when the host changes this filter */ ++ rndis->port.cdc_filter = 0; ++ ++ /* RNDIS has special (and complex) framing */ ++ rndis->port.header_len = sizeof(struct rndis_packet_msg_type); ++ rndis->port.wrap = rndis_add_header; ++ rndis->port.unwrap = rndis_rm_hdr; ++ ++ rndis->port.func.name = "rndis"; ++ rndis->port.func.strings = rndis_strings; ++ /* descriptors are per-instance copies */ ++ rndis->port.func.bind = rndis_bind; ++ rndis->port.func.unbind = rndis_unbind; ++ rndis->port.func.set_alt = rndis_set_alt; ++ rndis->port.func.setup = rndis_setup; ++ rndis->port.func.disable = rndis_disable; ++ ++ status = usb_add_function(c, &rndis->port.func); ++ if (status) { ++ kfree(rndis); ++fail: ++ rndis_exit(); ++ } ++ return status; ++} +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -35,7 +35,7 @@ g_printer-objs := printer.o usbstring. + epautoconf.o + + ifeq ($(CONFIG_USB_ETH_RNDIS),y) +- g_ether-objs += rndis.o ++ g_ether-objs += f_rndis.o rndis.o + endif + + obj-$(CONFIG_USB_ZERO) += g_zero.o +--- a/drivers/usb/gadget/u_ether.h ++++ b/drivers/usb/gadget/u_ether.h +@@ -34,10 +34,13 @@ + /* + * This represents the USB side of an "ethernet" link, managed by a USB + * function which provides control and (maybe) framing. Two functions +- * in different configurations could share the same ethernet link/netdev. ++ * in different configurations could share the same ethernet link/netdev, ++ * using different host interaction models. + * +- * There is currently a limitation that one instance of this function +- * may be present in any given configuration. ++ * There is a current limitation that only one instance of this link may ++ * be present in any given configuration. When that's a problem, network ++ * layer facilities can be used to package multiple logical links on this ++ * single "physical" one. + */ + struct gether { + struct usb_function func; +@@ -107,4 +110,18 @@ static inline bool can_support_ecm(struc + int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); + int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); + ++#ifdef CONFIG_USB_ETH_RNDIS ++ ++int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]); ++ ++#else ++ ++static inline int ++rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) ++{ ++ return 0; ++} ++ ++#endif ++ + #endif /* __U_ETHER_H */ diff --git a/usb/usb-gadget-composite-gadget-framework.patch b/usb/usb-gadget-composite-gadget-core.patch index 51a992806b9803..843dd3a9ad5831 100644 --- a/usb/usb-gadget-composite-gadget-framework.patch +++ b/usb/usb-gadget-composite-gadget-core.patch @@ -1,13 +1,15 @@ -From david-b@pacbell.net Fri Jun 6 15:10:24 2008 +From david-b@pacbell.net Mon Jun 30 11:09:21 2008 From: David Brownell <david-b@pacbell.net> -Date: Tue, 20 May 2008 11:04:46 -0700 -Subject: usb gadget: composite gadget framework +Date: Thu, 19 Jun 2008 17:52:58 -0700 +Subject: usb gadget: composite gadget core To: linux-usb@vger.kernel.org -Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> -Message-ID: <200805201104.46710.david-b@pacbell.net> +Cc: Greg KH <greg@kroah.com> +Message-ID: <200806191752.58233.david-b@pacbell.net> Content-Disposition: inline +From: David Brownell <dbrownell@users.sourceforge.net> + Add <linux/usb/composite.h> interfaces for composite gadget drivers, and basic implementation support behind it: @@ -42,9 +44,9 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- Documentation/DocBook/gadget.tmpl | 35 + - drivers/usb/gadget/composite.c | 1041 ++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/composite.c | 1040 ++++++++++++++++++++++++++++++++++++++ include/linux/usb/composite.h | 338 ++++++++++++ - 3 files changed, 1414 insertions(+) + 3 files changed, 1413 insertions(+) --- a/Documentation/DocBook/gadget.tmpl +++ b/Documentation/DocBook/gadget.tmpl @@ -92,7 +94,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> <chapter id="controllers"><title>Peripheral Controller Drivers</title> --- /dev/null +++ b/drivers/usb/gadget/composite.c -@@ -0,0 +1,1041 @@ +@@ -0,0 +1,1040 @@ +/* + * composite.c - infrastructure for Composite USB Gadgets + * @@ -437,7 +439,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; -+ } ; speed; }), number, c ? c->label : ""); ++ } ; speed; }), number, c ? c->label : "unconfigured"); + + if (!c) + goto done; @@ -892,14 +894,14 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); -+ unsigned long flags; + + /* composite_disconnect() must already have been called + * by the underlying peripheral controller driver! ++ * so there's no i/o concurrency that could affect the ++ * state protected by cdev->lock. + */ + WARN_ON(cdev->config); + -+ spin_lock_irqsave(&cdev->lock, flags); + while (!list_empty(&cdev->configs)) { + struct usb_configuration *c; + @@ -927,7 +929,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + } + if (composite->unbind) + composite->unbind(cdev); -+ spin_unlock_irqrestore(&cdev->lock, flags); + + if (cdev->req) { + kfree(cdev->req->buf); @@ -1286,7 +1287,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + return fs; +} + -+#define MAX_CONFIG_INTERFACES 8 /* arbitrary; max 255 */ ++#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */ + +/** + * struct usb_configuration - represents one gadget configuration diff --git a/usb/usb-gadget-support-descriptor-copying.patch b/usb/usb-gadget-descriptor-copying-support.patch index 996eac0c45d9ec..bc68e223d794a2 100644 --- a/usb/usb-gadget-support-descriptor-copying.patch +++ b/usb/usb-gadget-descriptor-copying-support.patch @@ -1,13 +1,15 @@ -From david-b@pacbell.net Fri Jun 6 15:10:05 2008 +From david-b@pacbell.net Mon Jun 30 11:08:59 2008 From: David Brownell <david-b@pacbell.net> -Date: Tue, 20 May 2008 11:02:47 -0700 -Subject: USB: gadget support descriptor copying +Date: Thu, 19 Jun 2008 17:52:25 -0700 +Subject: usb gadget: descriptor copying support To: linux-usb@vger.kernel.org -Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> -Message-ID: <200805201102.47771.david-b@pacbell.net> +Cc: Greg KH <greg@kroah.com> +Message-ID: <200806191752.25376.david-b@pacbell.net> Content-Disposition: inline +From: David Brownell <dbrownell@users.sourceforge.net> + Define three new descriptor manipulation utilities, for use when setting up functions that may have multiple instances: @@ -20,28 +22,29 @@ tables of descriptors they update, now used as __initdata templates. When a function creates a new instance, it patches those tables with relevant interface and string IDs, plus endpoint assignments. Then it -makes a copy of those morphed descriptors and associates that with the -new function instance, and records the endpoint descriptors to use when +copies those morphed descriptors, associates the copies with the new +function instance, and records the endpoint descriptors to use when activating the endpoints. When initialization is done, only the copies remain in memory. The copies are freed on driver removal. -This ensures that each instances has descriptors which hold the right +This ensures that each instance has descriptors which hold the right instance-specific data. Two instances in the same configuration will -obviously never share the same interface IDs or use the same endpoints, -and instances in different configuration mostly wouldn't do so either. +obviously never share the same interface IDs or use the same endpoints. +Instances in different configurations won't do so either, which means +this is slightly less memory-efficient in some cases. This also includes a bugfix to the epautoconf code that shows up with this usage model. It must replace the previous endpoint number when -it updates descriptors, not just mask in a few more bits. +updating the template descriptors, not just mask in a few more bits. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- - drivers/usb/gadget/config.c | 75 +++++++++++++++++++++++++++++++++++++++- + drivers/usb/gadget/config.c | 76 +++++++++++++++++++++++++++++++++++++++- drivers/usb/gadget/epautoconf.c | 1 include/linux/usb/gadget.h | 19 ++++++++++ - 3 files changed, 94 insertions(+), 1 deletion(-) + 3 files changed, 95 insertions(+), 1 deletion(-) --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -54,7 +57,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> /* then interface/endpoint/class/vendor/... */ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, -@@ -115,3 +115,76 @@ int usb_gadget_config_buf( +@@ -115,3 +115,77 @@ int usb_gadget_config_buf( return len; } @@ -92,7 +95,8 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + * to descriptors copied starting at "mem"; + * and return "ret" + */ -+ ret = tmp = mem; ++ tmp = mem; ++ ret = mem; + mem += (n_desc + 1) * sizeof(*tmp); + while (*src) { + memcpy(mem, *src, (*src)->bLength); diff --git a/usb/usb-gadget-new-cdc-composite-gadget-driver.patch b/usb/usb-gadget-new-cdc-composite-gadget-driver.patch new file mode 100644 index 00000000000000..4c845adb8687e2 --- /dev/null +++ b/usb/usb-gadget-new-cdc-composite-gadget-driver.patch @@ -0,0 +1,344 @@ +From david-b@pacbell.net Mon Jun 30 11:19:25 2008 +From: David Brownell <david-b@pacbell.net> +Date: Thu, 19 Jun 2008 18:20:26 -0700 +Subject: usb gadget: new "CDC Composite" gadget driver +To: linux-usb@vger.kernel.org +Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com>, "Dima Zavin" <dmitriyz@google.com>, Greg KH <greg@kroah.com> +Message-ID: <200806191820.26287.david-b@pacbell.net> +Content-Disposition: inline + + +From: David Brownell <dbrownell@users.sourceforge.net> + +This is a simple example of a composite gadget, combining two +Communications Class Device (CDC) functions: ECM and ACM. + +This provides a clear example of how the composite gadget framework +is intended to work. It's surprising that MS-Windows (or at least, +XP and previous) won't "just work" with something this simple... + +One /proc/bus/usb/devices listing looks like: + + T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 46 Spd=480 MxCh= 0 + D: Ver= 2.00 Cls=02(comm.) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 + P: Vendor=0525 ProdID=a4aa Rev= 3.01 + S: Manufacturer=Linux 2.6.26-rc6-pnut with net2280 + S: Product=CDC Composite Gadget + C:* #Ifs= 4 Cfg#= 1 Atr=c0 MxPwr= 2mA + I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=06 Prot=00 Driver=cdc_ether + E: Ad=83(I) Atr=03(Int.) MxPS= 16 Ivl=32ms + I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_ether + I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_ether + E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms + E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms + I:* If#= 2 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=cdc_acm + E: Ad=86(I) Atr=03(Int.) MxPS= 8 Ivl=32ms + I:* If#= 3 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_acm + E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms + E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms + +Not all USB peripheral controller hardware can support this driver. +All the highspeed-capable peripheral controllers with drivers now in +the mainline kernel seem to support this, as does omap_udc. But +many full speed controllers don't have enough endpoints, or (as with +the PXA controllers) don't support altsettings. + +Lightly tested. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/Kconfig | 13 ++ + drivers/usb/gadget/Makefile | 3 + drivers/usb/gadget/cdc2.c | 246 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 262 insertions(+) + +--- /dev/null ++++ b/drivers/usb/gadget/cdc2.c +@@ -0,0 +1,246 @@ ++/* ++ * cdc2.c -- CDC Composite driver, with ECM and ACM support ++ * ++ * Copyright (C) 2008 David Brownell ++ * Copyright (C) 2008 Nokia Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/utsname.h> ++ ++#include "u_ether.h" ++#include "u_serial.h" ++ ++ ++#define DRIVER_DESC "CDC Composite Gadget" ++#define DRIVER_VERSION "King Kamehameha Day 2008" ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! ++ * Instead: allocate your own, using normal USB-IF procedures. ++ */ ++ ++/* Thanks to NetChip Technologies for donating this product ID. ++ * It's for devices with only this composite CDC configuration. ++ */ ++#define CDC_VENDOR_NUM 0x0525 /* NetChip */ ++#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_device_descriptor device_desc = { ++ .bLength = sizeof device_desc, ++ .bDescriptorType = USB_DT_DEVICE, ++ ++ .bcdUSB = __constant_cpu_to_le16(0x0200), ++ ++ .bDeviceClass = USB_CLASS_COMM, ++ .bDeviceSubClass = 0, ++ .bDeviceProtocol = 0, ++ /* .bMaxPacketSize0 = f(hardware) */ ++ ++ /* Vendor and product id can be overridden by module parameters. */ ++ .idVendor = __constant_cpu_to_le16(CDC_VENDOR_NUM), ++ .idProduct = __constant_cpu_to_le16(CDC_PRODUCT_NUM), ++ /* .bcdDevice = f(hardware) */ ++ /* .iManufacturer = DYNAMIC */ ++ /* .iProduct = DYNAMIC */ ++ /* NO SERIAL NUMBER */ ++ .bNumConfigurations = 1, ++}; ++ ++static struct usb_otg_descriptor otg_descriptor = { ++ .bLength = sizeof otg_descriptor, ++ .bDescriptorType = USB_DT_OTG, ++ ++ /* REVISIT SRP-only hardware is possible, although ++ * it would not be called "OTG" ... ++ */ ++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, ++}; ++ ++static const struct usb_descriptor_header *otg_desc[] = { ++ (struct usb_descriptor_header *) &otg_descriptor, ++ NULL, ++}; ++ ++ ++/* string IDs are assigned dynamically */ ++ ++#define STRING_MANUFACTURER_IDX 0 ++#define STRING_PRODUCT_IDX 1 ++ ++static char manufacturer[50]; ++ ++static struct usb_string strings_dev[] = { ++ [STRING_MANUFACTURER_IDX].s = manufacturer, ++ [STRING_PRODUCT_IDX].s = DRIVER_DESC, ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab_dev = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings_dev, ++}; ++ ++static struct usb_gadget_strings *dev_strings[] = { ++ &stringtab_dev, ++ NULL, ++}; ++ ++static u8 hostaddr[ETH_ALEN]; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * We _always_ have both CDC ECM and CDC ACM functions. ++ */ ++static int __init cdc_do_config(struct usb_configuration *c) ++{ ++ int status; ++ ++ if (gadget_is_otg(c->cdev->gadget)) { ++ c->descriptors = otg_desc; ++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++ } ++ ++ status = ecm_bind_config(c, hostaddr); ++ if (status < 0) ++ return status; ++ ++ status = acm_bind_config(c, 0); ++ if (status == 0) ++ return status; ++ ++ return 0; ++} ++ ++static struct usb_configuration cdc_config_driver = { ++ .label = "CDC Composite (ECM + ACM)", ++ .bind = cdc_do_config, ++ .bConfigurationValue = 1, ++ /* .iConfiguration = DYNAMIC */ ++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, ++ .bMaxPower = 1, /* 2 mA, minimal */ ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init cdc_bind(struct usb_composite_dev *cdev) ++{ ++ int gcnum; ++ struct usb_gadget *gadget = cdev->gadget; ++ int status; ++ ++ if (!can_support_ecm(cdev->gadget)) { ++ ERROR(cdev, "controller '%s' not usable\n", gadget->name); ++ return -EINVAL; ++ } ++ ++ /* set up network link layer */ ++ status = gether_setup(cdev->gadget, hostaddr); ++ if (status < 0) ++ return status; ++ ++ /* set up serial link layer */ ++ status = gserial_setup(cdev->gadget, 1); ++ if (status < 0) ++ goto fail0; ++ ++ gcnum = usb_gadget_controller_number(gadget); ++ if (gcnum >= 0) ++ device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); ++ else { ++ /* We assume that can_support_ecm() tells the truth; ++ * but if the controller isn't recognized at all then ++ * that assumption is a bit more likely to be wrong. ++ */ ++ WARN(cdev, "controller '%s' not recognized; trying %s\n", ++ gadget->name, ++ cdc_config_driver.label); ++ device_desc.bcdDevice = ++ __constant_cpu_to_le16(0x0300 | 0x0099); ++ } ++ ++ ++ /* Allocate string descriptor numbers ... note that string ++ * contents can be overridden by the composite_dev glue. ++ */ ++ ++ /* device descriptor strings: manufacturer, product */ ++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", ++ init_utsname()->sysname, init_utsname()->release, ++ gadget->name); ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail1; ++ strings_dev[STRING_MANUFACTURER_IDX].id = status; ++ device_desc.iManufacturer = status; ++ ++ status = usb_string_id(cdev); ++ if (status < 0) ++ goto fail1; ++ strings_dev[STRING_PRODUCT_IDX].id = status; ++ device_desc.iProduct = status; ++ ++ /* register our configuration */ ++ status = usb_add_config(cdev, &cdc_config_driver); ++ if (status < 0) ++ goto fail1; ++ ++ INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); ++ ++ return 0; ++ ++fail1: ++ gserial_cleanup(); ++fail0: ++ gether_cleanup(); ++ return status; ++} ++ ++static int __exit cdc_unbind(struct usb_composite_dev *cdev) ++{ ++ gserial_cleanup(); ++ gether_cleanup(); ++ return 0; ++} ++ ++static struct usb_composite_driver cdc_driver = { ++ .name = "g_cdc", ++ .dev = &device_desc, ++ .strings = dev_strings, ++ .bind = cdc_bind, ++ .unbind = __exit_p(cdc_unbind), ++}; ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR("David Brownell"); ++MODULE_LICENSE("GPL"); ++ ++static int __init init(void) ++{ ++ return usb_composite_register(&cdc_driver); ++} ++module_init(init); ++ ++static void __exit cleanup(void) ++{ ++ usb_composite_unregister(&cdc_driver); ++} ++module_exit(cleanup); +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -586,6 +586,19 @@ config USB_G_PRINTER + For more information, see Documentation/usb/gadget_printer.txt + which includes sample code for accessing the device file. + ++config USB_CDC_COMPOSITE ++ tristate "CDC Composite Device (Ethernet and ACM)" ++ help ++ This driver provides two functions in one configuration: ++ a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. ++ ++ This driver requires four bulk and two interrupt endpoints, ++ plus the ability to handle altsettings. Not all peripheral ++ controllers are that capable. ++ ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module. ++ + # put drivers that need isochronous transfer support (for audio + # or video class gadget drivers), or specific hardware, here. + +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -33,6 +33,8 @@ g_file_storage-objs := file_storage.o u + epautoconf.o + g_printer-objs := printer.o usbstring.o config.o \ + epautoconf.o ++g_cdc-objs := cdc2.o u_ether.o f_ecm.o \ ++ u_serial.o f_acm.o $(C_UTILS) + + ifeq ($(CONFIG_USB_ETH_RNDIS),y) + g_ether-objs += f_rndis.o rndis.o +@@ -45,4 +47,5 @@ obj-$(CONFIG_USB_FILE_STORAGE) += g_file + obj-$(CONFIG_USB_G_SERIAL) += g_serial.o + obj-$(CONFIG_USB_G_PRINTER) += g_printer.o + obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o ++obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o + diff --git a/usb/usb-gadget-rndis-cleanups.patch b/usb/usb-gadget-rndis-cleanups.patch new file mode 100644 index 00000000000000..af24c0107ff80d --- /dev/null +++ b/usb/usb-gadget-rndis-cleanups.patch @@ -0,0 +1,796 @@ +From david-b@pacbell.net Mon Jun 30 11:11:23 2008 +From: David Brownell <david-b@pacbell.net> +Date: Thu, 19 Jun 2008 18:19:16 -0700 +Subject: usb gadget: RNDIS cleanups +To: linux-usb@vger.kernel.org +Cc: Greg KH <greg@kroah.com> +Message-ID: <200806191819.16560.david-b@pacbell.net> +Content-Disposition: inline + + +From: David Brownell <dbrownell@users.sourceforge.net> + +Some cleanup to the RNDIS code: + + - Minor bugfix: rndis_unit() is supposed to put the link into the + RNDIS_UNINITIALIZED state, which does not mean "unused". There's + a separate method to stop using the link. (Bug doesn't affect + anything right now because of how the code is used.) + + - Reduce coupling between RNDIS code and its user(s), in preparation + for updates in that code: + + * Decouple RNDIS_RESPONSE_AVAILABLE notifications from net_device + by passing just a void* handle. (Also, remove the unused return + value of the notification callback.) + * When it needs a copy of net_device stats, just ask for it + + - Remove unused/untested code backing various never-used OIDs: + + * RNDIS_PM, RNDIS_WAKEUP ... "should" get implemented, but the + relevant docs were unclear, ambguous, and incomplete. Someone + with access to the Hidden Gospels (maybe in the EU?) might be + able to figure out what this should do. + * RNDIS_OPTIONAL_STATS ... as the name suggests, optional. Never + implemented in part because not all the semantics were clear. + * OID_GEN_RNDIS_CONFIG_PARAMETER, which has been #if 0 forever. + + - A few small whitespace fixes + +Plus switch the VERBOSE symbol over to the newer VERBOSE_DEBUG style. + +There should be no functional changes because of this patch; it's a +net source code shrink (because of the dead/unused code removal) and +a small object code shrink (a couple hundred bytes on ARMv5). + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + drivers/usb/gadget/ether.c | 24 +- + drivers/usb/gadget/ndis.h | 10 - + drivers/usb/gadget/rndis.c | 366 +++++++-------------------------------------- + drivers/usb/gadget/rndis.h | 7 + 4 files changed, 79 insertions(+), 328 deletions(-) + +--- a/drivers/usb/gadget/ether.c ++++ b/drivers/usb/gadget/ether.c +@@ -1106,6 +1106,8 @@ static void eth_reset_config (struct eth + + netif_stop_queue (dev->net); + netif_carrier_off (dev->net); ++ ++ /* RNDIS enters RNDIS_UNINITIALIZED state */ + rndis_uninit(dev->rndis_config); + + /* disable endpoints, forcing (synchronous) completion of +@@ -1604,8 +1606,6 @@ eth_disconnect (struct usb_gadget *gadge + 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). + */ +@@ -2067,23 +2067,23 @@ rndis_control_ack_complete (struct usb_e + eth_req_free(ep, req); + } + +-static int rndis_control_ack (struct net_device *net) ++static void rndis_resp_avail(void *_dev) + { +- struct eth_dev *dev = netdev_priv(net); ++ struct eth_dev *dev = _dev; + int length; + struct usb_request *resp = dev->stat_req; + + /* in case RNDIS calls this after disconnect */ + if (!dev->status) { + DEBUG (dev, "status ENODEV\n"); +- return -ENODEV; ++ return; + } + + /* in case queue length > 1 */ + if (resp->context) { + resp = eth_req_alloc (dev->status_ep, 8, GFP_ATOMIC); + if (!resp) +- return -ENOMEM; ++ return; + } + + /* Send RNDIS RESPONSE_AVAILABLE notification; +@@ -2101,13 +2101,11 @@ static int rndis_control_ack (struct net + resp->status = 0; + rndis_control_ack_complete (dev->status_ep, resp); + } +- +- return 0; + } + + #else + +-#define rndis_control_ack NULL ++#define rndis_resp_avail NULL + + #endif /* RNDIS */ + +@@ -2566,18 +2564,18 @@ autoconf_fail: + + /* FIXME RNDIS vendor id == "vendor NIC code" == ? */ + +- dev->rndis_config = rndis_register (rndis_control_ack); +- if (dev->rndis_config < 0) { ++ status = rndis_register(rndis_resp_avail, dev); ++ if (status < 0) { + fail0: + unregister_netdev (dev->net); +- status = -ENODEV; + goto fail; + } ++ dev->rndis_config = status; + + /* these set up a lot of the OIDs that RNDIS needs */ + rndis_set_host_mac (dev->rndis_config, dev->host_mac); + if (rndis_set_param_dev (dev->rndis_config, dev->net, +- &dev->stats, &dev->cdc_filter)) ++ &dev->cdc_filter)) + goto fail0; + if (rndis_set_param_vendor(dev->rndis_config, vendorID, + manufacturer)) +--- a/drivers/usb/gadget/ndis.h ++++ b/drivers/usb/gadget/ndis.h +@@ -1,11 +1,11 @@ + /* +- * ndis.h +- * ++ * ndis.h ++ * + * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de> +- * +- * Thanks to the cygwin development team, ++ * ++ * Thanks to the cygwin development team, + * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net> +- * ++ * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may +--- a/drivers/usb/gadget/rndis.c ++++ b/drivers/usb/gadget/rndis.c +@@ -37,9 +37,7 @@ + #include <asm/unaligned.h> + + +-#undef RNDIS_PM +-#undef RNDIS_WAKEUP +-#undef VERBOSE ++#undef VERBOSE_DEBUG + + #include "rndis.h" + +@@ -95,9 +93,6 @@ static const u32 oid_supported_list [] = + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_PHYSICAL_MEDIUM, +-#if 0 +- OID_GEN_RNDIS_CONFIG_PARAMETER, +-#endif + + /* the statistical stuff */ + OID_GEN_XMIT_OK, +@@ -145,7 +140,14 @@ static const u32 oid_supported_list [] = + #endif /* RNDIS_OPTIONAL_STATS */ + + #ifdef RNDIS_PM +- /* PM and wakeup are mandatory for USB: */ ++ /* PM and wakeup are "mandatory" for USB, but the RNDIS specs ++ * don't say what they mean ... and the NDIS specs are often ++ * confusing and/or ambiguous in this context. (That is, more ++ * so than their specs for the other OIDs.) ++ * ++ * FIXME someone who knows what these should do, please ++ * implement them! ++ */ + + /* power management */ + OID_PNP_CAPABILITIES, +@@ -172,6 +174,8 @@ gen_ndis_query_resp (int configNr, u32 O + __le32 *outbuf; + int i, count; + rndis_query_cmplt_type *resp; ++ struct net_device *net; ++ struct net_device_stats *stats; + + if (!r) return -ENOMEM; + resp = (rndis_query_cmplt_type *) r->buf; +@@ -193,6 +197,12 @@ gen_ndis_query_resp (int configNr, u32 O + outbuf = (__le32 *) &resp[1]; + resp->InformationBufferOffset = __constant_cpu_to_le32 (16); + ++ net = rndis_per_dev_params[configNr].dev; ++ if (net->get_stats) ++ stats = net->get_stats(net); ++ else ++ stats = NULL; ++ + switch (OID) { + + /* general oids (table 4-1) */ +@@ -349,11 +359,9 @@ gen_ndis_query_resp (int configNr, u32 O + case OID_GEN_XMIT_OK: + if (rndis_debug > 1) + DBG("%s: OID_GEN_XMIT_OK\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 ( +- rndis_per_dev_params [configNr].stats->tx_packets - +- rndis_per_dev_params [configNr].stats->tx_errors - +- rndis_per_dev_params [configNr].stats->tx_dropped); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->tx_packets ++ - stats->tx_errors - stats->tx_dropped); + retval = 0; + } + break; +@@ -362,11 +370,9 @@ gen_ndis_query_resp (int configNr, u32 O + case OID_GEN_RCV_OK: + if (rndis_debug > 1) + DBG("%s: OID_GEN_RCV_OK\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 ( +- rndis_per_dev_params [configNr].stats->rx_packets - +- rndis_per_dev_params [configNr].stats->rx_errors - +- rndis_per_dev_params [configNr].stats->rx_dropped); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->rx_packets ++ - stats->rx_errors - stats->rx_dropped); + retval = 0; + } + break; +@@ -375,9 +381,8 @@ gen_ndis_query_resp (int configNr, u32 O + case OID_GEN_XMIT_ERROR: + if (rndis_debug > 1) + DBG("%s: OID_GEN_XMIT_ERROR\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->tx_errors); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->tx_errors); + retval = 0; + } + break; +@@ -386,9 +391,8 @@ gen_ndis_query_resp (int configNr, u32 O + case OID_GEN_RCV_ERROR: + if (rndis_debug > 1) + DBG("%s: OID_GEN_RCV_ERROR\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_errors); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->rx_errors); + retval = 0; + } + break; +@@ -396,150 +400,12 @@ gen_ndis_query_resp (int configNr, u32 O + /* mandatory */ + case OID_GEN_RCV_NO_BUFFER: + DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_dropped); +- retval = 0; +- } +- break; +- +-#ifdef RNDIS_OPTIONAL_STATS +- case OID_GEN_DIRECTED_BYTES_XMIT: +- DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__); +- /* +- * Aunt Tilly's size of shoes +- * minus antarctica count of penguins +- * divided by weight of Alpha Centauri +- */ +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 ( +- (rndis_per_dev_params [configNr] +- .stats->tx_packets - +- rndis_per_dev_params [configNr] +- .stats->tx_errors - +- rndis_per_dev_params [configNr] +- .stats->tx_dropped) +- * 123); +- retval = 0; +- } +- break; +- +- case OID_GEN_DIRECTED_FRAMES_XMIT: +- DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__); +- /* dito */ +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 ( +- (rndis_per_dev_params [configNr] +- .stats->tx_packets - +- rndis_per_dev_params [configNr] +- .stats->tx_errors - +- rndis_per_dev_params [configNr] +- .stats->tx_dropped) +- / 123); +- retval = 0; +- } +- break; +- +- case OID_GEN_MULTICAST_BYTES_XMIT: +- DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->multicast*1234); +- retval = 0; +- } +- break; +- +- case OID_GEN_MULTICAST_FRAMES_XMIT: +- DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->multicast); +- retval = 0; +- } +- break; +- +- case OID_GEN_BROADCAST_BYTES_XMIT: +- DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->tx_packets/42*255); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->rx_dropped); + retval = 0; + } + break; + +- case OID_GEN_BROADCAST_FRAMES_XMIT: +- DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->tx_packets/42); +- retval = 0; +- } +- break; +- +- case OID_GEN_DIRECTED_BYTES_RCV: +- DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__); +- *outbuf = __constant_cpu_to_le32 (0); +- retval = 0; +- break; +- +- case OID_GEN_DIRECTED_FRAMES_RCV: +- DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__); +- *outbuf = __constant_cpu_to_le32 (0); +- retval = 0; +- break; +- +- case OID_GEN_MULTICAST_BYTES_RCV: +- DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->multicast * 1111); +- retval = 0; +- } +- break; +- +- case OID_GEN_MULTICAST_FRAMES_RCV: +- DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->multicast); +- retval = 0; +- } +- break; +- +- case OID_GEN_BROADCAST_BYTES_RCV: +- DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_packets/42*255); +- retval = 0; +- } +- break; +- +- case OID_GEN_BROADCAST_FRAMES_RCV: +- DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_packets/42); +- retval = 0; +- } +- break; +- +- case OID_GEN_RCV_CRC_ERROR: +- DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_crc_errors); +- retval = 0; +- } +- break; +- +- case OID_GEN_TRANSMIT_QUEUE_LENGTH: +- DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__); +- *outbuf = __constant_cpu_to_le32 (0); +- retval = 0; +- break; +-#endif /* RNDIS_OPTIONAL_STATS */ +- + /* ieee802.3 OIDs (table 4-3) */ + + /* mandatory */ +@@ -591,9 +457,8 @@ gen_ndis_query_resp (int configNr, u32 O + /* mandatory */ + case OID_802_3_RCV_ERROR_ALIGNMENT: + DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__); +- if (rndis_per_dev_params [configNr].stats) { +- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] +- .stats->rx_frame_errors); ++ if (stats) { ++ *outbuf = cpu_to_le32(stats->rx_frame_errors); + retval = 0; + } + break; +@@ -612,64 +477,6 @@ gen_ndis_query_resp (int configNr, u32 O + retval = 0; + break; + +-#ifdef RNDIS_OPTIONAL_STATS +- case OID_802_3_XMIT_DEFERRED: +- DBG("%s: OID_802_3_XMIT_DEFERRED\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_XMIT_MAX_COLLISIONS: +- DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_RCV_OVERRUN: +- DBG("%s: OID_802_3_RCV_OVERRUN\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_XMIT_UNDERRUN: +- DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_XMIT_HEARTBEAT_FAILURE: +- DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_XMIT_TIMES_CRS_LOST: +- DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__); +- /* TODO */ +- break; +- +- case OID_802_3_XMIT_LATE_COLLISIONS: +- DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__); +- /* TODO */ +- break; +-#endif /* RNDIS_OPTIONAL_STATS */ +- +-#ifdef RNDIS_PM +- /* power management OIDs (table 4-5) */ +- case OID_PNP_CAPABILITIES: +- DBG("%s: OID_PNP_CAPABILITIES\n", __func__); +- +- /* for now, no wakeup capabilities */ +- length = sizeof (struct NDIS_PNP_CAPABILITIES); +- memset(outbuf, 0, length); +- retval = 0; +- break; +- case OID_PNP_QUERY_POWER: +- DBG("%s: OID_PNP_QUERY_POWER D%d\n", __func__, +- get_unaligned_le32(buf) - 1); +- /* only suspend is a real power state, and +- * it can't be entered by OID_PNP_SET_POWER... +- */ +- length = 0; +- retval = 0; +- break; +-#endif +- + default: + pr_warning("%s: query unknown OID 0x%08X\n", + __func__, OID); +@@ -725,9 +532,6 @@ static int gen_ndis_set_resp (u8 configN + * what makes the packet flow start and stop, like + * activating the CDC Ethernet altsetting. + */ +-#ifdef RNDIS_PM +-update_linkstate: +-#endif + retval = 0; + if (*params->filter) { + params->state = RNDIS_DATA_INITIALIZED; +@@ -746,49 +550,6 @@ update_linkstate: + DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__); + retval = 0; + break; +-#if 0 +- case OID_GEN_RNDIS_CONFIG_PARAMETER: +- { +- struct rndis_config_parameter *param; +- param = (struct rndis_config_parameter *) buf; +- DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", +- __func__, +- min(cpu_to_le32(param->ParameterNameLength),80), +- buf + param->ParameterNameOffset); +- retval = 0; +- } +- break; +-#endif +- +-#ifdef RNDIS_PM +- case OID_PNP_SET_POWER: +- /* The only real power state is USB suspend, and RNDIS requests +- * can't enter it; this one isn't really about power. After +- * resuming, Windows forces a reset, and then SET_POWER D0. +- * FIXME ... then things go batty; Windows wedges itself. +- */ +- i = get_unaligned_le32(buf); +- DBG("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1); +- switch (i) { +- case NdisDeviceStateD0: +- *params->filter = params->saved_filter; +- goto update_linkstate; +- case NdisDeviceStateD3: +- case NdisDeviceStateD2: +- case NdisDeviceStateD1: +- params->saved_filter = *params->filter; +- retval = 0; +- break; +- } +- break; +- +-#ifdef RNDIS_WAKEUP +- // no wakeup support advertised, so wakeup OIDs always fail: +- // - OID_PNP_ENABLE_WAKE_UP +- // - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN +-#endif +- +-#endif /* RNDIS_PM */ + + default: + pr_warning("%s: set unknown OID 0x%08X, size %d\n", +@@ -806,8 +567,10 @@ static int rndis_init_response (int conf + { + rndis_init_cmplt_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + +- if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; ++ if (!params->dev) ++ return -ENOTSUPP; + + r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); + if (!r) +@@ -825,7 +588,7 @@ static int rndis_init_response (int conf + resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3); + resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1); + resp->MaxTransferSize = cpu_to_le32 ( +- rndis_per_dev_params [configNr].dev->mtu ++ params->dev->mtu + + sizeof (struct ethhdr) + + sizeof (struct rndis_packet_msg_type) + + 22); +@@ -833,10 +596,7 @@ static int rndis_init_response (int conf + resp->AFListOffset = __constant_cpu_to_le32 (0); + resp->AFListSize = __constant_cpu_to_le32 (0); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); +- ++ params->resp_avail(params->v); + return 0; + } + +@@ -844,9 +604,11 @@ static int rndis_query_response (int con + { + rndis_query_cmplt_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + + // DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); +- if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; ++ if (!params->dev) ++ return -ENOTSUPP; + + /* + * we need more memory: +@@ -877,9 +639,7 @@ static int rndis_query_response (int con + } else + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); ++ params->resp_avail(params->v); + return 0; + } + +@@ -888,6 +648,7 @@ static int rndis_set_response (int confi + u32 BufLength, BufOffset; + rndis_set_cmplt_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + + r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); + if (!r) +@@ -897,7 +658,7 @@ static int rndis_set_response (int confi + BufLength = le32_to_cpu (buf->InformationBufferLength); + BufOffset = le32_to_cpu (buf->InformationBufferOffset); + +-#ifdef VERBOSE ++#ifdef VERBOSE_DEBUG + DBG("%s: Length: %d\n", __func__, BufLength); + DBG("%s: Offset: %d\n", __func__, BufOffset); + DBG("%s: InfoBuffer: ", __func__); +@@ -918,10 +679,7 @@ static int rndis_set_response (int confi + else + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); +- ++ params->resp_avail(params->v); + return 0; + } + +@@ -929,6 +687,7 @@ static int rndis_reset_response (int con + { + rndis_reset_cmplt_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + + r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); + if (!r) +@@ -941,10 +700,7 @@ static int rndis_reset_response (int con + /* resent information */ + resp->AddressingReset = __constant_cpu_to_le32 (1); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); +- ++ params->resp_avail(params->v); + return 0; + } + +@@ -953,6 +709,7 @@ static int rndis_keepalive_response (int + { + rndis_keepalive_cmplt_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + + /* host "should" check only in RNDIS_DATA_INITIALIZED state */ + +@@ -967,10 +724,7 @@ static int rndis_keepalive_response (int + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); +- ++ params->resp_avail(params->v); + return 0; + } + +@@ -982,8 +736,9 @@ static int rndis_indicate_status_msg (in + { + rndis_indicate_status_msg_type *resp; + rndis_resp_t *r; ++ struct rndis_params *params = rndis_per_dev_params + configNr; + +- if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED) ++ if (params->state == RNDIS_UNINITIALIZED) + return -ENOTSUPP; + + r = rndis_add_response (configNr, +@@ -999,9 +754,7 @@ static int rndis_indicate_status_msg (in + resp->StatusBufferLength = __constant_cpu_to_le32 (0); + resp->StatusBufferOffset = __constant_cpu_to_le32 (0); + +- if (rndis_per_dev_params [configNr].ack) +- rndis_per_dev_params [configNr].ack ( +- rndis_per_dev_params [configNr].dev); ++ params->resp_avail(params->v); + return 0; + } + +@@ -1028,7 +781,6 @@ void rndis_uninit (int configNr) + + if (configNr >= RNDIS_MAX_CONFIGS) + return; +- rndis_per_dev_params [configNr].used = 0; + rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED; + + /* drain the response queue */ +@@ -1141,21 +893,25 @@ int rndis_msg_parser (u8 configNr, u8 *b + return -ENOTSUPP; + } + +-int rndis_register (int (* rndis_control_ack) (struct net_device *)) ++int rndis_register(void (*resp_avail)(void *v), void *v) + { + u8 i; + ++ if (!resp_avail) ++ return -EINVAL; ++ + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + if (!rndis_per_dev_params [i].used) { + rndis_per_dev_params [i].used = 1; +- rndis_per_dev_params [i].ack = rndis_control_ack; ++ rndis_per_dev_params [i].resp_avail = resp_avail; ++ rndis_per_dev_params [i].v = v; + DBG("%s: configNr = %d\n", __func__, i); + return i; + } + } + DBG("failed\n"); + +- return -1; ++ return -ENODEV; + } + + void rndis_deregister (int configNr) +@@ -1168,16 +924,14 @@ void rndis_deregister (int configNr) + return; + } + +-int rndis_set_param_dev (u8 configNr, struct net_device *dev, +- struct net_device_stats *stats, +- u16 *cdc_filter) ++int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) + { + DBG("%s:\n", __func__ ); +- if (!dev || !stats) return -1; ++ if (!dev) ++ return -EINVAL; + if (configNr >= RNDIS_MAX_CONFIGS) return -1; + + rndis_per_dev_params [configNr].dev = dev; +- rndis_per_dev_params [configNr].stats = stats; + rndis_per_dev_params [configNr].filter = cdc_filter; + + return 0; +--- a/drivers/usb/gadget/rndis.h ++++ b/drivers/usb/gadget/rndis.h +@@ -233,20 +233,19 @@ typedef struct rndis_params + const u8 *host_mac; + u16 *filter; + struct net_device *dev; +- struct net_device_stats *stats; + + u32 vendorID; + const char *vendorDescr; +- int (*ack) (struct net_device *); ++ void (*resp_avail)(void *v); ++ void *v; + struct list_head resp_queue; + } rndis_params; + + /* RNDIS Message parser and other useless functions */ + int rndis_msg_parser (u8 configNr, u8 *buf); +-int rndis_register (int (*rndis_control_ack) (struct net_device *)); ++int rndis_register(void (*resp_avail)(void *v), void *v); + void rndis_deregister (int configNr); + int rndis_set_param_dev (u8 configNr, struct net_device *dev, +- struct net_device_stats *stats, + u16 *cdc_filter); + int rndis_set_param_vendor (u8 configNr, u32 vendorID, + const char *vendorDescr); diff --git a/usb/usb-serial-gadget-cdc-acm-function-driver.patch b/usb/usb-gadget-serial-split-out-cdc-acm-function.patch index f9b59ca1746408..135d7e168152bc 100644 --- a/usb/usb-serial-gadget-cdc-acm-function-driver.patch +++ b/usb/usb-gadget-serial-split-out-cdc-acm-function.patch @@ -1,13 +1,15 @@ -From david-b@pacbell.net Fri Jun 6 15:11:23 2008 +From david-b@pacbell.net Mon Jun 30 11:10:27 2008 From: David Brownell <david-b@pacbell.net> -Date: Tue, 20 May 2008 11:10:52 -0700 -Subject: usb serial gadget: cdc acm function driver +Date: Thu, 19 Jun 2008 18:18:40 -0700 +Subject: usb gadget serial: split out CDC ACM function To: linux-usb@vger.kernel.org -Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> -Message-ID: <200805201110.52928.david-b@pacbell.net> +Cc: Al Borchers <alborchers@steinerpoint.com>, Greg KH <greg@kroah.com> +Message-ID: <200806191818.41035.david-b@pacbell.net> Content-Disposition: inline +From: David Brownell <dbrownell@users.sourceforge.net> + Split out CDC ACM parts of "gadget serial" to a "function driver". Some key structural differences from the previous ACM support, shared with with the generic serial function (next patch): @@ -32,9 +34,9 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- Documentation/DocBook/gadget.tmpl | 2 drivers/usb/gadget/Makefile | 2 - drivers/usb/gadget/f_acm.c | 587 ++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/f_acm.c | 589 ++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/u_serial.h | 10 - 4 files changed, 595 insertions(+), 6 deletions(-) + 4 files changed, 597 insertions(+), 6 deletions(-) --- a/Documentation/DocBook/gadget.tmpl +++ b/Documentation/DocBook/gadget.tmpl @@ -49,12 +51,13 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- /dev/null +++ b/drivers/usb/gadget/f_acm.c -@@ -0,0 +1,587 @@ +@@ -0,0 +1,589 @@ +/* -+ * f_acm.c -- USB CDC ACM function driver ++ * f_acm.c -- USB CDC serial (ACM) function driver + * + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 by David Brownell ++ * Copyright (C) 2008 by Nokia Corporation + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, @@ -64,7 +67,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> -+#include <linux/utsname.h> +#include <linux/device.h> + +#include "u_serial.h" @@ -80,6 +82,12 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + * interop solution for serial-port type connections. The control + * models are often not necessary, and in any case don't do much in + * this bare-bones implementation. ++ * ++ * Note that even MS-Windows has some support for ACM. However, that ++ * support is somewhat broken because when you use ACM in a composite ++ * device, having multiple interfaces confuses the poor OS. It doesn't ++ * seem to understand CDC Union descriptors. The new "association" ++ * descriptors (roughly equivalent to CDC Unions) may sometimes help. + */ + +struct acm_ep_descs { @@ -257,7 +265,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +/* static strings, in UTF-8 */ +static struct usb_string acm_string_defs[] = { -+ [ACM_CTRL_IDX].s = "CDC ACM Control", ++ [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", + [ACM_DATA_IDX].s = "CDC ACM Data", + { /* ZEROES END LIST */ }, +}; @@ -395,10 +403,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + /* we know alt == 0, so this is an activation or a reset */ + -+ /* REVISIT hardware on PXA handles SET_INTERFACE; -+ * this is probably doing resets wrong ... -+ */ -+ + if (intf == acm->ctrl_id) { + /* REVISIT this may need more work when we start to + * send notifications ... @@ -650,7 +654,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> g_file_storage-objs := file_storage.o usbstring.o config.o \ --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h -@@ -1,10 +1,7 @@ +@@ -12,10 +12,7 @@ #ifndef __U_SERIAL_H #define __U_SERIAL_H @@ -662,7 +666,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> #include <linux/usb/cdc.h> /* -@@ -21,7 +18,7 @@ +@@ -32,7 +29,7 @@ * REVISIT someday, allow multiplexing several TTYs over these endpoints. */ struct gserial { @@ -671,7 +675,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> /* port is managed by gserial_{connect,disconnect} */ struct gs_port *ioport; -@@ -48,4 +45,7 @@ void gserial_cleanup(void); +@@ -59,4 +56,7 @@ void gserial_cleanup(void); int gserial_connect(struct gserial *, u8 port_num); void gserial_disconnect(struct gserial *); diff --git a/usb/usb-serial-gadget-generic-serial-function-driver.patch b/usb/usb-gadget-serial-split-out-generic-serial-function.patch index 3aa638262d159e..4d59b72074ae08 100644 --- a/usb/usb-serial-gadget-generic-serial-function-driver.patch +++ b/usb/usb-gadget-serial-split-out-generic-serial-function.patch @@ -1,13 +1,15 @@ -From david-b@pacbell.net Fri Jun 6 15:11:41 2008 +From david-b@pacbell.net Mon Jun 30 11:10:42 2008 From: David Brownell <david-b@pacbell.net> -Date: Tue, 20 May 2008 11:11:31 -0700 -Subject: usb serial gadget: generic serial function driver +Date: Thu, 19 Jun 2008 18:18:50 -0700 +Subject: usb gadget serial: split out generic serial function To: linux-usb@vger.kernel.org -Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> -Message-ID: <200805201111.31815.david-b@pacbell.net> +Cc: Al Borchers <alborchers@steinerpoint.com>, Greg KH <greg@kroah.com> +Message-ID: <200806191818.50765.david-b@pacbell.net> Content-Disposition: inline +From: David Brownell <dbrownell@users.sourceforge.net> + Split out the generic serial support into a "function driver". This closely mimics the ACM support, but with a MUCH simpler control model. @@ -17,9 +19,9 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- Documentation/DocBook/gadget.tmpl | 1 drivers/usb/gadget/Makefile | 2 - drivers/usb/gadget/f_serial.c | 298 ++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/f_serial.c | 296 ++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/u_serial.h | 1 - 4 files changed, 301 insertions(+), 1 deletion(-) + 4 files changed, 299 insertions(+), 1 deletion(-) --- a/Documentation/DocBook/gadget.tmpl +++ b/Documentation/DocBook/gadget.tmpl @@ -33,12 +35,13 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- /dev/null +++ b/drivers/usb/gadget/f_serial.c -@@ -0,0 +1,298 @@ +@@ -0,0 +1,296 @@ +/* -+ * f_serial.c - generic USB serial function ++ * f_serial.c - generic USB serial function driver + * + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 by David Brownell ++ * Copyright (C) 2008 by Nokia Corporation + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, @@ -169,9 +172,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + /* we know alt == 0, so this is an activation or a reset */ + -+ /* REVISIT hardware on PXA handles SET_INTERFACE; -+ * this is probably doing resets wrong ... -+ */ + if (gser->port.in->driver_data) { + DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); + gserial_disconnect(&gser->port); @@ -345,7 +345,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> g_file_storage-objs := file_storage.o usbstring.o config.o \ --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h -@@ -47,5 +47,6 @@ void gserial_disconnect(struct gserial * +@@ -58,5 +58,6 @@ void gserial_disconnect(struct gserial * /* functions are bound to configurations by a config or gadget driver */ int acm_bind_config(struct usb_configuration *c, u8 port_num); diff --git a/usb/usb-serial-gadget-use-updated-framework.patch b/usb/usb-gadget-serial-use-composite-gadget-framework.patch index e92e015abf1e6e..bc1a7dc5a53d23 100644 --- a/usb/usb-serial-gadget-use-updated-framework.patch +++ b/usb/usb-gadget-serial-use-composite-gadget-framework.patch @@ -1,36 +1,38 @@ -From david-b@pacbell.net Fri Jun 6 15:11:53 2008 +From david-b@pacbell.net Mon Jun 30 11:10:56 2008 From: David Brownell <david-b@pacbell.net> -Date: Tue, 20 May 2008 11:21:06 -0700 -Subject: usb serial gadget: use updated framework +Date: Thu, 19 Jun 2008 18:19:03 -0700 +Subject: usb gadget serial: use composite gadget framework To: linux-usb@vger.kernel.org -Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> -Message-ID: <200805201121.06576.david-b@pacbell.net> +Cc: Al Borchers <alborchers@steinerpoint.com>, Greg KH <greg@kroah.com> +Message-ID: <200806191819.03229.david-b@pacbell.net> Content-Disposition: inline +From: David Brownell <dbrownell@users.sourceforge.net> + This switches the serial gadget over to using the new "function" versions of the serial port interfacing code. The remaining code -in this module is quite small... - -Note one new feature added by this patch: a new module parameter -saying how many instances to create, defaulting to "one". So for -hardware which allows it, you can "modprobe g_serial n_ports=2" -(or more) to get two ACM ports. - -Also, ACM is the (more interoperable) default now, with the use_acm -parameter switched to be a boolean. +in the main source file is quite small... Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- - drivers/usb/gadget/serial.c | 1073 +++++------------------------------------- + drivers/usb/gadget/serial.c | 1075 +++++------------------------------------- drivers/usb/gadget/u_serial.h | 5 - 2 files changed, 144 insertions(+), 934 deletions(-) + 2 files changed, 146 insertions(+), 934 deletions(-) --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c -@@ -21,314 +21,78 @@ +@@ -3,6 +3,7 @@ + * + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 by David Brownell ++ * Copyright (C) 2008 by Nokia Corporation + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, +@@ -21,314 +22,79 @@ /* Defines */ @@ -178,6 +180,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> - .iProduct = GS_PRODUCT_STR_ID, - .bNumConfigurations = GS_NUM_CONFIGS, + /* .idProduct = f(use_acm) */ ++ /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + .bNumConfigurations = 1, @@ -384,7 +387,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> /*-------------------------------------------------------------------------*/ /* Module */ -@@ -337,699 +101,150 @@ MODULE_AUTHOR("Al Borchers"); +@@ -337,699 +103,150 @@ MODULE_AUTHOR("Al Borchers"); MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL"); @@ -722,11 +725,11 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> - wValue, wIndex, wLength); - break; - } -+ serial_config_driver.iConfiguration = status; - +- - return ret; -} -- ++ serial_config_driver.iConfiguration = status; + -static void gs_setup_complete_set_line_coding(struct usb_ep *ep, - struct usb_request *req) -{ @@ -1098,14 +1101,8 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> - function = high_speed - ? gs_bulk_highspeed_function - : gs_bulk_fullspeed_function; -+ serial_config_driver.label = "Generic Serial config"; -+ serial_config_driver.bConfigurationValue = 1; -+ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; -+ device_desc.idProduct = -+ __constant_cpu_to_le16(GS_PRODUCT_ID); - } -+ strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; - +- } +- - /* for now, don't advertise srp-only devices */ - if (!is_otg) - function++; @@ -1142,8 +1139,14 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> - usb_ep_free_request(ep, req); - return NULL; - } -- } -- ++ serial_config_driver.label = "Generic Serial config"; ++ serial_config_driver.bConfigurationValue = 1; ++ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; ++ device_desc.idProduct = ++ __constant_cpu_to_le16(GS_PRODUCT_ID); + } ++ strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; + - return req; + return usb_composite_register(&gserial_driver); } @@ -1191,7 +1194,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +module_exit(cleanup); --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h -@@ -30,11 +30,6 @@ struct gserial { +@@ -41,11 +41,6 @@ struct gserial { /* REVISIT avoid this CDC-ACM support harder ... */ struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */ diff --git a/usb/usb-serial-gadget-modular-tty-glue.patch b/usb/usb-gadget-split-out-serial-core.patch index 0faeef71d49eb1..34d8313fa33388 100644 --- a/usb/usb-serial-gadget-modular-tty-glue.patch +++ b/usb/usb-gadget-split-out-serial-core.patch @@ -1,21 +1,23 @@ -From david-b@pacbell.net Mon May 19 17:09:43 2008 +From david-b@pacbell.net Mon Jun 30 11:08:16 2008 From: David Brownell <david-b@pacbell.net> -Date: Fri, 16 May 2008 10:42:09 -0700 -Subject: usb serial gadget: modular tty glue +Date: Thu, 19 Jun 2008 17:51:44 -0700 +Subject: usb gadget: split out serial core To: linux-usb@vger.kernel.org -Cc: Greg KH <greg@kroah.com>, Alan Cox <alan@lxorguk.ukuu.org.uk> -Message-ID: <200805161042.09655.david-b@pacbell.net> +Cc: Al Borchers <alborchers@steinerpoint.com>, Greg KH <greg@kroah.com> +Message-ID: <200806191751.44673.david-b@pacbell.net> Content-Disposition: inline +From: David Brownell <dbrownell@users.sourceforge.net> + This abstracts the "gadget serial" driver TTY glue into a separate -module, cleaning it up and disentangling it from connection state. +component, cleaning it up and disentangling it from connection state. It also changed some behaviors for the better: - Stops using "experimental" major #127, and switches over to having the TTY layer allocate the dev_t numbers. - + - Provides /sys/class/tty/ttyGS* nodes, thus mdev/udev support. (Note "mdev" hotplug bug in Busybox v1.7.2: /dev/ttyGS0 will be a *block* device without CONFIG_SYSFS_DEPRECATED_V2.) @@ -44,18 +46,19 @@ Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- - drivers/usb/gadget/u_serial.c | 1270 ++++++++++++++++++++++++++++++++++++++++++ - drivers/usb/gadget/u_serial.h | 51 + - 2 files changed, 1321 insertions(+) + drivers/usb/gadget/u_serial.c | 1243 ++++++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/u_serial.h | 62 ++ + 2 files changed, 1305 insertions(+) --- /dev/null +++ b/drivers/usb/gadget/u_serial.c -@@ -0,0 +1,1270 @@ +@@ -0,0 +1,1243 @@ +/* + * u_serial.c - utilities for USB gadget "serial port"/TTY support + * + * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) + * Copyright (C) 2008 David Brownell ++ * Copyright (C) 2008 by Nokia Corporation + * + * This code also borrows from usbserial.c, which is + * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) @@ -67,6 +70,8 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + * either version 2 of that License or (at your option) any later version. + */ + ++/* #define VERBOSE_DEBUG */ ++ +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/device.h> @@ -78,7 +83,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +/* -+ * This module encapsulates the TTY layer glue needed to provide basic ++ * This component encapsulates the TTY layer glue needed to provide basic + * "serial port" functionality through the USB gadget stack. Each such + * port is exposed through a /dev/ttyGS* node. + * @@ -93,7 +98,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + * login application. In one case that might use CDC ACM interface 0, + * while another configuration might use interface 3 for that. The + * work to handle that (including descriptor management) is not part -+ * of this module. ++ * of this component. + * + * Configurations may expose more than one TTY port. For example, if + * ttyGS0 provides login service, then ttyGS1 might provide dialer access @@ -138,7 +143,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + * (and thus for each /dev/ node). + */ +struct gs_port { -+ spinlock_t port_lock; ++ spinlock_t port_lock; /* guard port_* access */ + + struct gserial *port_usb; + struct tty_struct *port_tty; @@ -196,7 +201,8 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + return -ENOMEM; + + gb->buf_size = size; -+ gb->buf_get = gb->buf_put = gb->buf_buf; ++ gb->buf_put = gb->buf_buf; ++ gb->buf_get = gb->buf_buf; + + return 0; +} @@ -971,32 +977,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + port->port_num, started); +} + -+#if 0 -+ -+static void gs_break(struct tty_struct *tty, int break_state) -+{ -+} -+ -+static int gs_ioctl(struct tty_struct *tty, struct file *file, -+ unsigned cmd, unsigned long arg) -+{ -+ struct gs_port *port = tty->driver_data; -+ -+ pr_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n", -+ port->port_num, tty, file, cmd, arg); -+ -+ /* handle ioctls */ -+ -+ /* could not handle ioctl */ -+ return -ENOIOCTLCMD; -+} -+ -+static void gs_set_termios(struct tty_struct *tty, struct ktermios *old) -+{ -+} -+ -+#endif -+ +static const struct tty_operations gs_tty_ops = { + .open = gs_open, + .close = gs_close, @@ -1006,11 +986,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + .write_room = gs_write_room, + .chars_in_buffer = gs_chars_in_buffer, + .unthrottle = gs_unthrottle, -+#if 0 -+ .break_ctl = gs_break, -+ .ioctl = gs_ioctl, -+ .set_termios = gs_set_termios, -+#endif +}; + +/*-------------------------------------------------------------------------*/ @@ -1323,7 +1298,18 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +} --- /dev/null +++ b/drivers/usb/gadget/u_serial.h -@@ -0,0 +1,51 @@ +@@ -0,0 +1,62 @@ ++/* ++ * u_serial.h - interface to USB gadget "serial port"/TTY utilities ++ * ++ * Copyright (C) 2008 David Brownell ++ * Copyright (C) 2008 by Nokia Corporation ++ * ++ * This software is distributed under the terms of the GNU General ++ * Public License ("GPL") as published by the Free Software Foundation, ++ * either version 2 of that License or (at your option) any later version. ++ */ ++ +#ifndef __U_SERIAL_H +#define __U_SERIAL_H + @@ -1337,12 +1323,12 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + * One non-multiplexed "serial" I/O port ... there can be several of these + * on any given USB peripheral device, if it provides enough endpoints. + * -+ * The "u_serial" utility module exists to do one thing: manage TTY style -+ * I/O using the USB peripheral endpoints listed here, including hookups -+ * to sysfs and /dev for each logical "tty" device. ++ * The "u_serial" utility component exists to do one thing: manage TTY ++ * style I/O using the USB peripheral endpoints listed here, including ++ * hookups to sysfs and /dev for each logical "tty" device. + * + * REVISIT need TTY --> USB event flow too, so ACM can report open/close -+ * as carrier detect events. There's other ACM state too... ++ * as carrier detect events. Model after ECM. There's more ACM state too. + * + * REVISIT someday, allow multiplexing several TTYs over these endpoints. + */ diff --git a/usb/usb-serial-gadget-use-new-tty-glue.patch b/usb/usb-gadget-use-new-serial-core.patch index d5f2c60f1c5cf9..e2d2534a0b20a8 100644 --- a/usb/usb-serial-gadget-use-new-tty-glue.patch +++ b/usb/usb-gadget-use-new-serial-core.patch @@ -1,13 +1,15 @@ -From david-b@pacbell.net Mon May 19 17:10:00 2008 +From david-b@pacbell.net Mon Jun 30 11:08:41 2008 From: David Brownell <david-b@pacbell.net> -Date: Fri, 16 May 2008 10:44:09 -0700 -Subject: usb serial gadget: use new tty glue +Date: Thu, 19 Jun 2008 17:52:07 -0700 +Subject: usb gadget: use new serial core To: linux-usb@vger.kernel.org -Cc: Greg KH <greg@kroah.com>, Alan Cox <alan@lxorguk.ukuu.org.uk> -Message-ID: <200805161044.09956.david-b@pacbell.net> +Cc: Al Borchers <alborchers@steinerpoint.com>, Greg KH <greg@kroah.com> +Message-ID: <200806191752.07416.david-b@pacbell.net> Content-Disposition: inline +From: David Brownell <dbrownell@users.sourceforge.net> + Teach "gadget serial" to use the new abstracted (and bugfixed) TTY glue, and remove all the orignal tangled-up code. Update the documentation accordingly. This is a net object code shrink and cleanup; it should diff --git a/usb/usb-gadget-zero-loopback-function-driver.patch b/usb/usb-gadget-zero-split-out-loopback-config.patch index ee2af7a0a1d4a4..25927d4dc130fb 100644 --- a/usb/usb-gadget-zero-loopback-function-driver.patch +++ b/usb/usb-gadget-zero-split-out-loopback-config.patch @@ -1,13 +1,15 @@ -From david-b@pacbell.net Fri Jun 6 15:10:56 2008 +From david-b@pacbell.net Mon Jun 30 11:09:56 2008 From: David Brownell <david-b@pacbell.net> -Date: Tue, 20 May 2008 11:07:08 -0700 -Subject: usb gadget zero: loopback function driver +Date: Thu, 19 Jun 2008 17:55:35 -0700 +Subject: usb gadget zero: split out loopback config To: linux-usb@vger.kernel.org -Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> -Message-ID: <200805201107.08935.david-b@pacbell.net> +Cc: Greg KH <greg@kroah.com> +Message-ID: <200806191755.35886.david-b@pacbell.net> Content-Disposition: inline +From: David Brownell <dbrownell@users.sourceforge.net> + This splits the gadget zero "loopback" configuration into a standalone "configuration driver", building on the composite gadget framework code. It doesn't yet pull the original code out of gadget zero or update how @@ -17,18 +19,18 @@ Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- - drivers/usb/gadget/f_loopback.c | 383 ++++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/f_loopback.c | 381 ++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/g_zero.h | 2 - 2 files changed, 385 insertions(+) + 2 files changed, 383 insertions(+) --- /dev/null +++ b/drivers/usb/gadget/f_loopback.c -@@ -0,0 +1,383 @@ +@@ -0,0 +1,381 @@ +/* + * f_loopback.c - USB peripheral loopback configuration driver + * + * Copyright (C) 2003-2008 David Brownell -+ * All rights reserved. ++ * Copyright (C) 2008 by Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by @@ -397,8 +399,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + loopback_intf.iInterface = id; + loopback_driver.iConfiguration = id; + -+ /* FIXME pass in bConfigurationValue and OTG stuff */ -+ + /* support OTG systems */ + if (gadget_is_otg(cdev->gadget)) { + loopback_driver.descriptors = otg_desc; @@ -414,7 +414,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> /* global state */ +extern unsigned buflen; - extern const struct usb_descriptor_header **otg_desc; + extern const struct usb_descriptor_header *otg_desc[]; /* common utilities */ @@ -19,5 +20,6 @@ void disable_endpoints(struct usb_compos diff --git a/usb/usb-gadget-zero-sourcesink-config-driver.patch b/usb/usb-gadget-zero-split-out-source-sink-config.patch index 970deb13e6df92..627ef6d5ad36aa 100644 --- a/usb/usb-gadget-zero-sourcesink-config-driver.patch +++ b/usb/usb-gadget-zero-split-out-source-sink-config.patch @@ -1,38 +1,41 @@ -From david-b@pacbell.net Fri Jun 6 15:10:42 2008 +From david-b@pacbell.net Mon Jun 30 11:09:40 2008 From: David Brownell <david-b@pacbell.net> -Date: Tue, 20 May 2008 11:06:24 -0700 -Subject: usb gadget zero: sourcesink config driver +Date: Thu, 19 Jun 2008 17:55:23 -0700 +Subject: usb gadget zero: split out source/sink config To: linux-usb@vger.kernel.org -Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> -Message-ID: <200805201106.24630.david-b@pacbell.net> +Cc: Greg KH <greg@kroah.com> +Message-ID: <200806191755.23212.david-b@pacbell.net> Content-Disposition: inline +From: David Brownell <dbrownell@users.sourceforge.net> + This splits the gadget zero "source/sink" configuration into a standalone "configuration driver", building on the composite gadget framework code. It doesn't yet pull the original code out of gadget zero or update how that driver is built. Neither this, nor its sibling "loopback" configuration, is a function -driver that can be combined with other functions. However the code does -become simpler because of this conversion, so it's a net win. +driver that can be combined with other functions. (The host "usbtest" +driver wouldn't know how to deal with that!) However the code becomes +simpler because of this conversion, so it's a net win. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- - drivers/usb/gadget/f_sourcesink.c | 589 ++++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/f_sourcesink.c | 587 ++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/g_zero.h | 23 + - 2 files changed, 612 insertions(+) + 2 files changed, 610 insertions(+) --- /dev/null +++ b/drivers/usb/gadget/f_sourcesink.c -@@ -0,0 +1,589 @@ +@@ -0,0 +1,587 @@ +/* + * f_sourcesink.c - USB peripheral source/sink configuration driver + * + * Copyright (C) 2003-2008 David Brownell -+ * All rights reserved. ++ * Copyright (C) 2008 by Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by @@ -603,8 +606,6 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + source_sink_intf.iInterface = id; + sourcesink_driver.iConfiguration = id; + -+ /* FIXME pass in bConfigurationValue and OTG stuff */ -+ + /* support autoresume for remote wakeup testing */ + if (autoresume) + sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; @@ -631,7 +632,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +#include <linux/usb/composite.h> + +/* global state */ -+extern const struct usb_descriptor_header **otg_desc; ++extern const struct usb_descriptor_header *otg_desc[]; + +/* common utilities */ +struct usb_request *alloc_ep_req(struct usb_ep *ep); diff --git a/usb/usb-gadget-zero-use-updated-framework.patch b/usb/usb-gadget-zero-use-composite-gadget-framework.patch index f0bbcc562c2f05..031cdca32ff598 100644 --- a/usb/usb-gadget-zero-use-updated-framework.patch +++ b/usb/usb-gadget-zero-use-composite-gadget-framework.patch @@ -1,13 +1,15 @@ -From david-b@pacbell.net Fri Jun 6 15:11:08 2008 +From david-b@pacbell.net Mon Jun 30 11:10:10 2008 From: David Brownell <david-b@pacbell.net> -Date: Tue, 20 May 2008 11:07:49 -0700 -Subject: usb gadget zero: use updated framework +Date: Thu, 19 Jun 2008 18:18:27 -0700 +Subject: usb gadget zero: use composite gadget framework To: linux-usb@vger.kernel.org -Cc: Tony Lindgren <tony@atomide.com>, Felipe Balbi <felipebalbi@users.sourceforge.net>, Al Borchers <alborchers@steinerpoint.com> -Message-ID: <200805201107.49807.david-b@pacbell.net> +Cc: Greg KH <greg@kroah.com> +Message-ID: <200806191818.27785.david-b@pacbell.net> Content-Disposition: inline +From: David Brownell <dbrownell@users.sourceforge.net> + Update Gadget Zero to use the more modular versions of the loopback and source/sink configuration drivers which build on the new gadget framework code. @@ -25,8 +27,8 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- drivers/usb/gadget/Makefile | 4 - drivers/usb/gadget/zero.c | 1158 +++----------------------------------------- - 2 files changed, 95 insertions(+), 1067 deletions(-) + drivers/usb/gadget/zero.c | 1162 +++----------------------------------------- + 2 files changed, 97 insertions(+), 1069 deletions(-) --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -43,6 +45,17 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c +@@ -1,8 +1,8 @@ + /* + * zero.c -- Gadget Zero, for USB development + * +- * Copyright (C) 2003-2007 David Brownell +- * All rights reserved. ++ * Copyright (C) 2003-2008 David Brownell ++ * Copyright (C) 2008 by Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by @@ -30,12 +30,7 @@ * * It supports two similar configurations. One sinks whatever the usb host @@ -324,7 +337,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> }; -static const struct usb_descriptor_header *hs_source_sink_function[] = { -+const struct usb_descriptor_header **otg_desc = { ++const struct usb_descriptor_header *otg_desc[] = { (struct usb_descriptor_header *) &otg_descriptor, - (struct usb_descriptor_header *) &source_sink_intf, - (struct usb_descriptor_header *) &hs_source_desc, |
