Commit 4299c871 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: ethernet gadget learns about pxa2xx udc

The original code needed some updates to work properly with
the USB Device Controller found in Intel's PXA 2xx processors.

   - SET_INTERFACE is effectively unusable.  So the driver
     now has a "minimalist" mode, with none of the bells and
     whistles of CDC.

   - The config symbol will be CONFIG_USB_PXA2XX, since the
     same controller (modulo errata) is in several processors
     other than the "older" pxa250.

For the record, that "minimalist" mode was the original idea
behind this driver ... implementing CDC was a win for interop
since most non-MSFT host operating systems support it already,
without needing a new driver.

There were also a few other minor updates:

   - Net2280:  queue depth shrank a bit, don't use the same
     endpoint numbers for the IN and OUT sides.  (CATC doesn't
     let filters consider direction, just numbers.)

   - Join the Jihad!  Abolish register_netdev()!!  This just
     nests the gadget-private data structures inside the
     netdev data structure, instead of the other way around.

   - Minor cleanups.
parent 79715ebc
/* /*
* ether.c -- CDC 1.1 Ethernet gadget driver * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
* *
* Copyright (C) 2003 David Brownell * Copyright (C) 2003 David Brownell
* *
...@@ -58,21 +58,23 @@ ...@@ -58,21 +58,23 @@
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* /*
* "Communications Device Class" (CDC) Ethernet class driver * Ethernet gadget driver -- with CDC and non-CDC options
* *
* CDC Ethernet is the standard USB solution for sending Ethernet frames * CDC Ethernet is the standard USB solution for sending Ethernet frames
* using USB. Real hardware tends to use the same framing protocol but look * using USB. Real hardware tends to use the same framing protocol but look
* different for control features. And Microsoft pushes their own approach * different for control features. And Microsoft pushes their own approach
* (RNDIS) instead of the standard. * (RNDIS) instead of the standard.
*
* 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.
*/ */
#define DRIVER_DESC "CDC Ethernet Gadget" #define DRIVER_DESC "Ethernet Gadget"
#define DRIVER_VERSION "29 April 2003" #define DRIVER_VERSION "Bastille Day 2003"
static const char shortname [] = "ether"; static const char shortname [] = "ether";
static const char driver_desc [] = DRIVER_DESC; static const char driver_desc [] = DRIVER_DESC;
static const char control_name [] = "Communications Control";
static const char data_name [] = "CDC Ethernet Data";
#define MIN_PACKET sizeof(struct ethhdr) #define MIN_PACKET sizeof(struct ethhdr)
#define MAX_PACKET ETH_DATA_LEN /* biggest packet we'll rx/tx */ #define MAX_PACKET ETH_DATA_LEN /* biggest packet we'll rx/tx */
...@@ -92,8 +94,7 @@ struct eth_dev { ...@@ -92,8 +94,7 @@ struct eth_dev {
const struct usb_endpoint_descriptor const struct usb_endpoint_descriptor
*in, *out, *status; *in, *out, *status;
struct semaphore mutex; struct net_device *net;
struct net_device net;
struct net_device_stats stats; struct net_device_stats stats;
atomic_t tx_qlen; atomic_t tx_qlen;
...@@ -112,7 +113,7 @@ struct eth_dev { ...@@ -112,7 +113,7 @@ struct eth_dev {
static unsigned qmult = 4; static unsigned qmult = 4;
#define HS_FACTOR 15 #define HS_FACTOR 5
#define qlen(gadget) \ #define qlen(gadget) \
(qmult*((gadget->speed == USB_SPEED_HIGH) ? HS_FACTOR : 1)) (qmult*((gadget->speed == USB_SPEED_HIGH) ? HS_FACTOR : 1))
...@@ -128,7 +129,7 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR); ...@@ -128,7 +129,7 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR);
/* Thanks to NetChip Technologies for donating this product ID. /* Thanks to NetChip Technologies for donating this product ID.
* *
* DO NOT REUSE THESE IDs with any other driver!! Ever!! * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures. * Instead: allocate your own, using normal USB-IF procedures.
*/ */
#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */ #define DRIVER_VENDOR_NUM 0x0525 /* NetChip */
...@@ -158,6 +159,11 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR); ...@@ -158,6 +159,11 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR);
* for some reason doesn't handle full speed bulk maxpacket of 64. * for some reason doesn't handle full speed bulk maxpacket of 64.
*/ */
#define DEV_CONFIG_VALUE 3 /* some hardware cares */
/* #undef on hardware that can't implement CDC */
#define DEV_CONFIG_CDC
/* /*
* NetChip 2280, PCI based. * NetChip 2280, PCI based.
* *
...@@ -172,7 +178,7 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR); ...@@ -172,7 +178,7 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR);
#define DRIVER_VERSION_NUM 0x0101 #define DRIVER_VERSION_NUM 0x0101
#define EP0_MAXPACKET 64 #define EP0_MAXPACKET 64
static const char EP_OUT_NAME [] = "ep-a"; static const char EP_OUT_NAME [] = "ep-a";
#define EP_OUT_NUM 2 #define EP_OUT_NUM 1
static const char EP_IN_NAME [] = "ep-b"; static const char EP_IN_NAME [] = "ep-b";
#define EP_IN_NUM 2 #define EP_IN_NUM 2
static const char EP_STATUS_NAME [] = "ep-f"; static const char EP_STATUS_NAME [] = "ep-f";
...@@ -194,22 +200,21 @@ static inline void hw_optimize (struct usb_gadget *gadget) ...@@ -194,22 +200,21 @@ static inline void hw_optimize (struct usb_gadget *gadget)
#endif #endif
/* /*
* PXA-250 UDC: widely used in second gen Linux-capable PDAs. * PXA-2xx UDC: widely used in second gen Linux-capable ARM PDAs
* and other products.
* *
* no limitations except from set_interface: docs say "no" to a third * multiple interfaces (or altsettings) aren't usable. so this hardware
* interface. and the interrupt-only endpoints don't toggle, so we'll * can't implement CDC, which needs both capabilities.
* just use a bulk-capable one instead.
*/ */
#ifdef CONFIG_USB_ETH_PXA250 #ifdef CONFIG_USB_ETH_PXA2XX
#define CHIP "pxa250" #undef DEV_CONFIG_CDC
#define CHIP "pxa2xx"
#define DRIVER_VERSION_NUM 0x0103 #define DRIVER_VERSION_NUM 0x0103
#define EP0_MAXPACKET 16 #define EP0_MAXPACKET 16
static const char EP_OUT_NAME [] = "ep12out-bulk"; static const char EP_OUT_NAME [] = "ep2out-bulk";
#define EP_OUT_NUM 12 #define EP_OUT_NUM 2
static const char EP_IN_NAME [] = "ep11in-bulk"; static const char EP_IN_NAME [] = "ep1in-bulk";
#define EP_IN_NUM 11 #define EP_IN_NUM 1
static const char EP_STATUS_NAME [] = "ep6in-bulk";
#define EP_STATUS_NUM 6
/* doesn't support bus-powered operation */ /* doesn't support bus-powered operation */
#define SELFPOWER USB_CONFIG_ATT_SELFPOWER #define SELFPOWER USB_CONFIG_ATT_SELFPOWER
/* supports remote wakeup, but this driver doesn't */ /* supports remote wakeup, but this driver doesn't */
...@@ -247,6 +252,29 @@ static const char EP_IN_NAME [] = "ep2in-bulk"; ...@@ -247,6 +252,29 @@ static const char EP_IN_NAME [] = "ep2in-bulk";
# error Configure some USB peripheral controller driver! # error Configure some USB peripheral controller driver!
#endif #endif
/* We normally expect hardware that can talk CDC. That involves
* using multiple interfaces and altsettings, and maybe a status
* interrupt. Driver binding to be done according to USB-IF class,
* though you can use different VENDOR and PRODUCT numbers if you
* want (and they're officially assigned).
*
* For hardware that can't talk CDC, we use the same vendor ID that
* ARM Linux has used for ethernet-over-usb, both with sa1100 and
* with pxa250. We're protocol-compatible, if the host-side drivers
* use the endpoint descriptors. DRIVER_VERSION_NUM is nonzero, so
* drivers that need to hard-wire endpoint numbers have a hook.
*/
#ifdef DEV_CONFIG_CDC
#define DEV_CONFIG_CLASS USB_CLASS_COMM
#else
#define DEV_CONFIG_CLASS USB_CLASS_VENDOR_SPEC
#undef EP_STATUS_NUM
#undef DRIVER_VENDOR_NUM
#undef DRIVER_PRODUCT_NUM
#define DRIVER_VENDOR_NUM 0x049f
#define DRIVER_PRODUCT_NUM 0x505a
#endif /* CONFIG_CDC_ETHER */
/* power usage is config specific. /* power usage is config specific.
* hardware that supports remote wakeup defaults to disabling it. * hardware that supports remote wakeup defaults to disabling it.
*/ */
...@@ -274,7 +302,8 @@ static const char EP_IN_NAME [] = "ep2in-bulk"; ...@@ -274,7 +302,8 @@ static const char EP_IN_NAME [] = "ep2in-bulk";
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#define xprintk(d,level,fmt,args...) \ #define xprintk(d,level,fmt,args...) \
dev_printk(level , &(d)->gadget->dev , fmt , ## args) printk(level "%s %s: " fmt , shortname , (d)->gadget->dev.bus_id , \
## args)
#ifdef DEBUG #ifdef DEBUG
#undef DEBUG #undef DEBUG
...@@ -309,7 +338,7 @@ static const char EP_IN_NAME [] = "ep2in-bulk"; ...@@ -309,7 +338,7 @@ static const char EP_IN_NAME [] = "ep2in-bulk";
/* /*
* DESCRIPTORS ... most are static, but strings and (full) configuration * DESCRIPTORS ... most are static, but strings and (full) configuration
* descriptors are built on demand. Notice how most of the cdc descriptors * descriptors are built on demand. Notice how most of the cdc descriptors
* add no value to simple (typical) configurations. * aren't needed in the "minimalist" mode.
*/ */
#define STRING_MANUFACTURER 1 #define STRING_MANUFACTURER 1
...@@ -323,15 +352,14 @@ static const char EP_IN_NAME [] = "ep2in-bulk"; ...@@ -323,15 +352,14 @@ static const char EP_IN_NAME [] = "ep2in-bulk";
/* /*
* This device advertises one configuration. * This device advertises one configuration.
*/ */
#define CONFIG_CDC_ETHER 3
static const struct usb_device_descriptor static const struct usb_device_descriptor
device_desc = { device_desc = {
.bLength = sizeof device_desc, .bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE, .bDescriptorType = USB_DT_DEVICE,
.bcdUSB = __constant_cpu_to_le16 (0x0200), .bcdUSB = __constant_cpu_to_le16 (0x0200),
.bDeviceClass = USB_CLASS_COMM,
.bDeviceClass = DEV_CONFIG_CLASS,
.bDeviceSubClass = 0, .bDeviceSubClass = 0,
.bDeviceProtocol = 0, .bDeviceProtocol = 0,
.bMaxPacketSize0 = EP0_MAXPACKET, .bMaxPacketSize0 = EP0_MAXPACKET,
...@@ -350,13 +378,26 @@ eth_config = { ...@@ -350,13 +378,26 @@ eth_config = {
.bDescriptorType = USB_DT_CONFIG, .bDescriptorType = USB_DT_CONFIG,
/* compute wTotalLength on the fly */ /* compute wTotalLength on the fly */
#ifdef DEV_CONFIG_CDC
.bNumInterfaces = 2, .bNumInterfaces = 2,
.bConfigurationValue = CONFIG_CDC_ETHER, #else
.bNumInterfaces = 1,
#endif
.bConfigurationValue = DEV_CONFIG_VALUE,
.iConfiguration = STRING_PRODUCT, .iConfiguration = STRING_PRODUCT,
.bmAttributes = USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP, .bmAttributes = USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
.bMaxPower = (MAX_USB_POWER + 1) / 2, .bMaxPower = (MAX_USB_POWER + 1) / 2,
}; };
#ifdef DEV_CONFIG_CDC
/*
* Compared to the "minimalist" non-CDC model, the CDC model adds
* three class descriptors, two interface descrioptors, and a status
* endpoint. Both have a "data" interface and two bulk endpoints.
* There are also differences in how control requests are handled.
*/
/* master comm interface optionally has a status notification endpoint */ /* master comm interface optionally has a status notification endpoint */
static const struct usb_interface_descriptor static const struct usb_interface_descriptor
...@@ -446,7 +487,7 @@ static const struct ether_desc ether_desc = { ...@@ -446,7 +487,7 @@ static const struct ether_desc ether_desc = {
* some drivers (like current Linux cdc-ether!) "need" it to exist even * some drivers (like current Linux cdc-ether!) "need" it to exist even
* if they ignore the connect/disconnect notifications that real aether * if they ignore the connect/disconnect notifications that real aether
* can provide. more advanced cdc configurations might want to support * can provide. more advanced cdc configurations might want to support
* encapsulated commands. * encapsulated commands (vendor-specific, using control-OUT).
*/ */
#define LOG2_STATUS_INTERVAL_MSEC 6 #define LOG2_STATUS_INTERVAL_MSEC 6
...@@ -494,6 +535,29 @@ data_intf = { ...@@ -494,6 +535,29 @@ data_intf = {
.bInterfaceProtocol = 0, .bInterfaceProtocol = 0,
.iInterface = STRING_DATA, .iInterface = STRING_DATA,
}; };
#else
/*
* "Minimalist" non-CDC option is a simple vendor-neutral model that most
* full speed controllers can handle: one interface, two bulk endpoints.
*/
static const struct usb_interface_descriptor
data_intf = {
.bLength = sizeof data_intf,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = STRING_DATA,
};
#endif /* DEV_CONFIG_CDC */
static const struct usb_endpoint_descriptor static const struct usb_endpoint_descriptor
fs_source_desc = { fs_source_desc = {
...@@ -563,12 +627,12 @@ dev_qualifier = { ...@@ -563,12 +627,12 @@ dev_qualifier = {
.bDescriptorType = USB_DT_DEVICE_QUALIFIER, .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
.bcdUSB = __constant_cpu_to_le16 (0x0200), .bcdUSB = __constant_cpu_to_le16 (0x0200),
.bDeviceClass = USB_CLASS_VENDOR_SPEC, .bDeviceClass = DEV_CONFIG_CLASS,
/* assumes ep0 uses the same value for both speeds ... */ /* assumes ep0 uses the same value for both speeds ... */
.bMaxPacketSize0 = EP0_MAXPACKET, .bMaxPacketSize0 = EP0_MAXPACKET,
.bNumConfigurations = 2, .bNumConfigurations = 1,
}; };
/* maxpacket and other transfer characteristics vary by speed. */ /* maxpacket and other transfer characteristics vary by speed. */
...@@ -581,16 +645,24 @@ dev_qualifier = { ...@@ -581,16 +645,24 @@ dev_qualifier = {
#endif /* !HIGHSPEED */ #endif /* !HIGHSPEED */
/*-------------------------------------------------------------------------*/
/* descriptors that are built on-demand */
#ifdef DEV_CONFIG_CDC
/* address that the host will use ... usually assigned at random */ /* address that the host will use ... usually assigned at random */
static char ethaddr [2 * ETH_ALEN + 1]; static char ethaddr [2 * ETH_ALEN + 1];
#endif
/* static strings, in iso 8859/1 */ /* static strings, in iso 8859/1 */
static struct usb_string strings [] = { static struct usb_string strings [] = {
{ STRING_MANUFACTURER, UTS_SYSNAME " " UTS_RELEASE "/" CHIP, }, { STRING_MANUFACTURER, UTS_SYSNAME " " UTS_RELEASE "/" CHIP, },
{ STRING_PRODUCT, driver_desc, }, { STRING_PRODUCT, driver_desc, },
#ifdef DEV_CONFIG_CDC
{ STRING_ETHADDR, ethaddr, }, { STRING_ETHADDR, ethaddr, },
{ STRING_CONTROL, control_name, }, { STRING_CONTROL, "CDC Communications Control", },
{ STRING_DATA, data_name, }, #endif
{ STRING_DATA, "Ethernet Data", },
{ } /* end of list */ { } /* end of list */
}; };
...@@ -607,14 +679,18 @@ static int ...@@ -607,14 +679,18 @@ static int
config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index) config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index)
{ {
const unsigned config_len = USB_DT_CONFIG_SIZE const unsigned config_len = USB_DT_CONFIG_SIZE
+ 3 * USB_DT_INTERFACE_SIZE #ifdef DEV_CONFIG_CDC
+ 2 * USB_DT_INTERFACE_SIZE
+ sizeof header_desc + sizeof header_desc
+ sizeof union_desc + sizeof union_desc
+ sizeof ether_desc + sizeof ether_desc
#ifdef EP_STATUS_NUM #ifdef EP_STATUS_NUM
+ USB_DT_ENDPOINT_SIZE + USB_DT_ENDPOINT_SIZE
#endif #endif
#endif /* DEV_CONFIG_CDC */
+ USB_DT_INTERFACE_SIZE
+ 2 * USB_DT_ENDPOINT_SIZE; + 2 * USB_DT_ENDPOINT_SIZE;
#ifdef HIGHSPEED #ifdef HIGHSPEED
int hs; int hs;
#endif #endif
...@@ -636,6 +712,7 @@ config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index) ...@@ -636,6 +712,7 @@ config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index)
hs = !hs; hs = !hs;
#endif #endif
#ifdef DEV_CONFIG_CDC
/* control interface, class descriptors, optional status endpoint */ /* control interface, class descriptors, optional status endpoint */
memcpy (buf, &control_intf, USB_DT_INTERFACE_SIZE); memcpy (buf, &control_intf, USB_DT_INTERFACE_SIZE);
buf += USB_DT_INTERFACE_SIZE; buf += USB_DT_INTERFACE_SIZE;
...@@ -660,6 +737,7 @@ config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index) ...@@ -660,6 +737,7 @@ config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index)
/* default data altsetting has no endpoints */ /* default data altsetting has no endpoints */
memcpy (buf, &data_nop_intf, USB_DT_INTERFACE_SIZE); memcpy (buf, &data_nop_intf, USB_DT_INTERFACE_SIZE);
buf += USB_DT_INTERFACE_SIZE; buf += USB_DT_INTERFACE_SIZE;
#endif /* DEV_CONFIG_CDC */
/* the "real" data interface has two endpoints */ /* the "real" data interface has two endpoints */
memcpy (buf, &data_intf, USB_DT_INTERFACE_SIZE); memcpy (buf, &data_intf, USB_DT_INTERFACE_SIZE);
...@@ -684,6 +762,8 @@ config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index) ...@@ -684,6 +762,8 @@ config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void eth_start (struct eth_dev *dev, int gfp_flags);
static int static int
set_ether_config (struct eth_dev *dev, int gfp_flags) set_ether_config (struct eth_dev *dev, int gfp_flags)
{ {
...@@ -694,7 +774,8 @@ set_ether_config (struct eth_dev *dev, int gfp_flags) ...@@ -694,7 +774,8 @@ set_ether_config (struct eth_dev *dev, int gfp_flags)
gadget_for_each_ep (ep, gadget) { gadget_for_each_ep (ep, gadget) {
const struct usb_endpoint_descriptor *d; const struct usb_endpoint_descriptor *d;
/* NOTE: the host isn't allowed to use these two data #ifdef DEV_CONFIG_CDC
/* With CDC, the host isn't allowed to use these two data
* endpoints in the default altsetting for the interface. * endpoints in the default altsetting for the interface.
* so we don't activate them yet. * so we don't activate them yet.
*/ */
...@@ -714,10 +795,11 @@ set_ether_config (struct eth_dev *dev, int gfp_flags) ...@@ -714,10 +795,11 @@ set_ether_config (struct eth_dev *dev, int gfp_flags)
dev->out_ep = ep; dev->out_ep = ep;
dev->out = d; dev->out = d;
continue; continue;
}
#ifdef EP_STATUS_NUM #ifdef EP_STATUS_NUM
/* optional status/notification endpoint */ /* optional status/notification endpoint */
} else if (strcmp (ep->name, EP_STATUS_NAME) == 0) { else if (strcmp (ep->name, EP_STATUS_NAME) == 0) {
d = ep_desc (gadget, &hs_status_desc, &fs_status_desc); d = ep_desc (gadget, &hs_status_desc, &fs_status_desc);
result = usb_ep_enable (ep, d); result = usb_ep_enable (ep, d);
if (result == 0) { if (result == 0) {
...@@ -726,16 +808,57 @@ set_ether_config (struct eth_dev *dev, int gfp_flags) ...@@ -726,16 +808,57 @@ set_ether_config (struct eth_dev *dev, int gfp_flags)
dev->status = d; dev->status = d;
continue; continue;
} }
}
#endif #endif
#else /* !CONFIG_CDC_ETHER */
/* non-CDC is simpler: if the device is there,
* it's live with rx and tx endpoints.
*/
/* one endpoint writes data back IN to the host */
if (strcmp (ep->name, EP_IN_NAME) == 0) {
d = ep_desc (gadget, &hs_source_desc, &fs_source_desc);
result = usb_ep_enable (ep, d);
if (result == 0) {
ep->driver_data = dev;
dev->in_ep = ep;
dev->in = d;
continue;
}
/* one endpoint just reads OUT packets */
} else if (strcmp (ep->name, EP_OUT_NAME) == 0) {
d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc);
result = usb_ep_enable (ep, d);
if (result == 0) {
ep->driver_data = dev;
dev->out_ep = ep;
dev->out = d;
continue;
}
}
#endif /* !CONFIG_CDC_ETHER */
/* ignore any other endpoints */ /* ignore any other endpoints */
} else else
continue; continue;
/* stop on error */ /* stop on error */
ERROR (dev, "can't enable %s, result %d\n", ep->name, result); ERROR (dev, "can't enable %s, result %d\n", ep->name, result);
break; break;
} }
if (!result && (!dev->in_ep || !dev->out_ep))
result = -ENODEV;
#ifndef DEV_CONFIG_CDC
if (result == 0) {
netif_carrier_on (&dev->net);
if (netif_running (&dev->net))
eth_start (dev, GFP_ATOMIC);
}
#endif /* !CONFIG_CDC_ETHER */
if (result == 0) if (result == 0)
DEBUG (dev, "qlen %d\n", qlen (gadget)); DEBUG (dev, "qlen %d\n", qlen (gadget));
...@@ -751,8 +874,8 @@ static void eth_reset_config (struct eth_dev *dev) ...@@ -751,8 +874,8 @@ static void eth_reset_config (struct eth_dev *dev)
DEBUG (dev, "%s\n", __FUNCTION__); DEBUG (dev, "%s\n", __FUNCTION__);
netif_stop_queue (&dev->net); netif_stop_queue (dev->net);
netif_carrier_off (&dev->net); netif_carrier_off (dev->net);
/* just disable endpoints, forcing completion of pending i/o. /* just disable endpoints, forcing completion of pending i/o.
* all our completion handlers free their requests in this case. * all our completion handlers free their requests in this case.
...@@ -797,7 +920,7 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) ...@@ -797,7 +920,7 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
hw_optimize (gadget); hw_optimize (gadget);
switch (number) { switch (number) {
case CONFIG_CDC_ETHER: case DEV_CONFIG_VALUE:
result = set_ether_config (dev, gfp_flags); result = set_ether_config (dev, gfp_flags);
break; break;
default: default:
...@@ -807,8 +930,6 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) ...@@ -807,8 +930,6 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags)
return result; return result;
} }
if (!result && (!dev->in_ep || !dev->out_ep))
result = -ENODEV;
if (result) if (result)
eth_reset_config (dev); eth_reset_config (dev);
else { else {
...@@ -896,6 +1017,7 @@ static void issue_start_status (struct eth_dev *dev) ...@@ -896,6 +1017,7 @@ static void issue_start_status (struct eth_dev *dev)
* FIXME ugly idiom, maybe we'd be better with just * FIXME ugly idiom, maybe we'd be better with just
* a "cancel the whole queue" primitive since any * a "cancel the whole queue" primitive since any
* unlink-one primitive has way too many error modes. * unlink-one primitive has way too many error modes.
* here, we "know" toggle is already clear...
*/ */
usb_ep_disable (dev->status_ep); usb_ep_disable (dev->status_ep);
usb_ep_enable (dev->status_ep, dev->status); usb_ep_enable (dev->status_ep, dev->status);
...@@ -953,8 +1075,6 @@ static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req) ...@@ -953,8 +1075,6 @@ static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req)
*/ */
#define CDC_SET_ETHERNET_PACKET_FILTER 0x43 /* required */ #define CDC_SET_ETHERNET_PACKET_FILTER 0x43 /* required */
static void eth_start (struct eth_dev *dev, int gfp_flags);
/* /*
* The setup() callback implements all the ep0 functionality that's not * The setup() callback implements all the ep0 functionality that's not
* handled lower down. CDC has a number of less-common features: * handled lower down. CDC has a number of less-common features:
...@@ -1056,15 +1176,15 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ...@@ -1056,15 +1176,15 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (ctrl->wValue == 1) { if (ctrl->wValue == 1) {
usb_ep_enable (dev->in_ep, dev->in); usb_ep_enable (dev->in_ep, dev->in);
usb_ep_enable (dev->out_ep, dev->out); usb_ep_enable (dev->out_ep, dev->out);
netif_carrier_on (&dev->net); netif_carrier_on (dev->net);
#ifdef EP_STATUS_NUM #ifdef EP_STATUS_NUM
issue_start_status (dev); issue_start_status (dev);
#endif #endif
if (netif_running (&dev->net)) if (netif_running (dev->net))
eth_start (dev, GFP_ATOMIC); eth_start (dev, GFP_ATOMIC);
} else { } else {
netif_stop_queue (&dev->net); netif_stop_queue (dev->net);
netif_carrier_off (&dev->net); netif_carrier_off (dev->net);
} }
value = 0; value = 0;
break; break;
...@@ -1079,12 +1199,13 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ...@@ -1079,12 +1199,13 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
/* if carrier is on, data interface is active. */ /* if carrier is on, data interface is active. */
*(u8 *)req->buf = *(u8 *)req->buf =
((ctrl->wIndex == 1) && netif_carrier_ok (&dev->net)) ((ctrl->wIndex == 1) && netif_carrier_ok (dev->net))
? 1 ? 1
: 0, : 0,
value = min (ctrl->wLength, (u16) 1); value = min (ctrl->wLength, (u16) 1);
break; break;
#ifdef DEV_CONFIG_CDC
case CDC_SET_ETHERNET_PACKET_FILTER: case CDC_SET_ETHERNET_PACKET_FILTER:
/* see 6.2.30: no data, wIndex = interface, /* see 6.2.30: no data, wIndex = interface,
* wValue = packet filter bitmap * wValue = packet filter bitmap
...@@ -1099,6 +1220,7 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ...@@ -1099,6 +1220,7 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
*/ */
value = 0; value = 0;
break; break;
#endif /* DEV_CONFIG_CDC */
default: default:
VDEBUG (dev, VDEBUG (dev,
...@@ -1129,8 +1251,8 @@ eth_disconnect (struct usb_gadget *gadget) ...@@ -1129,8 +1251,8 @@ eth_disconnect (struct usb_gadget *gadget)
unsigned long flags; unsigned long flags;
spin_lock_irqsave (&dev->lock, flags); spin_lock_irqsave (&dev->lock, flags);
netif_stop_queue (&dev->net); netif_stop_queue (dev->net);
netif_carrier_off (&dev->net); netif_carrier_off (dev->net);
eth_reset_config (dev); eth_reset_config (dev);
spin_unlock_irqrestore (&dev->lock, flags); spin_unlock_irqrestore (&dev->lock, flags);
...@@ -1175,10 +1297,10 @@ static int eth_ethtool_ioctl (struct net_device *net, void *useraddr) ...@@ -1175,10 +1297,10 @@ static int eth_ethtool_ioctl (struct net_device *net, void *useraddr)
memset (&info, 0, sizeof info); memset (&info, 0, sizeof info);
info.cmd = ETHTOOL_GDRVINFO; info.cmd = ETHTOOL_GDRVINFO;
strncpy (info.driver, shortname, sizeof info.driver); strlcpy (info.driver, shortname, sizeof info.driver);
strncpy (info.version, DRIVER_VERSION, sizeof info.version); strlcpy (info.version, DRIVER_VERSION, sizeof info.version);
strncpy (info.fw_version, CHIP, sizeof info.fw_version); strlcpy (info.fw_version, CHIP, sizeof info.fw_version);
strncpy (info.bus_info, dev->gadget->dev.bus_id, strlcpy (info.bus_info, dev->gadget->dev.bus_id,
sizeof info.bus_info); sizeof info.bus_info);
if (copy_to_user (useraddr, &info, sizeof (info))) if (copy_to_user (useraddr, &info, sizeof (info)))
return -EFAULT; return -EFAULT;
...@@ -1227,7 +1349,7 @@ rx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags) ...@@ -1227,7 +1349,7 @@ rx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags)
int retval = 0; int retval = 0;
size_t size; size_t size;
size = (sizeof (struct ethhdr) + dev->net.mtu + RX_EXTRA); size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA);
if ((skb = alloc_skb (size, gfp_flags)) == 0) { if ((skb = alloc_skb (size, gfp_flags)) == 0) {
DEBUG (dev, "no rx skb\n"); DEBUG (dev, "no rx skb\n");
...@@ -1241,16 +1363,9 @@ rx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags) ...@@ -1241,16 +1363,9 @@ rx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags)
req->complete = rx_complete; req->complete = rx_complete;
req->context = skb; req->context = skb;
if (netif_running (&dev->net)) { retval = usb_ep_queue (dev->out_ep, req, gfp_flags);
retval = usb_ep_queue (dev->out_ep, req, gfp_flags); if (retval == -ENOMEM)
if (retval == -ENOMEM) defer_kevent (dev, WORK_RX_MEMORY);
defer_kevent (dev, WORK_RX_MEMORY);
if (retval)
DEBUG (dev, "%s %d\n", __FUNCTION__, retval);
} else {
DEBUG (dev, "%s stopped\n", __FUNCTION__);
retval = -ENOLINK;
}
if (retval) { if (retval) {
DEBUG (dev, "rx submit --> %d\n", retval); DEBUG (dev, "rx submit --> %d\n", retval);
dev_kfree_skb_any (skb); dev_kfree_skb_any (skb);
...@@ -1278,8 +1393,8 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req) ...@@ -1278,8 +1393,8 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req)
break; break;
} }
skb->dev = &dev->net; skb->dev = dev->net;
skb->protocol = eth_type_trans (skb, &dev->net); skb->protocol = eth_type_trans (skb, dev->net);
dev->stats.rx_packets++; dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len; dev->stats.rx_bytes += skb->len;
...@@ -1294,9 +1409,7 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req) ...@@ -1294,9 +1409,7 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req)
case -ECONNRESET: // unlink case -ECONNRESET: // unlink
case -ESHUTDOWN: // disconnect etc case -ESHUTDOWN: // disconnect etc
VDEBUG (dev, "rx shutdown, code %d\n", status); VDEBUG (dev, "rx shutdown, code %d\n", status);
usb_ep_free_request (dev->out_ep, req); goto clean;
req = 0;
break;
/* data overrun */ /* data overrun */
case -EOVERFLOW: case -EOVERFLOW:
...@@ -1311,7 +1424,11 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req) ...@@ -1311,7 +1424,11 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req)
if (skb) if (skb)
dev_kfree_skb_any (skb); dev_kfree_skb_any (skb);
if (!netif_running (&dev->net)) {
clean:
usb_ep_free_request (dev->out_ep, req);
req = 0;
}
if (req) if (req)
rx_submit (dev, req, GFP_ATOMIC); rx_submit (dev, req, GFP_ATOMIC);
} }
...@@ -1323,7 +1440,7 @@ static void eth_work (void *_dev) ...@@ -1323,7 +1440,7 @@ static void eth_work (void *_dev)
if (test_bit (WORK_RX_MEMORY, &dev->todo)) { if (test_bit (WORK_RX_MEMORY, &dev->todo)) {
struct usb_request *req = 0; struct usb_request *req = 0;
if (netif_running (&dev->net)) if (netif_running (dev->net))
req = usb_ep_alloc_request (dev->in_ep, GFP_KERNEL); req = usb_ep_alloc_request (dev->in_ep, GFP_KERNEL);
else else
clear_bit (WORK_RX_MEMORY, &dev->todo); clear_bit (WORK_RX_MEMORY, &dev->todo);
...@@ -1342,18 +1459,25 @@ static void tx_complete (struct usb_ep *ep, struct usb_request *req) ...@@ -1342,18 +1459,25 @@ static void tx_complete (struct usb_ep *ep, struct usb_request *req)
struct sk_buff *skb = req->context; struct sk_buff *skb = req->context;
struct eth_dev *dev = ep->driver_data; struct eth_dev *dev = ep->driver_data;
if (req->status) switch (req->status) {
default:
dev->stats.tx_errors++; dev->stats.tx_errors++;
else VDEBUG (dev, "tx err %d\n", req->status);
/* FALLTHROUGH */
case -ECONNRESET: // unlink
case -ESHUTDOWN: // disconnect etc
break;
case 0:
dev->stats.tx_bytes += skb->len; dev->stats.tx_bytes += skb->len;
}
dev->stats.tx_packets++; dev->stats.tx_packets++;
usb_ep_free_request (ep, req); usb_ep_free_request (ep, req);
dev_kfree_skb_any (skb); dev_kfree_skb_any (skb);
atomic_inc (&dev->tx_qlen); atomic_inc (&dev->tx_qlen);
if (netif_carrier_ok (&dev->net)) if (netif_carrier_ok (dev->net))
netif_wake_queue (&dev->net); netif_wake_queue (dev->net);
} }
static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
...@@ -1437,7 +1561,7 @@ static void eth_start (struct eth_dev *dev, int gfp_flags) ...@@ -1437,7 +1561,7 @@ static void eth_start (struct eth_dev *dev, int gfp_flags)
/* and open the tx floodgates */ /* and open the tx floodgates */
atomic_set (&dev->tx_qlen, size); atomic_set (&dev->tx_qlen, size);
netif_wake_queue (&dev->net); netif_wake_queue (dev->net);
} }
static int eth_open (struct net_device *net) static int eth_open (struct net_device *net)
...@@ -1445,10 +1569,8 @@ static int eth_open (struct net_device *net) ...@@ -1445,10 +1569,8 @@ static int eth_open (struct net_device *net)
struct eth_dev *dev = (struct eth_dev *) net->priv; struct eth_dev *dev = (struct eth_dev *) net->priv;
DEBUG (dev, "%s\n", __FUNCTION__); DEBUG (dev, "%s\n", __FUNCTION__);
down (&dev->mutex); if (netif_carrier_ok (dev->net))
if (netif_carrier_ok (&dev->net))
eth_start (dev, GFP_KERNEL); eth_start (dev, GFP_KERNEL);
up (&dev->mutex);
return 0; return 0;
} }
...@@ -1457,7 +1579,6 @@ static int eth_stop (struct net_device *net) ...@@ -1457,7 +1579,6 @@ static int eth_stop (struct net_device *net)
struct eth_dev *dev = (struct eth_dev *) net->priv; struct eth_dev *dev = (struct eth_dev *) net->priv;
DEBUG (dev, "%s\n", __FUNCTION__); DEBUG (dev, "%s\n", __FUNCTION__);
down (&dev->mutex);
netif_stop_queue (net); netif_stop_queue (net);
DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
...@@ -1469,7 +1590,7 @@ static int eth_stop (struct net_device *net) ...@@ -1469,7 +1590,7 @@ static int eth_stop (struct net_device *net)
if (dev->gadget->speed != USB_SPEED_UNKNOWN) { if (dev->gadget->speed != USB_SPEED_UNKNOWN) {
usb_ep_disable (dev->in_ep); usb_ep_disable (dev->in_ep);
usb_ep_disable (dev->out_ep); usb_ep_disable (dev->out_ep);
if (netif_carrier_ok (&dev->net)) { if (netif_carrier_ok (dev->net)) {
DEBUG (dev, "host still using in/out endpoints\n"); DEBUG (dev, "host still using in/out endpoints\n");
usb_ep_enable (dev->in_ep, dev->in); usb_ep_enable (dev->in_ep, dev->in);
usb_ep_enable (dev->out_ep, dev->out); usb_ep_enable (dev->out_ep, dev->out);
...@@ -1480,7 +1601,6 @@ static int eth_stop (struct net_device *net) ...@@ -1480,7 +1601,6 @@ static int eth_stop (struct net_device *net)
#endif #endif
} }
up (&dev->mutex);
return 0; return 0;
} }
...@@ -1492,7 +1612,6 @@ eth_unbind (struct usb_gadget *gadget) ...@@ -1492,7 +1612,6 @@ eth_unbind (struct usb_gadget *gadget)
struct eth_dev *dev = get_gadget_data (gadget); struct eth_dev *dev = get_gadget_data (gadget);
DEBUG (dev, "unbind\n"); DEBUG (dev, "unbind\n");
down (&dev->mutex);
/* we've already been disconnected ... no i/o is active */ /* we've already been disconnected ... no i/o is active */
if (dev->req) { if (dev->req) {
...@@ -1500,15 +1619,13 @@ eth_unbind (struct usb_gadget *gadget) ...@@ -1500,15 +1619,13 @@ eth_unbind (struct usb_gadget *gadget)
dev->req->buf, dev->req->dma, dev->req->buf, dev->req->dma,
USB_BUFSIZ); USB_BUFSIZ);
usb_ep_free_request (gadget->ep0, dev->req); usb_ep_free_request (gadget->ep0, dev->req);
dev->req = 0;
} }
unregister_netdev (&dev->net); unregister_netdev (dev->net);
up (&dev->mutex);
/* assuming we used keventd, it must quiesce too */ /* assuming we used keventd, it must quiesce too */
flush_scheduled_work (); flush_scheduled_work ();
kfree (dev);
set_gadget_data (gadget, 0); set_gadget_data (gadget, 0);
} }
...@@ -1517,22 +1634,24 @@ eth_bind (struct usb_gadget *gadget) ...@@ -1517,22 +1634,24 @@ eth_bind (struct usb_gadget *gadget)
{ {
struct eth_dev *dev; struct eth_dev *dev;
struct net_device *net; struct net_device *net;
int status = -ENOMEM;
#ifdef DEV_CONFIG_CDC
u8 node_id [ETH_ALEN]; u8 node_id [ETH_ALEN];
/* just one upstream link at a time */ /* just one upstream link at a time */
if (ethaddr [0] != 0) if (ethaddr [0] != 0)
return -ENODEV; return -ENODEV;
#endif
dev = kmalloc (sizeof *dev, SLAB_KERNEL); net = alloc_etherdev (sizeof *dev);
if (!dev) if (!net)
return -ENOMEM; return status;
memset (dev, 0, sizeof *dev); dev = net->priv;
spin_lock_init (&dev->lock); spin_lock_init (&dev->lock);
init_MUTEX_LOCKED (&dev->mutex);
INIT_WORK (&dev->work, eth_work, dev); INIT_WORK (&dev->work, eth_work, dev);
/* network device setup */ /* network device setup */
net = &dev->net; dev->net = net;
SET_MODULE_OWNER (net); SET_MODULE_OWNER (net);
net->priv = dev; net->priv = dev;
strcpy (net->name, "usb%d"); strcpy (net->name, "usb%d");
...@@ -1545,6 +1664,7 @@ eth_bind (struct usb_gadget *gadget) ...@@ -1545,6 +1664,7 @@ eth_bind (struct usb_gadget *gadget)
net->dev_addr [0] &= 0xfe; // clear multicast bit net->dev_addr [0] &= 0xfe; // clear multicast bit
net->dev_addr [0] |= 0x02; // set local assignment bit (IEEE802) net->dev_addr [0] |= 0x02; // set local assignment bit (IEEE802)
#ifdef DEV_CONFIG_CDC
/* ... another address for the host, on the other end of the /* ... another address for the host, on the other end of the
* link, gets exported through CDC (see CDC spec table 41) * link, gets exported through CDC (see CDC spec table 41)
*/ */
...@@ -1554,6 +1674,7 @@ eth_bind (struct usb_gadget *gadget) ...@@ -1554,6 +1674,7 @@ eth_bind (struct usb_gadget *gadget)
snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X", snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
node_id [0], node_id [1], node_id [2], node_id [0], node_id [1], node_id [2],
node_id [3], node_id [4], node_id [5]); node_id [3], node_id [4], node_id [5]);
#endif
net->change_mtu = eth_change_mtu; net->change_mtu = eth_change_mtu;
net->get_stats = eth_get_stats; net->get_stats = eth_get_stats;
...@@ -1567,36 +1688,38 @@ eth_bind (struct usb_gadget *gadget) ...@@ -1567,36 +1688,38 @@ eth_bind (struct usb_gadget *gadget)
/* preallocate control response and buffer */ /* preallocate control response and buffer */
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
if (!dev->req) if (!dev->req)
goto enomem; goto fail;
dev->req->complete = eth_setup_complete; dev->req->complete = eth_setup_complete;
dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ, dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,
&dev->req->dma, GFP_KERNEL); &dev->req->dma, GFP_KERNEL);
if (!dev->req->buf) { if (!dev->req->buf) {
usb_ep_free_request (gadget->ep0, dev->req); usb_ep_free_request (gadget->ep0, dev->req);
goto enomem; goto fail;
} }
/* finish hookup to lower layer ... */ /* finish hookup to lower layer ... */
dev->gadget = gadget; dev->gadget = gadget;
set_gadget_data (gadget, dev); set_gadget_data (gadget, dev);
gadget->ep0->driver_data = dev; gadget->ep0->driver_data = dev;
INFO (dev, "%s, " CHIP ", version: " DRIVER_VERSION "\n", driver_desc);
#ifdef DEV_CONFIG_CDC
INFO (dev, "CDC host enet %s\n", ethaddr);
#endif
/* two kinds of host-initiated state changes: /* two kinds of host-initiated state changes:
* - iff DATA transfer is active, carrier is "on" * - iff DATA transfer is active, carrier is "on"
* - tx queueing enabled if open *and* carrier is "on" * - tx queueing enabled if open *and* carrier is "on"
*/ */
INFO (dev, "%s, host enet %s, version: " DRIVER_VERSION "\n", netif_stop_queue (dev->net);
driver_desc, ethaddr); netif_carrier_off (dev->net);
register_netdev (&dev->net);
netif_stop_queue (&dev->net); // SET_NETDEV_DEV (dev->net, &gadget->dev);
netif_carrier_off (&dev->net); status = register_netdev (dev->net);
if (status == 0)
up (&dev->mutex); return status;
return 0; fail:
enomem:
eth_unbind (gadget); eth_unbind (gadget);
return -ENOMEM; return status;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment