#include <linux/version.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
+#include <linux/ctype.h>
#include <asm/byteorder.h>
#include <asm/io.h>
/*
* Ethernet gadget driver -- with CDC and non-CDC options
+ * Builds on hardware support for a full duplex link.
*
* CDC Ethernet is the standard USB solution for sending Ethernet frames
* using USB. Real hardware tends to use the same framing protocol but look
unsigned zlp:1;
unsigned cdc:1;
unsigned rndis:1;
+ unsigned suspended:1;
+ u16 cdc_filter;
unsigned long todo;
#define WORK_RX_MEMORY 0
int rndis_config;
module_param(iProduct, charp, S_IRUGO);
MODULE_PARM_DESC(iProduct, "USB Product string");
+/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
+static char *__initdata dev_addr;
+module_param(dev_addr, charp, S_IRUGO);
+MODULE_PARM_DESC(iProduct, "Device Ethernet Address");
+
+/* this address is invisible to ifconfig */
+static char *__initdata host_addr;
+module_param(host_addr, charp, S_IRUGO);
+MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
+
/*-------------------------------------------------------------------------*/
/* For CDC-incapable hardware, choose the simple cdc subset.
* Anything that talks bulk (without notable bugs) can do this.
*/
-#ifdef CONFIG_USB_GADGET_PXA
+#ifdef CONFIG_USB_GADGET_PXA2XX
#define DEV_CONFIG_SUBSET
#endif
#define DEV_CONFIG_SUBSET
#endif
+#ifdef CONFIG_USB_GADGET_LH7A40X
+#define DEV_CONFIG_CDC
+#endif
+
#ifdef CONFIG_USB_GADGET_SA1100
/* use non-CDC for backwards compatibility */
#define DEV_CONFIG_SUBSET
*
* NOTE: Controllers like superh_udc should probably be able to use
* an RNDIS-only configuration.
+ *
+ * FIXME define some higher-powered configurations to make it easier
+ * to recharge batteries ...
*/
#define DEV_CONFIG_VALUE 1 /* cdc or subset */
.bNumConfigurations = 1,
};
+static struct usb_otg_descriptor
+otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ .bmAttributes = USB_OTG_SRP,
+};
+
static struct usb_config_descriptor
eth_config = {
.bLength = sizeof eth_config,
};
#ifdef CONFIG_USB_ETH_RNDIS
-static const struct usb_config_descriptor
+static struct usb_config_descriptor
rndis_config = {
.bLength = sizeof rndis_config,
.bDescriptorType = USB_DT_CONFIG,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
-static const struct usb_descriptor_header *fs_eth_function [10] = {
+static const struct usb_descriptor_header *fs_eth_function [11] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
#ifdef DEV_CONFIG_CDC
/* "cdc" mode descriptors */
(struct usb_descriptor_header *) &control_intf,
(struct usb_descriptor_header *) &data_intf,
(struct usb_descriptor_header *) &fs_source_desc,
(struct usb_descriptor_header *) &fs_sink_desc,
- 0,
+ NULL,
#endif /* DEV_CONFIG_CDC */
};
static inline void __init fs_subset_descriptors(void)
{
#ifdef DEV_CONFIG_SUBSET
- fs_eth_function[0] = (struct usb_descriptor_header *) &subset_data_intf;
- fs_eth_function[1] = (struct usb_descriptor_header *) &fs_source_desc;
- fs_eth_function[2] = (struct usb_descriptor_header *) &fs_sink_desc;
- fs_eth_function[3] = 0;
+ 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;
#else
- fs_eth_function[0] = 0;
+ fs_eth_function[1] = NULL;
#endif
}
#ifdef CONFIG_USB_ETH_RNDIS
static const struct usb_descriptor_header *fs_rndis_function [] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &rndis_data_intf,
(struct usb_descriptor_header *) &fs_source_desc,
(struct usb_descriptor_header *) &fs_sink_desc,
- 0,
+ NULL,
};
#endif
.bNumConfigurations = 1,
};
-static const struct usb_descriptor_header *hs_eth_function [10] = {
+static const struct usb_descriptor_header *hs_eth_function [11] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
#ifdef DEV_CONFIG_CDC
/* "cdc" mode descriptors */
(struct usb_descriptor_header *) &control_intf,
(struct usb_descriptor_header *) &data_intf,
(struct usb_descriptor_header *) &hs_source_desc,
(struct usb_descriptor_header *) &hs_sink_desc,
- 0,
+ NULL,
#endif /* DEV_CONFIG_CDC */
};
static inline void __init hs_subset_descriptors(void)
{
#ifdef DEV_CONFIG_SUBSET
- hs_eth_function[0] = (struct usb_descriptor_header *) &subset_data_intf;
- hs_eth_function[1] = (struct usb_descriptor_header *) &fs_source_desc;
- hs_eth_function[2] = (struct usb_descriptor_header *) &fs_sink_desc;
- hs_eth_function[3] = 0;
+ 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;
#else
- hs_eth_function[0] = 0;
+ hs_eth_function[1] = NULL;
#endif
}
#ifdef CONFIG_USB_ETH_RNDIS
static const struct usb_descriptor_header *hs_rndis_function [] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &rndis_data_intf,
(struct usb_descriptor_header *) &hs_source_desc,
(struct usb_descriptor_header *) &hs_sink_desc,
- 0,
+ NULL,
};
#endif
static char ethaddr [2 * ETH_ALEN + 1];
#endif
-/* static strings, in iso 8859/1 */
+/* static strings, in UTF-8 */
static struct usb_string strings [] = {
{ STRING_MANUFACTURER, manufacturer, },
{ STRING_PRODUCT, product_desc, },
* complications: class descriptors, and an altsetting.
*/
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, int is_otg)
{
- int len;
+ int len;
+ const struct usb_config_descriptor *config;
+ const struct usb_descriptor_header **function;
#ifdef CONFIG_USB_GADGET_DUALSPEED
int hs = (speed == USB_SPEED_HIGH);
if (type == USB_DT_OTHER_SPEED_CONFIG)
hs = !hs;
-#define which_config(t) (hs ? & t ## _config : & t ## _config)
-#define which_fn(t) (hs ? & hs_ ## t ## _function : & fs_ ## t ## _function)
+#define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function)
#else
-#define which_config(t) (& t ## _config)
-#define which_fn(t) (& fs_ ## t ## _function)
+#define which_fn(t) (fs_ ## t ## _function)
#endif
if (index >= device_desc.bNumConfigurations)
/* list the RNDIS config first, to make Microsoft's drivers
* happy. DOCSIS 1.0 needs this too.
*/
- if (device_desc.bNumConfigurations == 2 && index == 0)
- len = usb_gadget_config_buf (which_config (rndis), buf,
- USB_BUFSIZ, (const struct usb_descriptor_header **)
- which_fn (rndis));
- else
+ if (device_desc.bNumConfigurations == 2 && index == 0) {
+ config = &rndis_config;
+ function = which_fn (rndis);
+ } else
#endif
- len = usb_gadget_config_buf (which_config (eth), buf,
- USB_BUFSIZ, (const struct usb_descriptor_header **)
- which_fn (eth));
+ {
+ config = ð_config;
+ function = which_fn (eth);
+ }
+
+ /* for now, don't advertise srp-only devices */
+ if (!is_otg)
+ function++;
+
+ len = usb_gadget_config_buf (config, buf, USB_BUFSIZ, function);
if (len < 0)
return len;
((struct usb_config_descriptor *) buf)->bDescriptorType = type;
if (dev->status_ep)
(void) usb_ep_disable (dev->status_ep);
#endif
- dev->status_ep = 0;
- dev->status = 0;
+ dev->status_ep = NULL;
+ dev->status = NULL;
#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS)
if (dev->rndis || !dev->cdc) {
if (dev->in_ep)
(void) usb_ep_disable (dev->out_ep);
}
#endif
- dev->in_ep = 0;
- dev->in = 0;
- dev->out_ep = 0;
- dev->out = 0;
+ dev->in_ep = NULL;
+ dev->in = NULL;
+ dev->out_ep = NULL;
+ dev->out = NULL;
} else
/* activate non-CDC configs right away
list_del (&req->list);
usb_ep_free_request (dev->in_ep, req);
}
- dev->in_ep = 0;
+ dev->in_ep = NULL;
}
if (dev->out_ep) {
usb_ep_disable (dev->out_ep);
list_del (&req->list);
usb_ep_free_request (dev->out_ep, req);
}
- dev->out_ep = 0;
+ dev->out_ep = NULL;
}
if (dev->status_ep) {
usb_ep_disable (dev->status_ep);
- dev->status_ep = 0;
+ dev->status_ep = NULL;
}
dev->config = 0;
}
}
eth_reset_config (dev);
+ /* default: pass all packets, no multicast filtering */
+ dev->cdc_filter = 0x000f;
+
switch (number) {
case DEV_CONFIG_VALUE:
dev->rndis = 0;
* section 3.6.2.1 table 4 has ACM requests; RNDIS requires the
* encapsulated command mechanism.
*/
-#define CDC_SEND_ENCAPSULATED_COMMAND 0x00 /* optional */
-#define CDC_GET_ENCAPSULATED_RESPONSE 0x01 /* optional */
-#define CDC_SET_ETHERNET_PACKET_FILTER 0x43 /* required */
+#define CDC_SEND_ENCAPSULATED_COMMAND 0x00 /* optional */
+#define CDC_GET_ENCAPSULATED_RESPONSE 0x01 /* optional */
+#define CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 /* optional */
+#define CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 /* optional */
+#define CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 /* optional */
+#define CDC_SET_ETHERNET_PACKET_FILTER 0x43 /* required */
+#define CDC_GET_ETHERNET_STATISTIC 0x44 /* optional */
+
+/* table 62; bits in cdc_filter */
+#define CDC_PACKET_TYPE_PROMISCUOUS (1 << 0)
+#define CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */
+#define CDC_PACKET_TYPE_DIRECTED (1 << 2)
+#define CDC_PACKET_TYPE_BROADCAST (1 << 3)
+#define CDC_PACKET_TYPE_MULTICAST (1 << 4) /* filtered */
#ifdef CONFIG_USB_ETH_RNDIS
static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req)
{
- struct eth_dev *dev = ep->driver_data;
-
if (req->status || req->actual != req->length)
DEBUG (dev, "rndis response complete --> %d, %d/%d\n",
req->status, req->actual, req->length);
/* done sending after CDC_GET_ENCAPSULATED_RESPONSE */
- rndis_free_response (dev->rndis_config, req->buf);
}
static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req)
{
struct eth_dev *dev = ep->driver_data;
+ int status;
/* received RNDIS command from CDC_SEND_ENCAPSULATED_COMMAND */
spin_lock(&dev->lock);
- if (rndis_msg_parser (dev->rndis_config, (u8 *) req->buf))
- ERROR(dev, "%s: rndis parse error\n", __FUNCTION__ );
+ status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf);
+ if (status < 0)
+ ERROR(dev, "%s: rndis parse error %d\n", __FUNCTION__, status);
spin_unlock(&dev->lock);
}
/* descriptors just go into the pre-allocated ep0 buffer,
* while config change events may enable network traffic.
*/
+ req->complete = eth_setup_complete;
switch (ctrl->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
case USB_DT_CONFIG:
value = config_buf (gadget->speed, req->buf,
ctrl->wValue >> 8,
- ctrl->wValue & 0xff);
+ ctrl->wValue & 0xff,
+ gadget->is_otg);
if (value >= 0)
value = min (ctrl->wLength, (u16) value);
break;
case USB_REQ_SET_CONFIGURATION:
if (ctrl->bRequestType != 0)
break;
+ if (gadget->a_hnp_support)
+ DEBUG (dev, "HNP available\n");
+ else if (gadget->a_alt_hnp_support)
+ DEBUG (dev, "HNP needs a different root port\n");
spin_lock (&dev->lock);
value = eth_set_config (dev, ctrl->wValue, GFP_ATOMIC);
spin_unlock (&dev->lock);
DEBUG (dev, "NOP packet filter %04x\n", ctrl->wValue);
/* NOTE: table 62 has 5 filter bits to reduce traffic,
* and we "must" support multicast and promiscuous.
- * this NOP implements a bad filter...
+ * this NOP implements a bad filter (always promisc)
*/
+ dev->cdc_filter = ctrl->wValue;
value = 0;
break;
#endif /* DEV_CONFIG_CDC */
if (buf) {
memcpy (req->buf, buf, value);
req->complete = rndis_response_complete;
+ rndis_free_response(dev->rndis_config, buf);
}
/* else stalls ... spec says to avoid that */
}
/* respond with data transfer before status phase? */
if (value >= 0) {
req->length = value;
+ req->zero = value < ctrl->wLength
+ && (value % gadget->ep0->maxpacket) == 0;
value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
if (value < 0) {
DEBUG (dev, "ep_queue --> %d\n", value);
return &((struct eth_dev *) net->priv)->stats;
}
-static int eth_ethtool_ioctl (struct net_device *net, void *useraddr)
+static int eth_ethtool_ioctl (struct net_device *net, void __user *useraddr)
{
struct eth_dev *dev = (struct eth_dev *) net->priv;
u32 cmd;
- if (get_user (cmd, (u32 *)useraddr))
+ if (get_user (cmd, (u32 __user *)useraddr))
return -EFAULT;
switch (cmd) {
{
switch (cmd) {
case SIOCETHTOOL:
- return eth_ethtool_ioctl (net, (void *)rq->ifr_data);
+ return eth_ethtool_ioctl(net, rq->ifr_data);
default:
return -EOPNOTSUPP;
}
/* 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)-1, since that memory is normally
- * already allocated. Major loss of synch means -EOVERFLOW; any
- * obviously corrupted packets will automatically be discarded.
+ * 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,
size += sizeof (struct rndis_packet_msg_type);
#endif
size -= size % dev->out_ep->maxpacket;
-#ifdef CONFIG_USB_ETH_RNDIS
- if (!dev->rndis)
-#endif
- size--;
if ((skb = alloc_skb (size, gfp_flags)) == 0) {
DEBUG (dev, "no rx skb\n");
* use skb buffers.
*/
status = netif_rx (skb);
- skb = 0;
+ skb = NULL;
break;
/* software-driven interface shutdown */
clean:
/* nobody reading rx_reqs, so no dev->lock */
list_add (&req->list, &dev->rx_reqs);
- req = 0;
+ req = NULL;
}
if (req)
rx_submit (dev, req, GFP_ATOMIC);
struct eth_dev *dev = (struct eth_dev *) net->priv;
int length = skb->len;
int retval;
- struct usb_request *req = 0;
+ struct usb_request *req = NULL;
unsigned long flags;
+ /* FIXME check dev->cdc_filter to decide whether to send this,
+ * instead of acting as if CDC_PACKET_TYPE_PROMISCUOUS were
+ * always set. RNDIS has the same kind of outgoing filter.
+ */
+
spin_lock_irqsave (&dev->lock, flags);
req = container_of (dev->tx_reqs.next, struct usb_request, list);
list_del (&req->list);
}
}
+static void rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length)
+ DEBUG (dev, "rndis control ack complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+
+ usb_ep_free_buffer(ep, req->buf, req->dma, 8);
+ usb_ep_free_request(ep, req);
+}
+
static int rndis_control_ack (struct net_device *net)
{
struct eth_dev *dev = (struct eth_dev *) net->priv;
* CDC_NOTIFY_RESPONSE_AVAILABLE should work too
*/
resp->length = 8;
- resp->complete = rndis_response_complete;
+ resp->complete = rndis_control_ack_complete;
*((u32 *) resp->buf) = __constant_cpu_to_le32 (1);
*((u32 *) resp->buf + 1) = __constant_cpu_to_le32 (0);
length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC);
if (length < 0) {
resp->status = 0;
- rndis_response_complete (dev->status_ep, resp);
+ rndis_control_ack_complete (dev->status_ep, resp);
}
return 0;
dev->req->buf, dev->req->dma,
USB_BUFSIZ);
usb_ep_free_request (gadget->ep0, dev->req);
- dev->req = 0;
+ dev->req = NULL;
}
unregister_netdev (dev->net);
/* assuming we used keventd, it must quiesce too */
flush_scheduled_work ();
- set_gadget_data (gadget, 0);
+ set_gadget_data (gadget, NULL);
+}
+
+static u8 __init nibble (unsigned char c)
+{
+ if (likely (isdigit (c)))
+ return c - '0';
+ c = toupper (c);
+ if (likely (isxdigit (c)))
+ return 10 + c - 'A';
+ return 0;
+}
+
+static void __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;
+ }
+ random_ether_addr(dev_addr);
}
static int __init
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207);
} else if (gadget_is_omap (gadget)) {
device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);
+ } else if (gadget_is_lh7a40x(gadget)) {
+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209);
} else {
/* can't assume CDC works. don't want to default to
* anything less functional on CDC-capable hardware,
UTS_SYSNAME " " UTS_RELEASE "/%s",
gadget->name);
- /* CDC subset ... recognized by Linux since 2.4.10, but Windows
- * drivers aren't widely available.
- */
- if (!cdc) {
- device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
- device_desc.idVendor =
- __constant_cpu_to_le16(SIMPLE_VENDOR_NUM);
- device_desc.idProduct =
- __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM);
- }
-
/* If there's an RNDIS configuration, that's what Windows wants to
* be using ... so use these product IDs here and in the "linux.inf"
* needed to install MSFT drivers. Current Linux kernels will use
__constant_cpu_to_le16(RNDIS_PRODUCT_NUM);
snprintf (product_desc, sizeof product_desc,
"RNDIS/%s", driver_desc);
+
+ /* CDC subset ... recognized by Linux since 2.4.10, but Windows
+ * drivers aren't widely available.
+ */
+ } else if (!cdc) {
+ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+ device_desc.idVendor =
+ __constant_cpu_to_le16(SIMPLE_VENDOR_NUM);
+ device_desc.idProduct =
+ __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM);
}
/* support optional vendor/distro customization */
EP_OUT_NAME = ep->name;
ep->driver_data = ep; /* claim */
+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
/* CDC Ethernet control interface doesn't require a status endpoint.
* Since some hosts expect one, try to allocate one anyway.
*/
"can't run RNDIS on %s\n",
gadget->name);
return -ENODEV;
-#ifdef DEV_CONFIG_CDC
} else if (cdc) {
control_intf.bNumEndpoints = 0;
/* FIXME remove endpoint from descriptor list */
-#endif
}
}
+#endif
/* one config: cdc, else minimal subset */
if (!cdc) {
device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
usb_gadget_set_selfpowered (gadget);
+ if (gadget->is_otg) {
+ otg_descriptor.bmAttributes |= USB_OTG_HNP,
+ eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+#ifdef CONFIG_USB_ETH_RNDIS
+ rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+#endif
+ }
+
net = alloc_etherdev (sizeof *dev);
if (!net)
return status;
dev->cdc = cdc;
dev->zlp = zlp;
- /* FIXME make these addresses configurable with module params.
- * also the manufacturer and product strings.
- */
-
- /* one random address for the gadget device ... both of these could
- * reasonably come from an id prom or a module parameter.
- */
- random_ether_addr(net->dev_addr);
-
- /* ... another address for the host, on the other end of the
- * link, gets exported through CDC (see CDC spec table 41)
- * and RNDIS.
+ /* 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.
*/
+ get_ether_addr(dev_addr, net->dev_addr);
if (cdc || rndis) {
- random_ether_addr(dev->host_mac);
+ get_ether_addr(host_addr, dev->host_mac);
#ifdef DEV_CONFIG_CDC
snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
dev->host_mac [0], dev->host_mac [1],
/*-------------------------------------------------------------------------*/
+static void
+eth_suspend (struct usb_gadget *gadget)
+{
+ struct eth_dev *dev = get_gadget_data (gadget);
+
+ DEBUG (dev, "suspend\n");
+ dev->suspended = 1;
+}
+
+static void
+eth_resume (struct usb_gadget *gadget)
+{
+ struct eth_dev *dev = get_gadget_data (gadget);
+
+ DEBUG (dev, "resume\n");
+ dev->suspended = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
static struct usb_gadget_driver eth_driver = {
#ifdef CONFIG_USB_GADGET_DUALSPEED
.speed = USB_SPEED_HIGH,
.setup = eth_setup,
.disconnect = eth_disconnect,
+ .suspend = eth_suspend,
+ .resume = eth_resume,
+
.driver = {
.name = (char *) shortname,
// .shutdown = ...