forked from Minki/linux
[PATCH] USB: usbnet (7/9) module for CDC Ethernet
Makes the CDC Ethernet support live in a separate driver module. This module is a bit special since it exports utility functions that are reused by the the Zaurus and RNDIS drivers, but it's not "core" like usbnet itself. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
0aa599c564
commit
4324fd4934
@ -137,35 +137,6 @@ config USB_PL2301
|
||||
Choose this option if you're using a host-to-host cable
|
||||
with one of these chips.
|
||||
|
||||
comment "Intelligent USB Devices/Gadgets"
|
||||
depends on USB_USBNET
|
||||
|
||||
config USB_CDCETHER
|
||||
boolean "CDC Ethernet support (smart devices such as cable modems)"
|
||||
depends on USB_USBNET
|
||||
default y
|
||||
help
|
||||
This option supports devices conforming to the Communication Device
|
||||
Class (CDC) Ethernet Control Model, a specification that's easy to
|
||||
implement in device firmware. The CDC specifications are available
|
||||
from <http://www.usb.org/>.
|
||||
|
||||
CDC Ethernet is an implementation option for DOCSIS cable modems
|
||||
that support USB connectivity, used for non-Microsoft USB hosts.
|
||||
The Linux-USB CDC Ethernet Gadget driver is an open implementation.
|
||||
This driver should work with at least the following devices:
|
||||
|
||||
* Ericsson PipeRider (all variants)
|
||||
* Motorola (DM100 and SB4100)
|
||||
* Broadcom Cable Modem (reference design)
|
||||
* Toshiba PCX1100U
|
||||
* ...
|
||||
|
||||
This driver creates an interface named "ethX", where X depends on
|
||||
what other networking devices you have in use. However, if the
|
||||
IEEE 802 "local assignment" bit is set in the address, a "usbX"
|
||||
name is used instead.
|
||||
|
||||
comment "Drivers built using the usbnet core"
|
||||
|
||||
config USB_NET_AX8817X
|
||||
@ -197,6 +168,32 @@ config USB_NET_AX8817X
|
||||
what other networking devices you have in use.
|
||||
|
||||
|
||||
config USB_NET_CDCETHER
|
||||
tristate "CDC Ethernet support (smart devices such as cable modems)"
|
||||
depends on USB_USBNET
|
||||
default y
|
||||
help
|
||||
This option supports devices conforming to the Communication Device
|
||||
Class (CDC) Ethernet Control Model, a specification that's easy to
|
||||
implement in device firmware. The CDC specifications are available
|
||||
from <http://www.usb.org/>.
|
||||
|
||||
CDC Ethernet is an implementation option for DOCSIS cable modems
|
||||
that support USB connectivity, used for non-Microsoft USB hosts.
|
||||
The Linux-USB CDC Ethernet Gadget driver is an open implementation.
|
||||
This driver should work with at least the following devices:
|
||||
|
||||
* Ericsson PipeRider (all variants)
|
||||
* Motorola (DM100 and SB4100)
|
||||
* Broadcom Cable Modem (reference design)
|
||||
* Toshiba PCX1100U
|
||||
* ...
|
||||
|
||||
This driver creates an interface named "ethX", where X depends on
|
||||
what other networking devices you have in use. However, if the
|
||||
IEEE 802 "local assignment" bit is set in the address, a "usbX"
|
||||
name is used instead.
|
||||
|
||||
config USB_NET_GL620A
|
||||
tristate "GeneSys GL620USB-A based cables"
|
||||
depends on USB_USBNET
|
||||
@ -280,6 +277,7 @@ config USB_EPSON2888
|
||||
config USB_NET_ZAURUS
|
||||
tristate "Sharp Zaurus (stock ROMs) and compatible"
|
||||
depends on USB_USBNET
|
||||
select USB_NET_CDCETHER
|
||||
select CRC32
|
||||
default y
|
||||
help
|
||||
|
@ -7,6 +7,7 @@ obj-$(CONFIG_USB_KAWETH) += kaweth.o
|
||||
obj-$(CONFIG_USB_PEGASUS) += pegasus.o
|
||||
obj-$(CONFIG_USB_RTL8150) += rtl8150.o
|
||||
obj-$(CONFIG_USB_NET_AX8817X) += asix.o
|
||||
obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o
|
||||
obj-$(CONFIG_USB_NET_GL620A) += gl620a.o
|
||||
obj-$(CONFIG_USB_NET_NET1080) += net1080.o
|
||||
obj-$(CONFIG_USB_NET_CDC_SUBSET) += cdc_subset.o
|
||||
|
509
drivers/usb/net/cdc_ether.c
Normal file
509
drivers/usb/net/cdc_ether.c
Normal file
@ -0,0 +1,509 @@
|
||||
/*
|
||||
* CDC Ethernet based networking peripherals
|
||||
* Copyright (C) 2003-2005 by David Brownell
|
||||
*
|
||||
* 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 DEBUG // error path messages, extra info
|
||||
// #define VERBOSE // more; success messages
|
||||
|
||||
#include <linux/config.h>
|
||||
#ifdef CONFIG_USB_DEBUG
|
||||
# define DEBUG
|
||||
#endif
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb_cdc.h>
|
||||
|
||||
#include "usbnet.h"
|
||||
|
||||
|
||||
/*
|
||||
* probes control interface, claims data interface, collects the bulk
|
||||
* endpoints, activates data interface (if needed), maybe sets MTU.
|
||||
* all pure cdc, except for certain firmware workarounds, and knowing
|
||||
* that rndis uses one different rule.
|
||||
*/
|
||||
int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
u8 *buf = intf->cur_altsetting->extra;
|
||||
int len = intf->cur_altsetting->extralen;
|
||||
struct usb_interface_descriptor *d;
|
||||
struct cdc_state *info = (void *) &dev->data;
|
||||
int status;
|
||||
int rndis;
|
||||
struct usb_driver *driver = driver_of(intf);
|
||||
|
||||
if (sizeof dev->data < sizeof *info)
|
||||
return -EDOM;
|
||||
|
||||
/* expect strict spec conformance for the descriptors, but
|
||||
* cope with firmware which stores them in the wrong place
|
||||
*/
|
||||
if (len == 0 && dev->udev->actconfig->extralen) {
|
||||
/* Motorola SB4100 (and others: Brad Hards says it's
|
||||
* from a Broadcom design) put CDC descriptors here
|
||||
*/
|
||||
buf = dev->udev->actconfig->extra;
|
||||
len = dev->udev->actconfig->extralen;
|
||||
if (len)
|
||||
dev_dbg(&intf->dev,
|
||||
"CDC descriptors on config\n");
|
||||
}
|
||||
|
||||
/* this assumes that if there's a non-RNDIS vendor variant
|
||||
* of cdc-acm, it'll fail RNDIS requests cleanly.
|
||||
*/
|
||||
rndis = (intf->cur_altsetting->desc.bInterfaceProtocol == 0xff);
|
||||
|
||||
memset(info, 0, sizeof *info);
|
||||
info->control = intf;
|
||||
while (len > 3) {
|
||||
if (buf [1] != USB_DT_CS_INTERFACE)
|
||||
goto next_desc;
|
||||
|
||||
/* use bDescriptorSubType to identify the CDC descriptors.
|
||||
* We expect devices with CDC header and union descriptors.
|
||||
* For CDC Ethernet we need the ethernet descriptor.
|
||||
* For RNDIS, ignore two (pointless) CDC modem descriptors
|
||||
* in favor of a complicated OID-based RPC scheme doing what
|
||||
* CDC Ethernet achieves with a simple descriptor.
|
||||
*/
|
||||
switch (buf [2]) {
|
||||
case USB_CDC_HEADER_TYPE:
|
||||
if (info->header) {
|
||||
dev_dbg(&intf->dev, "extra CDC header\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
info->header = (void *) buf;
|
||||
if (info->header->bLength != sizeof *info->header) {
|
||||
dev_dbg(&intf->dev, "CDC header len %u\n",
|
||||
info->header->bLength);
|
||||
goto bad_desc;
|
||||
}
|
||||
break;
|
||||
case USB_CDC_UNION_TYPE:
|
||||
if (info->u) {
|
||||
dev_dbg(&intf->dev, "extra CDC union\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
info->u = (void *) buf;
|
||||
if (info->u->bLength != sizeof *info->u) {
|
||||
dev_dbg(&intf->dev, "CDC union len %u\n",
|
||||
info->u->bLength);
|
||||
goto bad_desc;
|
||||
}
|
||||
|
||||
/* we need a master/control interface (what we're
|
||||
* probed with) and a slave/data interface; union
|
||||
* descriptors sort this all out.
|
||||
*/
|
||||
info->control = usb_ifnum_to_if(dev->udev,
|
||||
info->u->bMasterInterface0);
|
||||
info->data = usb_ifnum_to_if(dev->udev,
|
||||
info->u->bSlaveInterface0);
|
||||
if (!info->control || !info->data) {
|
||||
dev_dbg(&intf->dev,
|
||||
"master #%u/%p slave #%u/%p\n",
|
||||
info->u->bMasterInterface0,
|
||||
info->control,
|
||||
info->u->bSlaveInterface0,
|
||||
info->data);
|
||||
goto bad_desc;
|
||||
}
|
||||
if (info->control != intf) {
|
||||
dev_dbg(&intf->dev, "bogus CDC Union\n");
|
||||
/* Ambit USB Cable Modem (and maybe others)
|
||||
* interchanges master and slave interface.
|
||||
*/
|
||||
if (info->data == intf) {
|
||||
info->data = info->control;
|
||||
info->control = intf;
|
||||
} else
|
||||
goto bad_desc;
|
||||
}
|
||||
|
||||
/* a data interface altsetting does the real i/o */
|
||||
d = &info->data->cur_altsetting->desc;
|
||||
if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
|
||||
dev_dbg(&intf->dev, "slave class %u\n",
|
||||
d->bInterfaceClass);
|
||||
goto bad_desc;
|
||||
}
|
||||
break;
|
||||
case USB_CDC_ETHERNET_TYPE:
|
||||
if (info->ether) {
|
||||
dev_dbg(&intf->dev, "extra CDC ether\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
info->ether = (void *) buf;
|
||||
if (info->ether->bLength != sizeof *info->ether) {
|
||||
dev_dbg(&intf->dev, "CDC ether len %u\n",
|
||||
info->ether->bLength);
|
||||
goto bad_desc;
|
||||
}
|
||||
dev->hard_mtu = le16_to_cpu(
|
||||
info->ether->wMaxSegmentSize);
|
||||
/* because of Zaurus, we may be ignoring the host
|
||||
* side link address we were given.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
next_desc:
|
||||
len -= buf [0]; /* bLength */
|
||||
buf += buf [0];
|
||||
}
|
||||
|
||||
if (!info->header || !info->u || (!rndis && !info->ether)) {
|
||||
dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n",
|
||||
info->header ? "" : "header ",
|
||||
info->u ? "" : "union ",
|
||||
info->ether ? "" : "ether ");
|
||||
goto bad_desc;
|
||||
}
|
||||
|
||||
/* claim data interface and set it up ... with side effects.
|
||||
* network traffic can't flow until an altsetting is enabled.
|
||||
*/
|
||||
status = usb_driver_claim_interface(driver, info->data, dev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
status = usbnet_get_endpoints(dev, info->data);
|
||||
if (status < 0) {
|
||||
/* ensure immediate exit from usbnet_disconnect */
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface(driver, info->data);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */
|
||||
dev->status = NULL;
|
||||
if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
|
||||
dev->status = &info->control->cur_altsetting->endpoint [0];
|
||||
desc = &dev->status->desc;
|
||||
if (desc->bmAttributes != USB_ENDPOINT_XFER_INT
|
||||
|| !(desc->bEndpointAddress & USB_DIR_IN)
|
||||
|| (le16_to_cpu(desc->wMaxPacketSize)
|
||||
< sizeof(struct usb_cdc_notification))
|
||||
|| !desc->bInterval) {
|
||||
dev_dbg(&intf->dev, "bad notification endpoint\n");
|
||||
dev->status = NULL;
|
||||
}
|
||||
}
|
||||
if (rndis && !dev->status) {
|
||||
dev_dbg(&intf->dev, "missing RNDIS status endpoint\n");
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface(driver, info->data);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
|
||||
bad_desc:
|
||||
dev_info(&dev->udev->dev, "bad CDC descriptors\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_generic_cdc_bind);
|
||||
|
||||
void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
struct cdc_state *info = (void *) &dev->data;
|
||||
struct usb_driver *driver = driver_of(intf);
|
||||
|
||||
/* disconnect master --> disconnect slave */
|
||||
if (intf == info->control && info->data) {
|
||||
/* ensure immediate exit from usbnet_disconnect */
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface(driver, info->data);
|
||||
info->data = NULL;
|
||||
}
|
||||
|
||||
/* and vice versa (just in case) */
|
||||
else if (intf == info->data && info->control) {
|
||||
/* ensure immediate exit from usbnet_disconnect */
|
||||
usb_set_intfdata(info->control, NULL);
|
||||
usb_driver_release_interface(driver, info->control);
|
||||
info->control = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* Communications Device Class, Ethernet Control model
|
||||
*
|
||||
* Takes two interfaces. The DATA interface is inactive till an altsetting
|
||||
* is selected. Configuration data includes class descriptors. There's
|
||||
* an optional status endpoint on the control interface.
|
||||
*
|
||||
* This should interop with whatever the 2.4 "CDCEther.c" driver
|
||||
* (by Brad Hards) talked with, with more functionality.
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
static void dumpspeed(struct usbnet *dev, __le32 *speeds)
|
||||
{
|
||||
if (netif_msg_timer(dev))
|
||||
devinfo(dev, "link speeds: %u kbps up, %u kbps down",
|
||||
__le32_to_cpu(speeds[0]) / 1000,
|
||||
__le32_to_cpu(speeds[1]) / 1000);
|
||||
}
|
||||
|
||||
static void cdc_status(struct usbnet *dev, struct urb *urb)
|
||||
{
|
||||
struct usb_cdc_notification *event;
|
||||
|
||||
if (urb->actual_length < sizeof *event)
|
||||
return;
|
||||
|
||||
/* SPEED_CHANGE can get split into two 8-byte packets */
|
||||
if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) {
|
||||
dumpspeed(dev, (__le32 *) urb->transfer_buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
event = urb->transfer_buffer;
|
||||
switch (event->bNotificationType) {
|
||||
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
|
||||
if (netif_msg_timer(dev))
|
||||
devdbg(dev, "CDC: carrier %s",
|
||||
event->wValue ? "on" : "off");
|
||||
if (event->wValue)
|
||||
netif_carrier_on(dev->net);
|
||||
else
|
||||
netif_carrier_off(dev->net);
|
||||
break;
|
||||
case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */
|
||||
if (netif_msg_timer(dev))
|
||||
devdbg(dev, "CDC: speed change (len %d)",
|
||||
urb->actual_length);
|
||||
if (urb->actual_length != (sizeof *event + 8))
|
||||
set_bit(EVENT_STS_SPLIT, &dev->flags);
|
||||
else
|
||||
dumpspeed(dev, (__le32 *) &event[1]);
|
||||
break;
|
||||
/* USB_CDC_NOTIFY_RESPONSE_AVAILABLE can happen too (e.g. RNDIS),
|
||||
* but there are no standard formats for the response data.
|
||||
*/
|
||||
default:
|
||||
deverr(dev, "CDC: unexpected notification %02x!",
|
||||
event->bNotificationType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 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 inline int
|
||||
get_ethernet_addr(struct usbnet *dev, struct usb_cdc_ether_desc *e)
|
||||
{
|
||||
int tmp, i;
|
||||
unsigned char buf [13];
|
||||
|
||||
tmp = usb_string(dev->udev, e->iMACAddress, buf, sizeof buf);
|
||||
if (tmp != 12) {
|
||||
dev_dbg(&dev->udev->dev,
|
||||
"bad MAC string %d fetch, %d\n", e->iMACAddress, tmp);
|
||||
if (tmp >= 0)
|
||||
tmp = -EINVAL;
|
||||
return tmp;
|
||||
}
|
||||
for (i = tmp = 0; i < 6; i++, tmp += 2)
|
||||
dev->net->dev_addr [i] =
|
||||
(nibble(buf [tmp]) << 4) + nibble(buf [tmp + 1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
int status;
|
||||
struct cdc_state *info = (void *) &dev->data;
|
||||
|
||||
status = usbnet_generic_cdc_bind(dev, intf);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
status = get_ethernet_addr(dev, info->ether);
|
||||
if (status < 0) {
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface(driver_of(intf), info->data);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* FIXME cdc-ether has some multicast code too, though it complains
|
||||
* in routine cases. info->ether describes the multicast support.
|
||||
* Implement that here, manipulating the cdc filter as needed.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct driver_info cdc_info = {
|
||||
.description = "CDC Ethernet Device",
|
||||
.flags = FLAG_ETHER,
|
||||
// .check_connect = cdc_check_connect,
|
||||
.bind = cdc_bind,
|
||||
.unbind = usbnet_cdc_unbind,
|
||||
.status = cdc_status,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
static const struct usb_device_id products [] = {
|
||||
/*
|
||||
* BLACKLIST !!
|
||||
*
|
||||
* First blacklist any products that are egregiously nonconformant
|
||||
* with the CDC Ethernet specs. Minor braindamage we cope with; when
|
||||
* they're not even trying, needing a separate driver is only the first
|
||||
* of the differences to show up.
|
||||
*/
|
||||
|
||||
#define ZAURUS_MASTER_INTERFACE \
|
||||
.bInterfaceClass = USB_CLASS_COMM, \
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \
|
||||
.bInterfaceProtocol = USB_CDC_PROTO_NONE
|
||||
|
||||
/* SA-1100 based Sharp Zaurus ("collie"), or compatible;
|
||||
* wire-incompatible with true CDC Ethernet implementations.
|
||||
* (And, it seems, needlessly so...)
|
||||
*/
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8004,
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
},
|
||||
|
||||
/* PXA-25x based Sharp Zaurii. Note that it seems some of these
|
||||
* (later models especially) may have shipped only with firmware
|
||||
* advertising false "CDC MDLM" compatibility ... but we're not
|
||||
* clear which models did that, so for now let's assume the worst.
|
||||
*/
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8005, /* A-300 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8006, /* B-500/SL-5600 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x8007, /* C-700 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x9031, /* C-750 C-760 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
.idProduct = 0x9032, /* SL-6000 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
}, {
|
||||
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
|
||||
| USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = 0x04DD,
|
||||
/* reported with some C860 units */
|
||||
.idProduct = 0x9050, /* C-860 */
|
||||
ZAURUS_MASTER_INTERFACE,
|
||||
.driver_info = 0,
|
||||
},
|
||||
|
||||
/*
|
||||
* WHITELIST!!!
|
||||
*
|
||||
* CDC Ether uses two interfaces, not necessarily consecutive.
|
||||
* We match the main interface, ignoring the optional device
|
||||
* class so we could handle devices that aren't exclusively
|
||||
* CDC ether.
|
||||
*
|
||||
* NOTE: this match must come AFTER entries blacklisting devices
|
||||
* because of bugs/quirks in a given product (like Zaurus, above).
|
||||
*/
|
||||
{
|
||||
USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
|
||||
USB_CDC_PROTO_NONE),
|
||||
.driver_info = (unsigned long) &cdc_info,
|
||||
},
|
||||
{ }, // END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, products);
|
||||
|
||||
static struct usb_driver cdc_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "cdc_ether",
|
||||
.id_table = products,
|
||||
.probe = usbnet_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
};
|
||||
|
||||
|
||||
static int __init cdc_init(void)
|
||||
{
|
||||
BUG_ON((sizeof(((struct usbnet *)0)->data)
|
||||
< sizeof(struct cdc_state)));
|
||||
|
||||
return usb_register(&cdc_driver);
|
||||
}
|
||||
module_init(cdc_init);
|
||||
|
||||
static void __exit cdc_exit(void)
|
||||
{
|
||||
usb_deregister(&cdc_driver);
|
||||
}
|
||||
module_exit(cdc_exit);
|
||||
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
MODULE_DESCRIPTION("USB CDC Ethernet devices");
|
||||
MODULE_LICENSE("GPL");
|
@ -300,377 +300,6 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_skb_return);
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* Communications Device Class declarations.
|
||||
* Used by CDC Ethernet, and some CDC variants
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_USB_CDCETHER
|
||||
#define NEED_GENERIC_CDC
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_USB_ZAURUS) || defined(CONFIG_USB_ZAURUS_MODULE)
|
||||
/* Ethernet variant uses funky framing, broken ethernet addressing */
|
||||
#define NEED_GENERIC_CDC
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_RNDIS
|
||||
/* ACM variant uses even funkier framing, complex control RPC scheme */
|
||||
#define NEED_GENERIC_CDC
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef NEED_GENERIC_CDC
|
||||
|
||||
#include <linux/usb_cdc.h>
|
||||
|
||||
static struct usb_driver usbnet_driver;
|
||||
|
||||
/*
|
||||
* probes control interface, claims data interface, collects the bulk
|
||||
* endpoints, activates data interface (if needed), maybe sets MTU.
|
||||
* all pure cdc, except for certain firmware workarounds.
|
||||
*/
|
||||
int usbnet_generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
u8 *buf = intf->cur_altsetting->extra;
|
||||
int len = intf->cur_altsetting->extralen;
|
||||
struct usb_interface_descriptor *d;
|
||||
struct cdc_state *info = (void *) &dev->data;
|
||||
int status;
|
||||
int rndis;
|
||||
|
||||
if (sizeof dev->data < sizeof *info)
|
||||
return -EDOM;
|
||||
|
||||
/* expect strict spec conformance for the descriptors, but
|
||||
* cope with firmware which stores them in the wrong place
|
||||
*/
|
||||
if (len == 0 && dev->udev->actconfig->extralen) {
|
||||
/* Motorola SB4100 (and others: Brad Hards says it's
|
||||
* from a Broadcom design) put CDC descriptors here
|
||||
*/
|
||||
buf = dev->udev->actconfig->extra;
|
||||
len = dev->udev->actconfig->extralen;
|
||||
if (len)
|
||||
dev_dbg (&intf->dev,
|
||||
"CDC descriptors on config\n");
|
||||
}
|
||||
|
||||
/* this assumes that if there's a non-RNDIS vendor variant
|
||||
* of cdc-acm, it'll fail RNDIS requests cleanly.
|
||||
*/
|
||||
rndis = (intf->cur_altsetting->desc.bInterfaceProtocol == 0xff);
|
||||
|
||||
memset (info, 0, sizeof *info);
|
||||
info->control = intf;
|
||||
while (len > 3) {
|
||||
if (buf [1] != USB_DT_CS_INTERFACE)
|
||||
goto next_desc;
|
||||
|
||||
/* use bDescriptorSubType to identify the CDC descriptors.
|
||||
* We expect devices with CDC header and union descriptors.
|
||||
* For CDC Ethernet we need the ethernet descriptor.
|
||||
* For RNDIS, ignore two (pointless) CDC modem descriptors
|
||||
* in favor of a complicated OID-based RPC scheme doing what
|
||||
* CDC Ethernet achieves with a simple descriptor.
|
||||
*/
|
||||
switch (buf [2]) {
|
||||
case USB_CDC_HEADER_TYPE:
|
||||
if (info->header) {
|
||||
dev_dbg (&intf->dev, "extra CDC header\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
info->header = (void *) buf;
|
||||
if (info->header->bLength != sizeof *info->header) {
|
||||
dev_dbg (&intf->dev, "CDC header len %u\n",
|
||||
info->header->bLength);
|
||||
goto bad_desc;
|
||||
}
|
||||
break;
|
||||
case USB_CDC_UNION_TYPE:
|
||||
if (info->u) {
|
||||
dev_dbg (&intf->dev, "extra CDC union\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
info->u = (void *) buf;
|
||||
if (info->u->bLength != sizeof *info->u) {
|
||||
dev_dbg (&intf->dev, "CDC union len %u\n",
|
||||
info->u->bLength);
|
||||
goto bad_desc;
|
||||
}
|
||||
|
||||
/* we need a master/control interface (what we're
|
||||
* probed with) and a slave/data interface; union
|
||||
* descriptors sort this all out.
|
||||
*/
|
||||
info->control = usb_ifnum_to_if(dev->udev,
|
||||
info->u->bMasterInterface0);
|
||||
info->data = usb_ifnum_to_if(dev->udev,
|
||||
info->u->bSlaveInterface0);
|
||||
if (!info->control || !info->data) {
|
||||
dev_dbg (&intf->dev,
|
||||
"master #%u/%p slave #%u/%p\n",
|
||||
info->u->bMasterInterface0,
|
||||
info->control,
|
||||
info->u->bSlaveInterface0,
|
||||
info->data);
|
||||
goto bad_desc;
|
||||
}
|
||||
if (info->control != intf) {
|
||||
dev_dbg (&intf->dev, "bogus CDC Union\n");
|
||||
/* Ambit USB Cable Modem (and maybe others)
|
||||
* interchanges master and slave interface.
|
||||
*/
|
||||
if (info->data == intf) {
|
||||
info->data = info->control;
|
||||
info->control = intf;
|
||||
} else
|
||||
goto bad_desc;
|
||||
}
|
||||
|
||||
/* a data interface altsetting does the real i/o */
|
||||
d = &info->data->cur_altsetting->desc;
|
||||
if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
|
||||
dev_dbg (&intf->dev, "slave class %u\n",
|
||||
d->bInterfaceClass);
|
||||
goto bad_desc;
|
||||
}
|
||||
break;
|
||||
case USB_CDC_ETHERNET_TYPE:
|
||||
if (info->ether) {
|
||||
dev_dbg (&intf->dev, "extra CDC ether\n");
|
||||
goto bad_desc;
|
||||
}
|
||||
info->ether = (void *) buf;
|
||||
if (info->ether->bLength != sizeof *info->ether) {
|
||||
dev_dbg (&intf->dev, "CDC ether len %u\n",
|
||||
info->ether->bLength);
|
||||
goto bad_desc;
|
||||
}
|
||||
dev->hard_mtu = le16_to_cpu(
|
||||
info->ether->wMaxSegmentSize);
|
||||
/* because of Zaurus, we may be ignoring the host
|
||||
* side link address we were given.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
next_desc:
|
||||
len -= buf [0]; /* bLength */
|
||||
buf += buf [0];
|
||||
}
|
||||
|
||||
if (!info->header || !info->u || (!rndis && !info->ether)) {
|
||||
dev_dbg (&intf->dev, "missing cdc %s%s%sdescriptor\n",
|
||||
info->header ? "" : "header ",
|
||||
info->u ? "" : "union ",
|
||||
info->ether ? "" : "ether ");
|
||||
goto bad_desc;
|
||||
}
|
||||
|
||||
/* claim data interface and set it up ... with side effects.
|
||||
* network traffic can't flow until an altsetting is enabled.
|
||||
*/
|
||||
status = usb_driver_claim_interface (&usbnet_driver, info->data, dev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
status = usbnet_get_endpoints (dev, info->data);
|
||||
if (status < 0) {
|
||||
/* ensure immediate exit from usbnet_disconnect */
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface (&usbnet_driver, info->data);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */
|
||||
dev->status = NULL;
|
||||
if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {
|
||||
struct usb_endpoint_descriptor *desc;
|
||||
|
||||
dev->status = &info->control->cur_altsetting->endpoint [0];
|
||||
desc = &dev->status->desc;
|
||||
if (desc->bmAttributes != USB_ENDPOINT_XFER_INT
|
||||
|| !(desc->bEndpointAddress & USB_DIR_IN)
|
||||
|| (le16_to_cpu(desc->wMaxPacketSize)
|
||||
< sizeof (struct usb_cdc_notification))
|
||||
|| !desc->bInterval) {
|
||||
dev_dbg (&intf->dev, "bad notification endpoint\n");
|
||||
dev->status = NULL;
|
||||
}
|
||||
}
|
||||
if (rndis && !dev->status) {
|
||||
dev_dbg (&intf->dev, "missing RNDIS status endpoint\n");
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface (&usbnet_driver, info->data);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
|
||||
bad_desc:
|
||||
dev_info (&dev->udev->dev, "bad CDC descriptors\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_generic_cdc_bind);
|
||||
|
||||
void usbnet_cdc_unbind (struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
struct cdc_state *info = (void *) &dev->data;
|
||||
|
||||
/* disconnect master --> disconnect slave */
|
||||
if (intf == info->control && info->data) {
|
||||
/* ensure immediate exit from usbnet_disconnect */
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface (&usbnet_driver, info->data);
|
||||
info->data = NULL;
|
||||
}
|
||||
|
||||
/* and vice versa (just in case) */
|
||||
else if (intf == info->data && info->control) {
|
||||
/* ensure immediate exit from usbnet_disconnect */
|
||||
usb_set_intfdata(info->control, NULL);
|
||||
usb_driver_release_interface (&usbnet_driver, info->control);
|
||||
info->control = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usbnet_cdc_unbind);
|
||||
|
||||
#endif /* NEED_GENERIC_CDC */
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_CDCETHER
|
||||
#define HAVE_HARDWARE
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* Communications Device Class, Ethernet Control model
|
||||
*
|
||||
* Takes two interfaces. The DATA interface is inactive till an altsetting
|
||||
* is selected. Configuration data includes class descriptors.
|
||||
*
|
||||
* This should interop with whatever the 2.4 "CDCEther.c" driver
|
||||
* (by Brad Hards) talked with.
|
||||
*
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
|
||||
|
||||
static void dumpspeed (struct usbnet *dev, __le32 *speeds)
|
||||
{
|
||||
if (netif_msg_timer (dev))
|
||||
devinfo (dev, "link speeds: %u kbps up, %u kbps down",
|
||||
__le32_to_cpu(speeds[0]) / 1000,
|
||||
__le32_to_cpu(speeds[1]) / 1000);
|
||||
}
|
||||
|
||||
static void cdc_status (struct usbnet *dev, struct urb *urb)
|
||||
{
|
||||
struct usb_cdc_notification *event;
|
||||
|
||||
if (urb->actual_length < sizeof *event)
|
||||
return;
|
||||
|
||||
/* SPEED_CHANGE can get split into two 8-byte packets */
|
||||
if (test_and_clear_bit (EVENT_STS_SPLIT, &dev->flags)) {
|
||||
dumpspeed (dev, (__le32 *) urb->transfer_buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
event = urb->transfer_buffer;
|
||||
switch (event->bNotificationType) {
|
||||
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
|
||||
if (netif_msg_timer (dev))
|
||||
devdbg (dev, "CDC: carrier %s",
|
||||
event->wValue ? "on" : "off");
|
||||
if (event->wValue)
|
||||
netif_carrier_on(dev->net);
|
||||
else
|
||||
netif_carrier_off(dev->net);
|
||||
break;
|
||||
case USB_CDC_NOTIFY_SPEED_CHANGE: /* tx/rx rates */
|
||||
if (netif_msg_timer (dev))
|
||||
devdbg (dev, "CDC: speed change (len %d)",
|
||||
urb->actual_length);
|
||||
if (urb->actual_length != (sizeof *event + 8))
|
||||
set_bit (EVENT_STS_SPLIT, &dev->flags);
|
||||
else
|
||||
dumpspeed (dev, (__le32 *) &event[1]);
|
||||
break;
|
||||
// case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: /* RNDIS; or unsolicited */
|
||||
default:
|
||||
deverr (dev, "CDC: unexpected notification %02x!",
|
||||
event->bNotificationType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 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 inline int
|
||||
get_ethernet_addr (struct usbnet *dev, struct usb_cdc_ether_desc *e)
|
||||
{
|
||||
int tmp, i;
|
||||
unsigned char buf [13];
|
||||
|
||||
tmp = usb_string (dev->udev, e->iMACAddress, buf, sizeof buf);
|
||||
if (tmp != 12) {
|
||||
dev_dbg (&dev->udev->dev,
|
||||
"bad MAC string %d fetch, %d\n", e->iMACAddress, tmp);
|
||||
if (tmp >= 0)
|
||||
tmp = -EINVAL;
|
||||
return tmp;
|
||||
}
|
||||
for (i = tmp = 0; i < 6; i++, tmp += 2)
|
||||
dev->net->dev_addr [i] =
|
||||
(nibble (buf [tmp]) << 4) + nibble (buf [tmp + 1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdc_bind (struct usbnet *dev, struct usb_interface *intf)
|
||||
{
|
||||
int status;
|
||||
struct cdc_state *info = (void *) &dev->data;
|
||||
|
||||
status = usbnet_generic_cdc_bind (dev, intf);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
status = get_ethernet_addr (dev, info->ether);
|
||||
if (status < 0) {
|
||||
usb_set_intfdata(info->data, NULL);
|
||||
usb_driver_release_interface (&usbnet_driver, info->data);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* FIXME cdc-ether has some multicast code too, though it complains
|
||||
* in routine cases. info->ether describes the multicast support.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct driver_info cdc_info = {
|
||||
.description = "CDC Ethernet Device",
|
||||
.flags = FLAG_ETHER,
|
||||
// .check_connect = cdc_check_connect,
|
||||
.bind = cdc_bind,
|
||||
.unbind = usbnet_cdc_unbind,
|
||||
.status = cdc_status,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_USB_CDCETHER */
|
||||
|
||||
|
||||
|
||||
#ifdef CONFIG_USB_PL2301
|
||||
#define HAVE_HARDWARE
|
||||
@ -1754,23 +1383,6 @@ static const struct usb_device_id products [] = {
|
||||
.driver_info = (unsigned long) &rndis_info,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_CDCETHER
|
||||
{
|
||||
/* CDC Ether uses two interfaces, not necessarily consecutive.
|
||||
* We match the main interface, ignoring the optional device
|
||||
* class so we could handle devices that aren't exclusively
|
||||
* CDC ether.
|
||||
*
|
||||
* NOTE: this match must come AFTER entries working around
|
||||
* bugs/quirks in a given product (like Zaurus, above).
|
||||
*/
|
||||
USB_INTERFACE_INFO (USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
|
||||
USB_CDC_PROTO_NONE),
|
||||
.driver_info = (unsigned long) &cdc_info,
|
||||
},
|
||||
#endif
|
||||
|
||||
{ }, // END
|
||||
};
|
||||
MODULE_DEVICE_TABLE (usb, products);
|
||||
@ -1792,10 +1404,6 @@ static int __init usbnet_init(void)
|
||||
// compiler should optimize these out
|
||||
BUG_ON (sizeof (((struct sk_buff *)0)->cb)
|
||||
< sizeof (struct skb_data));
|
||||
#ifdef CONFIG_USB_CDCETHER
|
||||
BUG_ON ((sizeof (((struct usbnet *)0)->data)
|
||||
< sizeof (struct cdc_state)));
|
||||
#endif
|
||||
|
||||
random_ether_addr(node_id);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user