// #define DEBUG 1
// #define VERBOSE
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
-#include <linux/usb_ch9.h>
-#include <linux/usb_cdc.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
#include <linux/usb_gadget.h>
#include <linux/random.h>
*
* There's some hardware that can't talk CDC. We make that hardware
* implement a "minimalist" vendor-agnostic CDC core: same framing, but
- * link-level setup only requires activating the configuration.
- * Linux supports it, but other host operating systems may not.
- * (This is a subset of CDC Ethernet.)
+ * link-level setup only requires activating the configuration. Only the
+ * endpoint descriptors, and product/vendor IDs, are relevant; no control
+ * operations are available. Linux supports it, but other host operating
+ * systems may not. (This is a subset of CDC Ethernet.)
+ *
+ * It turns out that if you add a few descriptors to that "CDC Subset",
+ * (Windows) host side drivers from MCCI can treat it as one submode of
+ * a proprietary scheme called "SAFE" ... without needing to know about
+ * specific product/vendor IDs. So we do that, making it easier to use
+ * those MS-Windows drivers. Those added descriptors make it resemble a
+ * CDC MDLM device, but they don't change device behavior at all. (See
+ * MCCI Engineering report 950198 "SAFE Networking Functions".)
*
* A third option is also in use. Rather than CDC Ethernet, or something
* simpler, Microsoft pushes their own approach: RNDIS. The published
struct usb_ep *in_ep, *out_ep, *status_ep;
const struct usb_endpoint_descriptor
*in, *out, *status;
+
+ spinlock_t req_lock;
struct list_head tx_reqs, rx_reqs;
struct net_device *net;
#define DEV_CONFIG_CDC
#endif
+#ifdef CONFIG_USB_GADGET_S3C2410
+#define DEV_CONFIG_CDC
+#endif
+
#ifdef CONFIG_USB_GADGET_AT91
#define DEV_CONFIG_CDC
#endif
#define DEV_CONFIG_CDC
#endif
-#ifdef CONFIG_USB_GADGET_MUSBHDRC
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
#define DEV_CONFIG_CDC
#endif
#define DEV_CONFIG_SUBSET
#endif
-#ifdef CONFIG_USB_GADGET_S3C2410
-#define DEV_CONFIG_CDC
-#endif
/*-------------------------------------------------------------------------*/
* endpoint. Both have a "data" interface and two bulk endpoints.
* There are also differences in how control requests are handled.
*
- * RNDIS shares a lot with CDC-Ethernet, since it's a variant of
- * the CDC-ACM (modem) spec.
+ * RNDIS shares a lot with CDC-Ethernet, since it's a variant of the
+ * CDC-ACM (modem) spec. Unfortunately MSFT's RNDIS driver is buggy; it
+ * may hang or oops. Since bugfixes (or accurate specs, letting Linux
+ * work around those bugs) are unlikely to ever come from MSFT, you may
+ * wish to avoid using RNDIS.
+ *
+ * MCCI offers an alternative to RNDIS if you need to connect to Windows
+ * but have hardware that can't support CDC Ethernet. We add descriptors
+ * to present the CDC Subset as a (nonconformant) CDC MDLM variant called
+ * "SAFE". That borrows from both CDC Ethernet and CDC MDLM. You can
+ * get those drivers from MCCI, or bundled with various products.
*/
#ifdef DEV_CONFIG_CDC
};
#endif
-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
-
static const struct usb_cdc_header_desc header_desc = {
.bLength = sizeof header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bcdCDC = __constant_cpu_to_le16 (0x0110),
};
+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+
static const struct usb_cdc_union_desc union_desc = {
.bLength = sizeof union_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
#endif
-#ifdef DEV_CONFIG_CDC
+#ifndef DEV_CONFIG_CDC
+
+/* "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.
+ */
+
+static const struct usb_cdc_mdlm_desc mdlm_desc = {
+ .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 const u8 mdlm_detail_desc[] = {
+ 6,
+ USB_DT_CS_INTERFACE,
+ USB_CDC_MDLM_DETAIL_TYPE,
+
+ 0, /* "SAFE" */
+ 0, /* network control capabilities (none) */
+ 0, /* network data capabilities ("raw" encapsulation) */
+};
+
+#endif
static const struct usb_cdc_ether_desc ether_desc = {
.bLength = sizeof ether_desc,
.bNumberPowerFilters = 0,
};
-#endif
#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
/*
* "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.
*/
static const struct usb_interface_descriptor
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 0,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM,
.bInterfaceProtocol = 0,
.iInterface = STRING_DATA,
};
static inline void __init fs_subset_descriptors(void)
{
#ifdef DEV_CONFIG_SUBSET
+ /* behavior is "CDC Subset"; extra descriptors say "SAFE" */
fs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf;
- fs_eth_function[2] = (struct usb_descriptor_header *) &fs_source_desc;
- fs_eth_function[3] = (struct usb_descriptor_header *) &fs_sink_desc;
- fs_eth_function[4] = NULL;
+ fs_eth_function[2] = (struct usb_descriptor_header *) &header_desc;
+ fs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc;
+ fs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc;
+ fs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc;
+ fs_eth_function[6] = (struct usb_descriptor_header *) &fs_source_desc;
+ fs_eth_function[7] = (struct usb_descriptor_header *) &fs_sink_desc;
+ fs_eth_function[8] = NULL;
#else
fs_eth_function[1] = NULL;
#endif
static inline void __init hs_subset_descriptors(void)
{
#ifdef DEV_CONFIG_SUBSET
+ /* behavior is "CDC Subset"; extra descriptors say "SAFE" */
hs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf;
- hs_eth_function[2] = (struct usb_descriptor_header *) &fs_source_desc;
- hs_eth_function[3] = (struct usb_descriptor_header *) &fs_sink_desc;
- hs_eth_function[4] = NULL;
+ hs_eth_function[2] = (struct usb_descriptor_header *) &header_desc;
+ hs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc;
+ hs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc;
+ hs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc;
+ hs_eth_function[6] = (struct usb_descriptor_header *) &hs_source_desc;
+ hs_eth_function[7] = (struct usb_descriptor_header *) &hs_sink_desc;
+ hs_eth_function[8] = NULL;
#else
hs_eth_function[1] = NULL;
#endif
static char product_desc [40] = DRIVER_DESC;
static char serial_number [20];
-#ifdef DEV_CONFIG_CDC
/* address that the host will use ... usually assigned at random */
static char ethaddr [2 * ETH_ALEN + 1];
-#endif
/* static strings, in UTF-8 */
static struct usb_string strings [] = {
{ STRING_PRODUCT, product_desc, },
{ STRING_SERIALNUMBER, serial_number, },
{ STRING_DATA, "Ethernet Data", },
+ { STRING_ETHADDR, ethaddr, },
#ifdef DEV_CONFIG_CDC
{ STRING_CDC, "CDC Ethernet", },
- { STRING_ETHADDR, ethaddr, },
{ STRING_CONTROL, "CDC Communications Control", },
#endif
#ifdef DEV_CONFIG_SUBSET
}
#endif
- dev->in = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc);
+ dev->in = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
dev->in_ep->driver_data = dev;
- dev->out = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc);
+ dev->out = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
dev->out_ep->driver_data = dev;
/* With CDC, the host isn't allowed to use these two data
*/
if (dev->in) {
usb_ep_disable (dev->in_ep);
+ spin_lock(&dev->req_lock);
while (likely (!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 (dev->in_ep, req);
+ spin_lock(&dev->req_lock);
}
+ spin_unlock(&dev->req_lock);
}
if (dev->out) {
usb_ep_disable (dev->out_ep);
+ spin_lock(&dev->req_lock);
while (likely (!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 (dev->out_ep, req);
+ spin_lock(&dev->req_lock);
}
+ spin_unlock(&dev->req_lock);
}
if (dev->status) {
if (retval) {
DEBUG (dev, "rx submit --> %d\n", retval);
dev_kfree_skb_any (skb);
- spin_lock (&dev->lock);
+ spin_lock(&dev->req_lock);
list_add (&req->list, &dev->rx_reqs);
- spin_unlock (&dev->lock);
+ spin_unlock(&dev->req_lock);
}
return retval;
}
dev_kfree_skb_any (skb);
if (!netif_running (dev->net)) {
clean:
- /* nobody reading rx_reqs, so no dev->lock */
+ spin_lock(&dev->req_lock);
list_add (&req->list, &dev->rx_reqs);
+ spin_unlock(&dev->req_lock);
req = NULL;
}
if (req)
{
int status;
+ spin_lock(&dev->req_lock);
status = prealloc (&dev->tx_reqs, dev->in_ep, n, gfp_flags);
if (status < 0)
goto fail;
status = prealloc (&dev->rx_reqs, dev->out_ep, n, gfp_flags);
if (status < 0)
goto fail;
- return 0;
+ goto done;
fail:
DEBUG (dev, "can't alloc requests\n");
+done:
+ spin_unlock(&dev->req_lock);
return status;
}
unsigned long flags;
/* fill unused rxq slots with some skb */
- spin_lock_irqsave (&dev->lock, flags);
+ 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->lock, flags);
+ 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->lock, flags);
+ spin_lock_irqsave(&dev->req_lock, flags);
}
- spin_unlock_irqrestore (&dev->lock, flags);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
}
-static void eth_work (void *_dev)
+static void eth_work (struct work_struct *work)
{
- struct eth_dev *dev = _dev;
+ 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))
}
dev->stats.tx_packets++;
- spin_lock (&dev->lock);
+ spin_lock(&dev->req_lock);
list_add (&req->list, &dev->tx_reqs);
- spin_unlock (&dev->lock);
+ spin_unlock(&dev->req_lock);
dev_kfree_skb_any (skb);
atomic_dec (&dev->tx_qlen);
if (!eth_is_promisc (dev)) {
u8 *dest = skb->data;
- if (dest [0] & 0x01) {
+ if (is_multicast_ether_addr(dest)) {
u16 type;
/* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
* SET_ETHERNET_MULTICAST_FILTERS requests
*/
- if (memcmp (dest, net->broadcast, ETH_ALEN) == 0)
+ if (is_broadcast_ether_addr(dest))
type = USB_CDC_PACKET_TYPE_BROADCAST;
else
type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
/* ignores USB_CDC_PACKET_TYPE_DIRECTED */
}
- spin_lock_irqsave (&dev->lock, flags);
+ spin_lock_irqsave(&dev->req_lock, flags);
req = container_of (dev->tx_reqs.next, struct usb_request, list);
list_del (&req->list);
if (list_empty (&dev->tx_reqs))
netif_stop_queue (net);
- spin_unlock_irqrestore (&dev->lock, flags);
+ 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.
drop:
dev->stats.tx_dropped++;
dev_kfree_skb_any (skb);
- spin_lock_irqsave (&dev->lock, flags);
+ 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->lock, flags);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
}
return 0;
}
static int rndis_control_ack (struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
- u32 length;
+ int length;
struct usb_request *resp = dev->stat_req;
/* in case RNDIS calls this after disconnect */
}
-static void __exit
+static void /* __init_or_exit */
eth_unbind (struct usb_gadget *gadget)
{
struct eth_dev *dev = get_gadget_data (gadget);
set_gadget_data (gadget, NULL);
}
-static u8 __init nibble (unsigned char c)
+static u8 __devinit nibble (unsigned char c)
{
if (likely (isdigit (c)))
return c - '0';
return 0;
}
-static int __init get_ether_addr(const char *str, u8 *dev_addr)
+static int __devinit get_ether_addr(const char *str, u8 *dev_addr)
{
if (str) {
unsigned i;
return 1;
}
-static int __init
+static int __devinit
eth_bind (struct usb_gadget *gadget)
{
struct eth_dev *dev;
if (gadget_is_pxa (gadget)) {
/* pxa doesn't support altsettings */
cdc = 0;
+ } else if (gadget_is_musbhdrc(gadget)) {
+ /* reduce tx dma overhead by avoiding special cases */
+ zlp = 0;
} else if (gadget_is_sh(gadget)) {
/* sh doesn't support multiple interfaces or configs */
cdc = 0;
return -ENODEV;
}
snprintf (manufacturer, sizeof manufacturer, "%s %s/%s",
- system_utsname.sysname, system_utsname.release,
+ init_utsname()->sysname, init_utsname()->release,
gadget->name);
/* If there's an RNDIS configuration, that's what Windows wants to
"RNDIS/%s", driver_desc);
/* CDC subset ... recognized by Linux since 2.4.10, but Windows
- * drivers aren't widely available.
+ * drivers aren't widely available. (That may be improved by
+ * supporting one submode of the "SAFE" variant of MDLM.)
*/
} else if (!cdc) {
- device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
device_desc.idVendor =
__constant_cpu_to_le16(SIMPLE_VENDOR_NUM);
device_desc.idProduct =
if (!cdc) {
eth_config.bNumInterfaces = 1;
eth_config.iConfiguration = STRING_SUBSET;
+
+ /* use functions to set these up, in case we're built to work
+ * with multiple controllers and must override CDC Ethernet.
+ */
fs_subset_descriptors();
hs_subset_descriptors();
}
return status;
dev = netdev_priv(net);
spin_lock_init (&dev->lock);
- INIT_WORK (&dev->work, eth_work, dev);
+ spin_lock_init (&dev->req_lock);
+ INIT_WORK (&dev->work, eth_work);
INIT_LIST_HEAD (&dev->tx_reqs);
INIT_LIST_HEAD (&dev->rx_reqs);
/* Module params for these addresses should come from ID proms.
* The host side address is used with CDC and RNDIS, and commonly
- * ends up in a persistent config database.
+ * ends up in a persistent config database. It's not clear if
+ * host side code for the SAFE thing cares -- its original BLAN
+ * thing didn't, Sharp never assigned those addresses on Zaurii.
*/
if (get_ether_addr(dev_addr, net->dev_addr))
dev_warn(&gadget->dev,
"using random %s ethernet address\n", "self");
- if (cdc || rndis) {
- if (get_ether_addr(host_addr, dev->host_mac))
- dev_warn(&gadget->dev,
- "using random %s ethernet address\n", "host");
-#ifdef DEV_CONFIG_CDC
- snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
- dev->host_mac [0], dev->host_mac [1],
- dev->host_mac [2], dev->host_mac [3],
- dev->host_mac [4], dev->host_mac [5]);
-#endif
- }
+ if (get_ether_addr(host_addr, dev->host_mac))
+ dev_warn(&gadget->dev,
+ "using random %s ethernet address\n", "host");
+ snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
+ dev->host_mac [0], dev->host_mac [1],
+ dev->host_mac [2], dev->host_mac [3],
+ dev->host_mac [4], dev->host_mac [5]);
if (rndis) {
status = rndis_init();
.function = (char *) driver_desc,
.bind = eth_bind,
- .unbind = __exit_p(eth_unbind),
+ .unbind = eth_unbind,
.setup = eth_setup,
.disconnect = eth_disconnect,