/*
* USB Networking Links
- * Copyright (C) 2000-2003 by David Brownell <dbrownell@users.sourceforge.net>
+ * Copyright (C) 2000-2005 by David Brownell <dbrownell@users.sourceforge.net>
* Copyright (C) 2002 Pavel Machek <pavel@ucw.cz>
* Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
* Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
# define EVENT_RX_HALT 1
# define EVENT_RX_MEMORY 2
# define EVENT_STS_SPLIT 3
+# define EVENT_LINK_RESET 4
};
// device-specific info used by the driver
/* for status polling */
void (*status)(struct usbnet *, struct urb *);
+ /* link reset handling, called from defer_kevent */
+ int (*link_reset)(struct usbnet *);
+
/* fixup rx packet (strip framing) */
int (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
static u32 usbnet_get_link (struct net_device *);
static u32 usbnet_get_msglevel (struct net_device *);
static void usbnet_set_msglevel (struct net_device *, u32);
+static void defer_kevent (struct usbnet *, int);
/* mostly for PDA style devices, which are always connected if present */
static int always_connected (struct usbnet *dev)
dev->stats.rx_bytes += skb->len;
if (netif_msg_rx_status (dev))
- devdbg (dev, "< rx, len %d, type 0x%x",
+ devdbg (dev, "< rx, len %zu, type 0x%x",
skb->len + sizeof (struct ethhdr), skb->protocol);
memset (skb->cb, 0, sizeof (struct skb_data));
status = netif_rx (skb);
#define AX_CMD_WRITE_MULTI_FILTER 0x16
#define AX_CMD_READ_NODE_ID 0x17
#define AX_CMD_READ_PHY_ID 0x19
+#define AX_CMD_READ_MEDIUM_STATUS 0x1a
#define AX_CMD_WRITE_MEDIUM_MODE 0x1b
#define AX_CMD_READ_MONITOR_MODE 0x1c
#define AX_CMD_WRITE_MONITOR_MODE 0x1d
#define AX_MONITOR_MAGIC 0x04
#define AX_MONITOR_HSFS 0x10
+/* AX88172 Medium Status Register values */
+#define AX_MEDIUM_FULL_DUPLEX 0x02
+#define AX_MEDIUM_TX_ABORT_ALLOW 0x04
+#define AX_MEDIUM_FLOW_CONTROL_EN 0x10
+
#define AX_MCAST_FILTER_SIZE 8
#define AX_MAX_MCAST 64
-#define AX_INTERRUPT_BUFSIZE 8
-
#define AX_EEPROM_LEN 0x40
#define AX_SWRESET_CLEAR 0x00
#define AX88772_IPG1_DEFAULT 0x0c
#define AX88772_IPG2_DEFAULT 0x12
+#define AX88772_MEDIUM_FULL_DUPLEX 0x0002
+#define AX88772_MEDIUM_RESERVED 0x0004
+#define AX88772_MEDIUM_RX_FC_ENABLE 0x0010
+#define AX88772_MEDIUM_TX_FC_ENABLE 0x0020
+#define AX88772_MEDIUM_PAUSE_FORMAT 0x0080
+#define AX88772_MEDIUM_RX_ENABLE 0x0100
+#define AX88772_MEDIUM_100MB 0x0200
+#define AX88772_MEDIUM_DEFAULT \
+ (AX88772_MEDIUM_FULL_DUPLEX | AX88772_MEDIUM_RX_FC_ENABLE | \
+ AX88772_MEDIUM_TX_FC_ENABLE | AX88772_MEDIUM_100MB | \
+ AX88772_MEDIUM_RESERVED | AX88772_MEDIUM_RX_ENABLE )
+
#define AX_EEPROM_MAGIC 0xdeadbeef
/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
struct ax8817x_data {
u8 multi_filter[AX_MCAST_FILTER_SIZE];
- struct urb *int_urb;
- u8 *int_buf;
};
+struct ax88172_int_data {
+ u16 res1;
+ u8 link;
+ u16 res2;
+ u8 status;
+ u16 res3;
+} __attribute__ ((packed));
+
static int ax8817x_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
u16 size, void *data)
{
usb_free_urb(urb);
}
-static void ax8817x_interrupt_complete(struct urb *urb, struct pt_regs *regs)
+static void ax8817x_status(struct usbnet *dev, struct urb *urb)
{
- struct usbnet *dev = (struct usbnet *)urb->context;
- struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+ struct ax88172_int_data *event;
int link;
- if (urb->status < 0) {
- devdbg(dev,"ax8817x_interrupt_complete() failed with %d",
- urb->status);
- } else {
- link = data->int_buf[2] & 0x01;
- if (netif_carrier_ok(dev->net) != link) {
- if (link)
- netif_carrier_on(dev->net);
- else
- netif_carrier_off(dev->net);
- devdbg(dev, "ax8817x - Link Status is: %d", link);
- }
- usb_submit_urb(data->int_urb, GFP_ATOMIC);
+ if (urb->actual_length < 8)
+ return;
+
+ event = urb->transfer_buffer;
+ link = event->link & 0x01;
+ if (netif_carrier_ok(dev->net) != link) {
+ if (link) {
+ netif_carrier_on(dev->net);
+ defer_kevent (dev, EVENT_LINK_RESET );
+ } else
+ netif_carrier_off(dev->net);
+ devdbg(dev, "ax8817x - Link Status is: %d", link);
}
}
ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
}
+static int ax88172_link_reset(struct usbnet *dev)
+{
+ u16 lpa;
+ u8 mode;
+
+ mode = AX_MEDIUM_TX_ABORT_ALLOW | AX_MEDIUM_FLOW_CONTROL_EN;
+ lpa = ax8817x_mdio_read(dev->net, dev->mii.phy_id, MII_LPA);
+ if (lpa & LPA_DUPLEX)
+ mode |= AX_MEDIUM_FULL_DUPLEX;
+ ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+
+ return 0;
+}
+
static void ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
{
struct usbnet *dev = netdev_priv(net);
void *buf;
int i;
unsigned long gpio_bits = dev->driver_info->data;
- struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
get_endpoints(dev,intf);
- if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == NULL) {
- dbg ("%s: cannot allocate interrupt URB",
- dev->net->name);
- ret = -ENOMEM;
- goto out1;
- }
-
- if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) {
- dbg ("%s: cannot allocate memory for interrupt buffer",
- dev->net->name);
- ret = -ENOMEM;
- goto out1;
- }
- memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE);
-
- usb_fill_int_urb (data->int_urb, dev->udev,
- usb_rcvintpipe (dev->udev, 1),
- data->int_buf, AX_INTERRUPT_BUFSIZE,
- ax8817x_interrupt_complete, dev,
- dev->udev->speed == USB_SPEED_HIGH ? 8 : 100);
-
buf = kmalloc(ETH_ALEN, GFP_KERNEL);
if(!buf) {
ret = -ENOMEM;
- goto out2;
+ goto out1;
}
/* Toggle the GPIOs in a manufacturer/model specific way */
if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
(gpio_bits >> (i * 8)) & 0xff, 0, 0,
buf)) < 0)
- goto out3;
+ goto out2;
msleep(5);
}
if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x80, 0, 0, buf)) < 0) {
dbg("send AX_CMD_WRITE_RX_CTL failed: %d", ret);
- goto out3;
+ goto out2;
}
/* Get the MAC address */
memset(buf, 0, ETH_ALEN);
if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, 6, buf)) < 0) {
dbg("read AX_CMD_READ_NODE_ID failed: %d", ret);
- goto out3;
+ goto out2;
}
memcpy(dev->net->dev_addr, buf, ETH_ALEN);
/* Get the PHY id */
if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) {
dbg("error on read AX_CMD_READ_PHY_ID: %02x", ret);
- goto out3;
+ goto out2;
} else if (ret < 2) {
/* this should always return 2 bytes */
dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x", ret);
ret = -EIO;
- goto out3;
+ goto out2;
}
/* Initialize MII structure */
dev->net->set_multicast_list = ax8817x_set_multicast;
dev->net->ethtool_ops = &ax8817x_ethtool_ops;
- ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
- cpu_to_le16(BMCR_RESET));
+ ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
- cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA | 0x0400));
+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
mii_nway_restart(&dev->mii);
- if((ret = usb_submit_urb(data->int_urb, GFP_KERNEL)) < 0) {
- dbg("Failed to submit interrupt URB: %02x", ret);
- goto out2;
- }
-
return 0;
-out3:
- kfree(buf);
out2:
- kfree(data->int_buf);
+ kfree(buf);
out1:
- usb_free_urb(data->int_urb);
return ret;
}
-static void ax8817x_unbind(struct usbnet *dev, struct usb_interface *intf)
-{
- struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
-
- usb_kill_urb(data->int_urb);
- usb_free_urb(data->int_urb);
- kfree(data->int_buf);
-}
-
static struct ethtool_ops ax88772_ethtool_ops = {
.get_drvinfo = ax8817x_get_drvinfo,
.get_link = ethtool_op_get_link,
{
int ret;
void *buf;
- struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
get_endpoints(dev,intf);
- if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == 0) {
- dbg ("Cannot allocate interrupt URB");
- ret = -ENOMEM;
- goto out1;
- }
-
- if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) {
- dbg ("Cannot allocate memory for interrupt buffer");
- ret = -ENOMEM;
- goto out1;
- }
- memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE);
-
- usb_fill_int_urb (data->int_urb, dev->udev,
- usb_rcvintpipe (dev->udev, 1),
- data->int_buf, AX_INTERRUPT_BUFSIZE,
- ax8817x_interrupt_complete, dev,
- dev->udev->speed == USB_SPEED_HIGH ? 8 : 100);
-
buf = kmalloc(6, GFP_KERNEL);
if(!buf) {
dbg ("Cannot allocate memory for buffer");
ret = -ENOMEM;
- goto out2;
+ goto out1;
}
if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
0x00B0, 0, 0, buf)) < 0)
- goto out3;
+ goto out2;
msleep(5);
if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0x0001, 0, 0, buf)) < 0) {
dbg("Select PHY #1 failed: %d", ret);
- goto out3;
+ goto out2;
}
if ((ret =
ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD, 0, 0, buf)) < 0) {
dbg("Failed to power down internal PHY: %d", ret);
- goto out3;
+ goto out2;
}
msleep(150);
if ((ret =
ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR, 0, 0, buf)) < 0) {
dbg("Failed to perform software reset: %d", ret);
- goto out3;
+ goto out2;
}
msleep(150);
if ((ret =
ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | AX_SWRESET_PRL, 0, 0, buf)) < 0) {
dbg("Failed to set Internal/External PHY reset control: %d", ret);
- goto out3;
+ goto out2;
}
msleep(150);
ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0000, 0, 0,
buf)) < 0) {
dbg("Failed to reset RX_CTL: %d", ret);
- goto out3;
+ goto out2;
}
/* Get the MAC address */
memset(buf, 0, ETH_ALEN);
if ((ret = ax8817x_read_cmd(dev, AX88772_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf)) < 0) {
dbg("Failed to read MAC address: %d", ret);
- goto out3;
+ goto out2;
}
memcpy(dev->net->dev_addr, buf, ETH_ALEN);
if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, buf)) < 0) {
dbg("Enabling software MII failed: %d", ret);
- goto out3;
+ goto out2;
}
if (((ret =
ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, 0x0010, 2, 2, buf)) < 0)
|| (*((u16 *)buf) != 0x003b)) {
dbg("Read PHY register 2 must be 0x3b00: %d", ret);
- goto out3;
+ goto out2;
}
/* Initialize MII structure */
/* Get the PHY id */
if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) {
dbg("Error reading PHY ID: %02x", ret);
- goto out3;
+ goto out2;
} else if (ret < 2) {
/* this should always return 2 bytes */
dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x",
ret);
ret = -EIO;
- goto out3;
+ goto out2;
}
dev->mii.phy_id = *((u8 *)buf + 1);
if ((ret =
ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_PRL, 0, 0, buf)) < 0) {
dbg("Set external PHY reset pin level: %d", ret);
- goto out3;
+ goto out2;
}
msleep(150);
if ((ret =
ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | AX_SWRESET_PRL, 0, 0, buf)) < 0) {
dbg("Set Internal/External PHY reset control: %d", ret);
- goto out3;
+ goto out2;
}
msleep(150);
dev->net->set_multicast_list = ax8817x_set_multicast;
dev->net->ethtool_ops = &ax88772_ethtool_ops;
- ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
- cpu_to_le16(BMCR_RESET));
+ ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
- cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA));
+ ADVERTISE_ALL | ADVERTISE_CSMA);
mii_nway_restart(&dev->mii);
- if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, 0x0336, 0, 0, buf)) < 0) {
+ if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, AX88772_MEDIUM_DEFAULT, 0, 0, buf)) < 0) {
dbg("Write medium mode register: %d", ret);
- goto out3;
+ goto out2;
}
if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0, AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,AX88772_IPG2_DEFAULT, 0, buf)) < 0) {
dbg("Write IPG,IPG1,IPG2 failed: %d", ret);
- goto out3;
+ goto out2;
}
if ((ret =
ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf)) < 0) {
dbg("Failed to set hardware MII: %02x", ret);
- goto out3;
+ goto out2;
}
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0088, 0, 0,
buf)) < 0) {
dbg("Reset RX_CTL failed: %d", ret);
- goto out3;
- }
-
- if((ret = usb_submit_urb(data->int_urb, GFP_KERNEL)) < 0) {
- dbg("Failed to submit interrupt URB: %02x", ret);
- goto out3;
+ goto out2;
}
kfree(buf);
return 0;
-out3:
- kfree(buf);
out2:
- kfree(data->int_buf);
+ kfree(buf);
out1:
- usb_free_urb(data->int_urb);
-
return ret;
}
return skb;
}
+static int ax88772_link_reset(struct usbnet *dev)
+{
+ u16 lpa;
+ u16 mode;
+
+ mode = AX88772_MEDIUM_DEFAULT;
+ lpa = ax8817x_mdio_read(dev->net, dev->mii.phy_id, MII_LPA);
+
+ if ((lpa & LPA_DUPLEX) == 0)
+ mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
+ if ((lpa & LPA_100) == 0)
+ mode &= ~AX88772_MEDIUM_100MB;
+ ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+
+ return 0;
+}
+
static const struct driver_info ax8817x_info = {
.description = "ASIX AX8817x USB 2.0 Ethernet",
.bind = ax8817x_bind,
- .unbind = ax8817x_unbind,
+ .status = ax8817x_status,
+ .link_reset = ax88172_link_reset,
+ .reset = ax88172_link_reset,
.flags = FLAG_ETHER,
.data = 0x00130103,
};
static const struct driver_info dlink_dub_e100_info = {
.description = "DLink DUB-E100 USB Ethernet",
.bind = ax8817x_bind,
- .unbind = ax8817x_unbind,
+ .status = ax8817x_status,
+ .link_reset = ax88172_link_reset,
+ .reset = ax88172_link_reset,
.flags = FLAG_ETHER,
.data = 0x009f9d9f,
};
static const struct driver_info netgear_fa120_info = {
.description = "Netgear FA-120 USB Ethernet",
.bind = ax8817x_bind,
- .unbind = ax8817x_unbind,
+ .status = ax8817x_status,
+ .link_reset = ax88172_link_reset,
+ .reset = ax88172_link_reset,
.flags = FLAG_ETHER,
.data = 0x00130103,
};
static const struct driver_info hawking_uf200_info = {
.description = "Hawking UF200 USB Ethernet",
.bind = ax8817x_bind,
- .unbind = ax8817x_unbind,
+ .status = ax8817x_status,
+ .link_reset = ax88172_link_reset,
+ .reset = ax88172_link_reset,
.flags = FLAG_ETHER,
.data = 0x001f1d1f,
};
static const struct driver_info ax88772_info = {
.description = "ASIX AX88772 USB 2.0 Ethernet",
.bind = ax88772_bind,
- .unbind = ax8817x_unbind,
+ .status = ax8817x_status,
+ .link_reset = ax88772_link_reset,
+ .reset = ax88772_link_reset,
.flags = FLAG_ETHER | FLAG_FRAMING_AX,
.rx_fixup = ax88772_rx_fixup,
.tx_fixup = ax88772_tx_fixup,
}
}
+#endif /* NEED_GENERIC_CDC */
+
+\f
+#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)
{
}
}
-#endif /* NEED_GENERIC_CDC */
-
-\f
-#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 u8 nibble (unsigned char c)
{
if (likely (isdigit (c)))
* All known Zaurii lie about their standards conformance. Most lie by
* saying they support CDC Ethernet. Some lie and say they support CDC
* MDLM (as if for access to cell phone modems). Someone, please beat
- * on Sharp for a while with a cluestick.
+ * on Sharp (and other such vendors) for a while with a cluestick.
*
*-------------------------------------------------------------------------*/
};
#define ZAURUS_PXA_INFO ((unsigned long)&zaurus_pxa_info)
-static const struct driver_info zaurus_pxa_mdlm_info = {
- .description = "Sharp Zaurus, PXA-255 based",
- .flags = FLAG_FRAMING_Z,
- .check_connect = always_connected,
- .tx_fixup = zaurus_tx_fixup,
-};
-
static const struct driver_info olympus_mxl_info = {
.description = "Olympus R1000",
.flags = FLAG_FRAMING_Z,
};
#define OLYMPUS_MXL_INFO ((unsigned long)&olympus_mxl_info)
+
+/* Some more recent products using Lineo/Belcarra code will wrongly claim
+ * CDC MDLM conformance. They aren't conformant: data endpoints live
+ * in the control interface, there's no data interface, and it's not used
+ * to talk to a cell phone radio. But at least we can detect these two
+ * pseudo-classes, rather than growing this product list with entries for
+ * each new nonconformant product (sigh).
+ */
+static const u8 safe_guid[16] = {
+ 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
+ 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
+};
+static const u8 blan_guid[16] = {
+ 0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70,
+ 0xa3, 0x67, 0x71, 0x34, 0xc9, 0xf5, 0x54, 0x37,
+};
+
+static int blan_mdlm_bind (struct usbnet *dev, struct usb_interface *intf)
+{
+ u8 *buf = intf->cur_altsetting->extra;
+ int len = intf->cur_altsetting->extralen;
+ struct usb_cdc_mdlm_desc *desc = NULL;
+ struct usb_cdc_mdlm_detail_desc *detail = NULL;
+
+ while (len > 3) {
+ if (buf [1] != USB_DT_CS_INTERFACE)
+ goto next_desc;
+
+ /* use bDescriptorSubType, and just verify that we get a
+ * "BLAN" (or "SAFE") descriptor.
+ */
+ switch (buf [2]) {
+ case USB_CDC_MDLM_TYPE:
+ if (desc) {
+ dev_dbg (&intf->dev, "extra MDLM\n");
+ goto bad_desc;
+ }
+ desc = (void *) buf;
+ if (desc->bLength != sizeof *desc) {
+ dev_dbg (&intf->dev, "MDLM len %u\n",
+ desc->bLength);
+ goto bad_desc;
+ }
+ /* expect bcdVersion 1.0, ignore */
+ if (memcmp(&desc->bGUID, blan_guid, 16)
+ && memcmp(&desc->bGUID, safe_guid, 16) ) {
+ /* hey, this one might _really_ be MDLM! */
+ dev_dbg (&intf->dev, "MDLM guid\n");
+ goto bad_desc;
+ }
+ break;
+ case USB_CDC_MDLM_DETAIL_TYPE:
+ if (detail) {
+ dev_dbg (&intf->dev, "extra MDLM detail\n");
+ goto bad_desc;
+ }
+ detail = (void *) buf;
+ switch (detail->bGuidDescriptorType) {
+ case 0: /* "SAFE" */
+ if (detail->bLength != (sizeof *detail + 2))
+ goto bad_detail;
+ break;
+ case 1: /* "BLAN" */
+ if (detail->bLength != (sizeof *detail + 3))
+ goto bad_detail;
+ break;
+ default:
+ goto bad_detail;
+ }
+
+ /* assuming we either noticed BLAN already, or will
+ * find it soon, there are some data bytes here:
+ * - bmNetworkCapabilities (unused)
+ * - bmDataCapabilities (bits, see below)
+ * - bPad (ignored, for PADAFTER -- BLAN-only)
+ * bits are:
+ * - 0x01 -- Zaurus framing (add CRC)
+ * - 0x02 -- PADBEFORE (CRC includes some padding)
+ * - 0x04 -- PADAFTER (some padding after CRC)
+ * - 0x08 -- "fermat" packet mangling (for hw bugs)
+ * the PADBEFORE appears not to matter; we interop
+ * with devices that use it and those that don't.
+ */
+ if ((detail->bDetailData[1] & ~02) != 0x01) {
+ /* bmDataCapabilites == 0 would be fine too,
+ * but framing is minidriver-coupled for now.
+ */
+bad_detail:
+ dev_dbg (&intf->dev,
+ "bad MDLM detail, %d %d %d\n",
+ detail->bLength,
+ detail->bDetailData[0],
+ detail->bDetailData[2]);
+ goto bad_desc;
+ }
+ break;
+ }
+next_desc:
+ len -= buf [0]; /* bLength */
+ buf += buf [0];
+ }
+
+ if (!desc || !detail) {
+ dev_dbg (&intf->dev, "missing cdc mdlm %s%sdescriptor\n",
+ desc ? "" : "func ",
+ detail ? "" : "detail ");
+ goto bad_desc;
+ }
+
+ /* There's probably a CDC Ethernet descriptor there, but we can't
+ * rely on the Ethernet address it provides since not all vendors
+ * bother to make it unique. Likewise there's no point in tracking
+ * of the CDC event notifications.
+ */
+ return get_endpoints (dev, intf);
+
+bad_desc:
+ dev_info (&dev->udev->dev, "unsupported MDLM descriptors\n");
+ return -ENODEV;
+}
+
+static const struct driver_info bogus_mdlm_info = {
+ .description = "pseudo-MDLM (BLAN) device",
+ .flags = FLAG_FRAMING_Z,
+ .check_connect = always_connected,
+ .tx_fixup = zaurus_tx_fixup,
+ .bind = blan_mdlm_bind,
+};
+
#else
/* blacklist all those devices */
}
}
+ if (test_bit (EVENT_LINK_RESET, &dev->flags)) {
+ struct driver_info *info = dev->driver_info;
+ int retval = 0;
+
+ clear_bit (EVENT_LINK_RESET, &dev->flags);
+ if(info->link_reset && (retval = info->link_reset(dev)) < 0) {
+ devinfo(dev, "link reset failed (%d) usbnet usb-%s-%s, %s",
+ retval,
+ dev->udev->bus->bus_name, dev->udev->devpath,
+ info->description);
+ }
+ }
+
if (dev->flags)
devdbg (dev, "kevent done, flags = 0x%lx",
dev->flags);
.idProduct = 0x9050, /* C-860 */
ZAURUS_MASTER_INTERFACE,
.driver_info = ZAURUS_PXA_INFO,
+},
+
#ifdef CONFIG_USB_ZAURUS
- /* at least some (reports vary) C-860 units have very different
- * lies about their standards support.
+ /* At least some (reports vary) PXA units have very different lies
+ * about their standards support: they claim to be cell phones with
+ * direct access to their radios. (They don't conform to CDC MDLM.)
*/
-}, {
- .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
- | USB_DEVICE_ID_MATCH_DEVICE,
- .idVendor = 0x04DD,
- /* reported with some C860 units */
- .idProduct = 0x9031, /* C-860 */
- .bInterfaceClass = USB_CLASS_COMM,
- .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM,
- .bInterfaceProtocol = USB_CDC_PROTO_NONE,
- .driver_info = (unsigned long) &zaurus_pxa_mdlm_info,
-#endif
+{
+ USB_INTERFACE_INFO (USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long) &bogus_mdlm_info,
},
+#endif
/* Olympus has some models with a Zaurus-compatible option.
* R-1000 uses a FreeScale i.MXL cpu (ARMv4T)