From 82bcc200c778abc6c6b9bc7716c33d4a58e8ffa6 Mon Sep 17 00:00:00 2001 From: Arne Fitzenreiter Date: Sat, 3 Jan 2015 15:47:38 +0100 Subject: [PATCH] backports: rebuild all usb net modules with backports. fix missing modules eg. with asix usb adapters. --- lfs/backports | 1 + ...ackports-3.18.1-1-add_usbnet_modules.patch | 29383 ++++++++++++++++ 2 files changed, 29384 insertions(+) create mode 100644 src/patches/backports-3.18.1-1-add_usbnet_modules.patch diff --git a/lfs/backports b/lfs/backports index c5aae66fa4..5acd54b325 100644 --- a/lfs/backports +++ b/lfs/backports @@ -77,6 +77,7 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects)) @rm -rf $(DIR_APP) && cd $(DIR_SRC) && tar Jxf $(DIR_DL)/$(DL_FILE) cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-3.18.1-1-ipfire-build.patch cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-3.18.1-1-grsecurity.patch + cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/backports-3.18.1-1-add_usbnet_modules.patch # DVB patches cd $(DIR_APP) && patch -Np2 < $(DIR_SRC)/src/patches/v4l-dvb_fix_tua6034_pll.patch diff --git a/src/patches/backports-3.18.1-1-add_usbnet_modules.patch b/src/patches/backports-3.18.1-1-add_usbnet_modules.patch new file mode 100644 index 0000000000..11b9638aec --- /dev/null +++ b/src/patches/backports-3.18.1-1-add_usbnet_modules.patch @@ -0,0 +1,29383 @@ +diff -Naur backports-3.18.1-1.org/drivers/net/usb/asix_common.c backports-3.18.1-1/drivers/net/usb/asix_common.c +--- backports-3.18.1-1.org/drivers/net/usb/asix_common.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/asix_common.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,582 @@ ++/* ++ * ASIX AX8817X based USB 2.0 Ethernet Devices ++ * Copyright (C) 2003-2006 David Hollis ++ * Copyright (C) 2005 Phil Chang ++ * Copyright (C) 2006 James Painter ++ * Copyright (c) 2002-2003 TiVo Inc. ++ * ++ * 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, see . ++ */ ++ ++#include "asix.h" ++ ++int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data) ++{ ++ int ret; ++ ret = usbnet_read_cmd(dev, cmd, ++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ value, index, data, size); ++ ++ if (ret != size && ret >= 0) ++ return -EINVAL; ++ return ret; ++} ++ ++int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data) ++{ ++ return usbnet_write_cmd(dev, cmd, ++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ value, index, data, size); ++} ++ ++void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data) ++{ ++ usbnet_write_cmd_async(dev, cmd, ++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ value, index, data, size); ++} ++ ++int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, ++ struct asix_rx_fixup_info *rx) ++{ ++ int offset = 0; ++ ++ while (offset + sizeof(u16) <= skb->len) { ++ u16 remaining = 0; ++ unsigned char *data; ++ ++ if (!rx->size) { ++ if ((skb->len - offset == sizeof(u16)) || ++ rx->split_head) { ++ if(!rx->split_head) { ++ rx->header = get_unaligned_le16( ++ skb->data + offset); ++ rx->split_head = true; ++ offset += sizeof(u16); ++ break; ++ } else { ++ rx->header |= (get_unaligned_le16( ++ skb->data + offset) ++ << 16); ++ rx->split_head = false; ++ offset += sizeof(u16); ++ } ++ } else { ++ rx->header = get_unaligned_le32(skb->data + ++ offset); ++ offset += sizeof(u32); ++ } ++ ++ /* get the packet length */ ++ rx->size = (u16) (rx->header & 0x7ff); ++ if (rx->size != ((~rx->header >> 16) & 0x7ff)) { ++ netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", ++ rx->header, offset); ++ rx->size = 0; ++ return 0; ++ } ++ rx->ax_skb = netdev_alloc_skb_ip_align(dev->net, ++ rx->size); ++ if (!rx->ax_skb) ++ return 0; ++ } ++ ++ if (rx->size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { ++ netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", ++ rx->size); ++ kfree_skb(rx->ax_skb); ++ rx->ax_skb = NULL; ++ rx->size = 0U; ++ ++ return 0; ++ } ++ ++ if (rx->size > skb->len - offset) { ++ remaining = rx->size - (skb->len - offset); ++ rx->size = skb->len - offset; ++ } ++ ++ data = skb_put(rx->ax_skb, rx->size); ++ memcpy(data, skb->data + offset, rx->size); ++ if (!remaining) ++ usbnet_skb_return(dev, rx->ax_skb); ++ ++ offset += (rx->size + 1) & 0xfffe; ++ rx->size = remaining; ++ } ++ ++ if (skb->len != offset) { ++ netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n", ++ skb->len, offset); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb) ++{ ++ struct asix_common_private *dp = dev->driver_priv; ++ struct asix_rx_fixup_info *rx = &dp->rx_fixup_info; ++ ++ return asix_rx_fixup_internal(dev, skb, rx); ++} ++ ++struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, ++ gfp_t flags) ++{ ++ int padlen; ++ int headroom = skb_headroom(skb); ++ int tailroom = skb_tailroom(skb); ++ u32 packet_len; ++ u32 padbytes = 0xffff0000; ++ ++ padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; ++ ++ /* We need to push 4 bytes in front of frame (packet_len) ++ * and maybe add 4 bytes after the end (if padlen is 4) ++ * ++ * Avoid skb_copy_expand() expensive call, using following rules : ++ * - We are allowed to push 4 bytes in headroom if skb_header_cloned() ++ * is false (and if we have 4 bytes of headroom) ++ * - We are allowed to put 4 bytes at tail if skb_cloned() ++ * is false (and if we have 4 bytes of tailroom) ++ * ++ * TCP packets for example are cloned, but skb_header_release() ++ * was called in tcp stack, allowing us to use headroom for our needs. ++ */ ++ if (!skb_header_cloned(skb) && ++ !(padlen && skb_cloned(skb)) && ++ headroom + tailroom >= 4 + padlen) { ++ /* following should not happen, but better be safe */ ++ if (headroom < 4 || ++ tailroom < padlen) { ++ skb->data = memmove(skb->head + 4, skb->data, skb->len); ++ skb_set_tail_pointer(skb, skb->len); ++ } ++ } else { ++ struct sk_buff *skb2; ++ ++ skb2 = skb_copy_expand(skb, 4, padlen, flags); ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ if (!skb) ++ return NULL; ++ } ++ ++ packet_len = ((skb->len ^ 0x0000ffff) << 16) + skb->len; ++ skb_push(skb, 4); ++ cpu_to_le32s(&packet_len); ++ skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); ++ ++ if (padlen) { ++ cpu_to_le32s(&padbytes); ++ memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); ++ skb_put(skb, sizeof(padbytes)); ++ } ++ return skb; ++} ++ ++int asix_set_sw_mii(struct usbnet *dev) ++{ ++ int ret; ++ ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, "Failed to enable software MII access\n"); ++ return ret; ++} ++ ++int asix_set_hw_mii(struct usbnet *dev) ++{ ++ int ret; ++ ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, "Failed to enable hardware MII access\n"); ++ return ret; ++} ++ ++int asix_read_phy_addr(struct usbnet *dev, int internal) ++{ ++ int offset = (internal ? 1 : 0); ++ u8 buf[2]; ++ int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf); ++ ++ netdev_dbg(dev->net, "asix_get_phy_addr()\n"); ++ ++ if (ret < 0) { ++ netdev_err(dev->net, "Error reading PHYID register: %02x\n", ret); ++ goto out; ++ } ++ netdev_dbg(dev->net, "asix_get_phy_addr() returning 0x%04x\n", ++ *((__le16 *)buf)); ++ ret = buf[offset]; ++ ++out: ++ return ret; ++} ++ ++int asix_get_phy_addr(struct usbnet *dev) ++{ ++ /* return the address of the internal phy */ ++ return asix_read_phy_addr(dev, 1); ++} ++ ++ ++int asix_sw_reset(struct usbnet *dev, u8 flags) ++{ ++ int ret; ++ ++ ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, "Failed to send software reset: %02x\n", ret); ++ ++ return ret; ++} ++ ++u16 asix_read_rx_ctl(struct usbnet *dev) ++{ ++ __le16 v; ++ int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v); ++ ++ if (ret < 0) { ++ netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret); ++ goto out; ++ } ++ ret = le16_to_cpu(v); ++out: ++ return ret; ++} ++ ++int asix_write_rx_ctl(struct usbnet *dev, u16 mode) ++{ ++ int ret; ++ ++ netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode); ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n", ++ mode, ret); ++ ++ return ret; ++} ++ ++u16 asix_read_medium_status(struct usbnet *dev) ++{ ++ __le16 v; ++ int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v); ++ ++ if (ret < 0) { ++ netdev_err(dev->net, "Error reading Medium Status register: %02x\n", ++ ret); ++ return ret; /* TODO: callers not checking for error ret */ ++ } ++ ++ return le16_to_cpu(v); ++ ++} ++ ++int asix_write_medium_mode(struct usbnet *dev, u16 mode) ++{ ++ int ret; ++ ++ netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode); ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n", ++ mode, ret); ++ ++ return ret; ++} ++ ++int asix_write_gpio(struct usbnet *dev, u16 value, int sleep) ++{ ++ int ret; ++ ++ netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value); ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n", ++ value, ret); ++ ++ if (sleep) ++ msleep(sleep); ++ ++ return ret; ++} ++ ++/* ++ * AX88772 & AX88178 have a 16-bit RX_CTL value ++ */ ++void asix_set_multicast(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct asix_data *data = (struct asix_data *)&dev->data; ++ u16 rx_ctl = AX_DEFAULT_RX_CTL; ++ ++ if (net->flags & IFF_PROMISC) { ++ rx_ctl |= AX_RX_CTL_PRO; ++ } else if (net->flags & IFF_ALLMULTI || ++ netdev_mc_count(net) > AX_MAX_MCAST) { ++ rx_ctl |= AX_RX_CTL_AMALL; ++ } else if (netdev_mc_empty(net)) { ++ /* just broadcast and directed */ ++ } else { ++ /* We use the 20 byte dev->data ++ * for our 8 byte filter buffer ++ * to avoid allocating memory that ++ * is tricky to free later */ ++ struct netdev_hw_addr *ha; ++ u32 crc_bits; ++ ++ memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); ++ ++ /* Build the multicast hash filter. */ ++ netdev_for_each_mc_addr(ha, net) { ++ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; ++ data->multi_filter[crc_bits >> 3] |= ++ 1 << (crc_bits & 7); ++ } ++ ++ asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0, ++ AX_MCAST_FILTER_SIZE, data->multi_filter); ++ ++ rx_ctl |= AX_RX_CTL_AM; ++ } ++ ++ asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); ++} ++ ++int asix_mdio_read(struct net_device *netdev, int phy_id, int loc) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ __le16 res; ++ ++ mutex_lock(&dev->phy_mutex); ++ asix_set_sw_mii(dev); ++ asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, ++ (__u16)loc, 2, &res); ++ asix_set_hw_mii(dev); ++ mutex_unlock(&dev->phy_mutex); ++ ++ netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", ++ phy_id, loc, le16_to_cpu(res)); ++ ++ return le16_to_cpu(res); ++} ++ ++void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ __le16 res = cpu_to_le16(val); ++ ++ netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", ++ phy_id, loc, val); ++ mutex_lock(&dev->phy_mutex); ++ asix_set_sw_mii(dev); ++ asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); ++ asix_set_hw_mii(dev); ++ mutex_unlock(&dev->phy_mutex); ++} ++ ++void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ u8 opt; ++ ++ if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) { ++ wolinfo->supported = 0; ++ wolinfo->wolopts = 0; ++ return; ++ } ++ wolinfo->supported = WAKE_PHY | WAKE_MAGIC; ++ wolinfo->wolopts = 0; ++ if (opt & AX_MONITOR_LINK) ++ wolinfo->wolopts |= WAKE_PHY; ++ if (opt & AX_MONITOR_MAGIC) ++ wolinfo->wolopts |= WAKE_MAGIC; ++} ++ ++int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ u8 opt = 0; ++ ++ if (wolinfo->wolopts & WAKE_PHY) ++ opt |= AX_MONITOR_LINK; ++ if (wolinfo->wolopts & WAKE_MAGIC) ++ opt |= AX_MONITOR_MAGIC; ++ ++ if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, ++ opt, 0, 0, NULL) < 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++int asix_get_eeprom_len(struct net_device *net) ++{ ++ return AX_EEPROM_LEN; ++} ++ ++int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, ++ u8 *data) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ u16 *eeprom_buff; ++ int first_word, last_word; ++ int i; ++ ++ if (eeprom->len == 0) ++ return -EINVAL; ++ ++ eeprom->magic = AX_EEPROM_MAGIC; ++ ++ first_word = eeprom->offset >> 1; ++ last_word = (eeprom->offset + eeprom->len - 1) >> 1; ++ ++ eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1), ++ GFP_KERNEL); ++ if (!eeprom_buff) ++ return -ENOMEM; ++ ++ /* ax8817x returns 2 bytes from eeprom on read */ ++ for (i = first_word; i <= last_word; i++) { ++ if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2, ++ &(eeprom_buff[i - first_word])) < 0) { ++ kfree(eeprom_buff); ++ return -EIO; ++ } ++ } ++ ++ memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len); ++ kfree(eeprom_buff); ++ return 0; ++} ++ ++int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, ++ u8 *data) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ u16 *eeprom_buff; ++ int first_word, last_word; ++ int i; ++ int ret; ++ ++ netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n", ++ eeprom->len, eeprom->offset, eeprom->magic); ++ ++ if (eeprom->len == 0) ++ return -EINVAL; ++ ++ if (eeprom->magic != AX_EEPROM_MAGIC) ++ return -EINVAL; ++ ++ first_word = eeprom->offset >> 1; ++ last_word = (eeprom->offset + eeprom->len - 1) >> 1; ++ ++ eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1), ++ GFP_KERNEL); ++ if (!eeprom_buff) ++ return -ENOMEM; ++ ++ /* align data to 16 bit boundaries, read the missing data from ++ the EEPROM */ ++ if (eeprom->offset & 1) { ++ ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2, ++ &(eeprom_buff[0])); ++ if (ret < 0) { ++ netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word); ++ goto free; ++ } ++ } ++ ++ if ((eeprom->offset + eeprom->len) & 1) { ++ ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2, ++ &(eeprom_buff[last_word - first_word])); ++ if (ret < 0) { ++ netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word); ++ goto free; ++ } ++ } ++ ++ memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len); ++ ++ /* write data to EEPROM */ ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL); ++ if (ret < 0) { ++ netdev_err(net, "Failed to enable EEPROM write\n"); ++ goto free; ++ } ++ msleep(20); ++ ++ for (i = first_word; i <= last_word; i++) { ++ netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n", ++ i, eeprom_buff[i - first_word]); ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i, ++ eeprom_buff[i - first_word], 0, NULL); ++ if (ret < 0) { ++ netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", ++ i); ++ goto free; ++ } ++ msleep(20); ++ } ++ ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL); ++ if (ret < 0) { ++ netdev_err(net, "Failed to disable EEPROM write\n"); ++ goto free; ++ } ++ ++ ret = 0; ++free: ++ kfree(eeprom_buff); ++ return ret; ++} ++ ++void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) ++{ ++ /* Inherit standard device info */ ++ usbnet_get_drvinfo(net, info); ++ strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver)); ++ strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); ++ info->eedump_len = AX_EEPROM_LEN; ++} ++ ++int asix_set_mac_address(struct net_device *net, void *p) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct asix_data *data = (struct asix_data *)&dev->data; ++ struct sockaddr *addr = p; ++ ++ if (netif_running(net)) ++ return -EBUSY; ++ if (!is_valid_ether_addr(addr->sa_data)) ++ return -EADDRNOTAVAIL; ++ ++ memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); ++ ++ /* We use the 20 byte dev->data ++ * for our 6 byte mac buffer ++ * to avoid allocating memory that ++ * is tricky to free later */ ++ memcpy(data->mac_addr, addr->sa_data, ETH_ALEN); ++ asix_write_cmd_async(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, ++ data->mac_addr); ++ ++ return 0; ++} +diff -Naur backports-3.18.1-1.org/drivers/net/usb/asix_devices.c backports-3.18.1-1/drivers/net/usb/asix_devices.c +--- backports-3.18.1-1.org/drivers/net/usb/asix_devices.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/asix_devices.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,1104 @@ ++/* ++ * ASIX AX8817X based USB 2.0 Ethernet Devices ++ * Copyright (C) 2003-2006 David Hollis ++ * Copyright (C) 2005 Phil Chang ++ * Copyright (C) 2006 James Painter ++ * Copyright (c) 2002-2003 TiVo Inc. ++ * ++ * 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, see . ++ */ ++ ++#include "asix.h" ++ ++#define PHY_MODE_MARVELL 0x0000 ++#define MII_MARVELL_LED_CTRL 0x0018 ++#define MII_MARVELL_STATUS 0x001b ++#define MII_MARVELL_CTRL 0x0014 ++ ++#define MARVELL_LED_MANUAL 0x0019 ++ ++#define MARVELL_STATUS_HWCFG 0x0004 ++ ++#define MARVELL_CTRL_TXDELAY 0x0002 ++#define MARVELL_CTRL_RXDELAY 0x0080 ++ ++#define PHY_MODE_RTL8211CL 0x000C ++ ++struct ax88172_int_data { ++ __le16 res1; ++ u8 link; ++ __le16 res2; ++ u8 status; ++ __le16 res3; ++} __packed; ++ ++static void asix_status(struct usbnet *dev, struct urb *urb) ++{ ++ struct ax88172_int_data *event; ++ int link; ++ ++ if (urb->actual_length < 8) ++ return; ++ ++ event = urb->transfer_buffer; ++ link = event->link & 0x01; ++ if (netif_carrier_ok(dev->net) != link) { ++ usbnet_link_change(dev, link, 1); ++ netdev_dbg(dev->net, "Link Status is: %d\n", link); ++ } ++} ++ ++static void asix_set_netdev_dev_addr(struct usbnet *dev, u8 *addr) ++{ ++ if (is_valid_ether_addr(addr)) { ++ memcpy(dev->net->dev_addr, addr, ETH_ALEN); ++ } else { ++ netdev_info(dev->net, "invalid hw address, using random\n"); ++ eth_hw_addr_random(dev->net); ++ } ++} ++ ++/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */ ++static u32 asix_get_phyid(struct usbnet *dev) ++{ ++ int phy_reg; ++ u32 phy_id; ++ int i; ++ ++ /* Poll for the rare case the FW or phy isn't ready yet. */ ++ for (i = 0; i < 100; i++) { ++ phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1); ++ if (phy_reg != 0 && phy_reg != 0xFFFF) ++ break; ++ mdelay(1); ++ } ++ ++ if (phy_reg <= 0 || phy_reg == 0xFFFF) ++ return 0; ++ ++ phy_id = (phy_reg & 0xffff) << 16; ++ ++ phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2); ++ if (phy_reg < 0) ++ return 0; ++ ++ phy_id |= (phy_reg & 0xffff); ++ ++ return phy_id; ++} ++ ++static u32 asix_get_link(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ ++ return mii_link_ok(&dev->mii); ++} ++ ++static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ ++ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); ++} ++ ++/* We need to override some ethtool_ops so we require our ++ own structure so we don't interfere with other usbnet ++ devices that may be connected at the same time. */ ++static const struct ethtool_ops ax88172_ethtool_ops = { ++ .get_drvinfo = asix_get_drvinfo, ++ .get_link = asix_get_link, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_wol = asix_get_wol, ++ .set_wol = asix_set_wol, ++ .get_eeprom_len = asix_get_eeprom_len, ++ .get_eeprom = asix_get_eeprom, ++ .set_eeprom = asix_set_eeprom, ++ .get_settings = usbnet_get_settings, ++ .set_settings = usbnet_set_settings, ++ .nway_reset = usbnet_nway_reset, ++}; ++ ++static void ax88172_set_multicast(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct asix_data *data = (struct asix_data *)&dev->data; ++ u8 rx_ctl = 0x8c; ++ ++ if (net->flags & IFF_PROMISC) { ++ rx_ctl |= 0x01; ++ } else if (net->flags & IFF_ALLMULTI || ++ netdev_mc_count(net) > AX_MAX_MCAST) { ++ rx_ctl |= 0x02; ++ } else if (netdev_mc_empty(net)) { ++ /* just broadcast and directed */ ++ } else { ++ /* We use the 20 byte dev->data ++ * for our 8 byte filter buffer ++ * to avoid allocating memory that ++ * is tricky to free later */ ++ struct netdev_hw_addr *ha; ++ u32 crc_bits; ++ ++ memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE); ++ ++ /* Build the multicast hash filter. */ ++ netdev_for_each_mc_addr(ha, net) { ++ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; ++ data->multi_filter[crc_bits >> 3] |= ++ 1 << (crc_bits & 7); ++ } ++ ++ asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0, ++ AX_MCAST_FILTER_SIZE, data->multi_filter); ++ ++ rx_ctl |= 0x10; ++ } ++ ++ asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); ++} ++ ++static int ax88172_link_reset(struct usbnet *dev) ++{ ++ u8 mode; ++ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; ++ ++ mii_check_media(&dev->mii, 1, 1); ++ mii_ethtool_gset(&dev->mii, &ecmd); ++ mode = AX88172_MEDIUM_DEFAULT; ++ ++ if (ecmd.duplex != DUPLEX_FULL) ++ mode |= ~AX88172_MEDIUM_FD; ++ ++ netdev_dbg(dev->net, "ax88172_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", ++ ethtool_cmd_speed(&ecmd), ecmd.duplex, mode); ++ ++ asix_write_medium_mode(dev, mode); ++ ++ return 0; ++} ++ ++static const struct net_device_ops ax88172_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = usbnet_change_mtu, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_do_ioctl = asix_ioctl, ++ .ndo_set_rx_mode = ax88172_set_multicast, ++}; ++ ++static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int ret = 0; ++ u8 buf[ETH_ALEN]; ++ int i; ++ unsigned long gpio_bits = dev->driver_info->data; ++ ++ usbnet_get_endpoints(dev,intf); ++ ++ /* Toggle the GPIOs in a manufacturer/model specific way */ ++ for (i = 2; i >= 0; i--) { ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, ++ (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL); ++ if (ret < 0) ++ goto out; ++ msleep(5); ++ } ++ ++ ret = asix_write_rx_ctl(dev, 0x80); ++ if (ret < 0) ++ goto out; ++ ++ /* Get the MAC address */ ++ ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf); ++ if (ret < 0) { ++ netdev_dbg(dev->net, "read AX_CMD_READ_NODE_ID failed: %d\n", ++ ret); ++ goto out; ++ } ++ ++ asix_set_netdev_dev_addr(dev, buf); ++ ++ /* Initialize MII structure */ ++ dev->mii.dev = dev->net; ++ dev->mii.mdio_read = asix_mdio_read; ++ dev->mii.mdio_write = asix_mdio_write; ++ dev->mii.phy_id_mask = 0x3f; ++ dev->mii.reg_num_mask = 0x1f; ++ dev->mii.phy_id = asix_get_phy_addr(dev); ++ ++ dev->net->netdev_ops = &ax88172_netdev_ops; ++ dev->net->ethtool_ops = &ax88172_ethtool_ops; ++ dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ ++ dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ ++ ++ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); ++ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ++ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); ++ mii_nway_restart(&dev->mii); ++ ++ return 0; ++ ++out: ++ return ret; ++} ++ ++static const struct ethtool_ops ax88772_ethtool_ops = { ++ .get_drvinfo = asix_get_drvinfo, ++ .get_link = asix_get_link, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_wol = asix_get_wol, ++ .set_wol = asix_set_wol, ++ .get_eeprom_len = asix_get_eeprom_len, ++ .get_eeprom = asix_get_eeprom, ++ .set_eeprom = asix_set_eeprom, ++ .get_settings = usbnet_get_settings, ++ .set_settings = usbnet_set_settings, ++ .nway_reset = usbnet_nway_reset, ++}; ++ ++static int ax88772_link_reset(struct usbnet *dev) ++{ ++ u16 mode; ++ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; ++ ++ mii_check_media(&dev->mii, 1, 1); ++ mii_ethtool_gset(&dev->mii, &ecmd); ++ mode = AX88772_MEDIUM_DEFAULT; ++ ++ if (ethtool_cmd_speed(&ecmd) != SPEED_100) ++ mode &= ~AX_MEDIUM_PS; ++ ++ if (ecmd.duplex != DUPLEX_FULL) ++ mode &= ~AX_MEDIUM_FD; ++ ++ netdev_dbg(dev->net, "ax88772_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", ++ ethtool_cmd_speed(&ecmd), ecmd.duplex, mode); ++ ++ asix_write_medium_mode(dev, mode); ++ ++ return 0; ++} ++ ++static int ax88772_reset(struct usbnet *dev) ++{ ++ struct asix_data *data = (struct asix_data *)&dev->data; ++ int ret, embd_phy; ++ u16 rx_ctl; ++ ++ ret = asix_write_gpio(dev, ++ AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5); ++ if (ret < 0) ++ goto out; ++ ++ embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); ++ ++ ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); ++ if (ret < 0) { ++ netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); ++ goto out; ++ } ++ ++ ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL); ++ if (ret < 0) ++ goto out; ++ ++ msleep(150); ++ ++ ret = asix_sw_reset(dev, AX_SWRESET_CLEAR); ++ if (ret < 0) ++ goto out; ++ ++ msleep(150); ++ ++ if (embd_phy) { ++ ret = asix_sw_reset(dev, AX_SWRESET_IPRL); ++ if (ret < 0) ++ goto out; ++ } else { ++ ret = asix_sw_reset(dev, AX_SWRESET_PRTE); ++ if (ret < 0) ++ goto out; ++ } ++ ++ msleep(150); ++ rx_ctl = asix_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); ++ ret = asix_write_rx_ctl(dev, 0x0000); ++ if (ret < 0) ++ goto out; ++ ++ rx_ctl = asix_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); ++ ++ ret = asix_sw_reset(dev, AX_SWRESET_PRL); ++ if (ret < 0) ++ goto out; ++ ++ msleep(150); ++ ++ ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL); ++ if (ret < 0) ++ goto out; ++ ++ msleep(150); ++ ++ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); ++ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ++ ADVERTISE_ALL | ADVERTISE_CSMA); ++ mii_nway_restart(&dev->mii); ++ ++ ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT); ++ if (ret < 0) ++ goto out; ++ ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0, ++ AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, ++ AX88772_IPG2_DEFAULT, 0, NULL); ++ if (ret < 0) { ++ netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret); ++ goto out; ++ } ++ ++ /* Rewrite MAC address */ ++ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, ++ data->mac_addr); ++ if (ret < 0) ++ goto out; ++ ++ /* Set RX_CTL to default values with 2k buffer, and enable cactus */ ++ ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL); ++ if (ret < 0) ++ goto out; ++ ++ rx_ctl = asix_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n", ++ rx_ctl); ++ ++ rx_ctl = asix_read_medium_status(dev); ++ netdev_dbg(dev->net, ++ "Medium Status is 0x%04x after all initializations\n", ++ rx_ctl); ++ ++ return 0; ++ ++out: ++ return ret; ++ ++} ++ ++static const struct net_device_ops ax88772_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = usbnet_change_mtu, ++ .ndo_set_mac_address = asix_set_mac_address, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_do_ioctl = asix_ioctl, ++ .ndo_set_rx_mode = asix_set_multicast, ++}; ++ ++static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int ret, embd_phy, i; ++ u8 buf[ETH_ALEN]; ++ u32 phyid; ++ ++ usbnet_get_endpoints(dev,intf); ++ ++ /* Get the MAC address */ ++ if (dev->driver_info->data & FLAG_EEPROM_MAC) { ++ for (i = 0; i < (ETH_ALEN >> 1); i++) { ++ ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x04 + i, ++ 0, 2, buf + i * 2); ++ if (ret < 0) ++ break; ++ } ++ } else { ++ ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, ++ 0, 0, ETH_ALEN, buf); ++ } ++ ++ if (ret < 0) { ++ netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret); ++ return ret; ++ } ++ ++ asix_set_netdev_dev_addr(dev, buf); ++ ++ /* Initialize MII structure */ ++ dev->mii.dev = dev->net; ++ dev->mii.mdio_read = asix_mdio_read; ++ dev->mii.mdio_write = asix_mdio_write; ++ dev->mii.phy_id_mask = 0x1f; ++ dev->mii.reg_num_mask = 0x1f; ++ dev->mii.phy_id = asix_get_phy_addr(dev); ++ ++ dev->net->netdev_ops = &ax88772_netdev_ops; ++ dev->net->ethtool_ops = &ax88772_ethtool_ops; ++ dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ ++ dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ ++ ++ embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); ++ ++ /* Reset the PHY to normal operation mode */ ++ ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); ++ if (ret < 0) { ++ netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); ++ return ret; ++ } ++ ++ ax88772_reset(dev); ++ ++ /* Read PHYID register *AFTER* the PHY was reset properly */ ++ phyid = asix_get_phyid(dev); ++ netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid); ++ ++ /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */ ++ if (dev->driver_info->flags & FLAG_FRAMING_AX) { ++ /* hard_mtu is still the default - the device does not support ++ jumbo eth frames */ ++ dev->rx_urb_size = 2048; ++ } ++ ++ dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL); ++ if (!dev->driver_priv) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ if (dev->driver_priv) ++ kfree(dev->driver_priv); ++} ++ ++static const struct ethtool_ops ax88178_ethtool_ops = { ++ .get_drvinfo = asix_get_drvinfo, ++ .get_link = asix_get_link, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_wol = asix_get_wol, ++ .set_wol = asix_set_wol, ++ .get_eeprom_len = asix_get_eeprom_len, ++ .get_eeprom = asix_get_eeprom, ++ .set_eeprom = asix_set_eeprom, ++ .get_settings = usbnet_get_settings, ++ .set_settings = usbnet_set_settings, ++ .nway_reset = usbnet_nway_reset, ++}; ++ ++static int marvell_phy_init(struct usbnet *dev) ++{ ++ struct asix_data *data = (struct asix_data *)&dev->data; ++ u16 reg; ++ ++ netdev_dbg(dev->net, "marvell_phy_init()\n"); ++ ++ reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_MARVELL_STATUS); ++ netdev_dbg(dev->net, "MII_MARVELL_STATUS = 0x%04x\n", reg); ++ ++ asix_mdio_write(dev->net, dev->mii.phy_id, MII_MARVELL_CTRL, ++ MARVELL_CTRL_RXDELAY | MARVELL_CTRL_TXDELAY); ++ ++ if (data->ledmode) { ++ reg = asix_mdio_read(dev->net, dev->mii.phy_id, ++ MII_MARVELL_LED_CTRL); ++ netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (1) = 0x%04x\n", reg); ++ ++ reg &= 0xf8ff; ++ reg |= (1 + 0x0100); ++ asix_mdio_write(dev->net, dev->mii.phy_id, ++ MII_MARVELL_LED_CTRL, reg); ++ ++ reg = asix_mdio_read(dev->net, dev->mii.phy_id, ++ MII_MARVELL_LED_CTRL); ++ netdev_dbg(dev->net, "MII_MARVELL_LED_CTRL (2) = 0x%04x\n", reg); ++ reg &= 0xfc0f; ++ } ++ ++ return 0; ++} ++ ++static int rtl8211cl_phy_init(struct usbnet *dev) ++{ ++ struct asix_data *data = (struct asix_data *)&dev->data; ++ ++ netdev_dbg(dev->net, "rtl8211cl_phy_init()\n"); ++ ++ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0005); ++ asix_mdio_write (dev->net, dev->mii.phy_id, 0x0c, 0); ++ asix_mdio_write (dev->net, dev->mii.phy_id, 0x01, ++ asix_mdio_read (dev->net, dev->mii.phy_id, 0x01) | 0x0080); ++ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0); ++ ++ if (data->ledmode == 12) { ++ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0x0002); ++ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1a, 0x00cb); ++ asix_mdio_write (dev->net, dev->mii.phy_id, 0x1f, 0); ++ } ++ ++ return 0; ++} ++ ++static int marvell_led_status(struct usbnet *dev, u16 speed) ++{ ++ u16 reg = asix_mdio_read(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL); ++ ++ netdev_dbg(dev->net, "marvell_led_status() read 0x%04x\n", reg); ++ ++ /* Clear out the center LED bits - 0x03F0 */ ++ reg &= 0xfc0f; ++ ++ switch (speed) { ++ case SPEED_1000: ++ reg |= 0x03e0; ++ break; ++ case SPEED_100: ++ reg |= 0x03b0; ++ break; ++ default: ++ reg |= 0x02f0; ++ } ++ ++ netdev_dbg(dev->net, "marvell_led_status() writing 0x%04x\n", reg); ++ asix_mdio_write(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL, reg); ++ ++ return 0; ++} ++ ++static int ax88178_reset(struct usbnet *dev) ++{ ++ struct asix_data *data = (struct asix_data *)&dev->data; ++ int ret; ++ __le16 eeprom; ++ u8 status; ++ int gpio0 = 0; ++ u32 phyid; ++ ++ asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status); ++ netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status); ++ ++ asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL); ++ asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom); ++ asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL); ++ ++ netdev_dbg(dev->net, "EEPROM index 0x17 is 0x%04x\n", eeprom); ++ ++ if (eeprom == cpu_to_le16(0xffff)) { ++ data->phymode = PHY_MODE_MARVELL; ++ data->ledmode = 0; ++ gpio0 = 1; ++ } else { ++ data->phymode = le16_to_cpu(eeprom) & 0x7F; ++ data->ledmode = le16_to_cpu(eeprom) >> 8; ++ gpio0 = (le16_to_cpu(eeprom) & 0x80) ? 0 : 1; ++ } ++ netdev_dbg(dev->net, "GPIO0: %d, PhyMode: %d\n", gpio0, data->phymode); ++ ++ /* Power up external GigaPHY through AX88178 GPIO pin */ ++ asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40); ++ if ((le16_to_cpu(eeprom) >> 8) != 1) { ++ asix_write_gpio(dev, 0x003c, 30); ++ asix_write_gpio(dev, 0x001c, 300); ++ asix_write_gpio(dev, 0x003c, 30); ++ } else { ++ netdev_dbg(dev->net, "gpio phymode == 1 path\n"); ++ asix_write_gpio(dev, AX_GPIO_GPO1EN, 30); ++ asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30); ++ } ++ ++ /* Read PHYID register *AFTER* powering up PHY */ ++ phyid = asix_get_phyid(dev); ++ netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid); ++ ++ /* Set AX88178 to enable MII/GMII/RGMII interface for external PHY */ ++ asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL); ++ ++ asix_sw_reset(dev, 0); ++ msleep(150); ++ ++ asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD); ++ msleep(150); ++ ++ asix_write_rx_ctl(dev, 0); ++ ++ if (data->phymode == PHY_MODE_MARVELL) { ++ marvell_phy_init(dev); ++ msleep(60); ++ } else if (data->phymode == PHY_MODE_RTL8211CL) ++ rtl8211cl_phy_init(dev); ++ ++ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, ++ BMCR_RESET | BMCR_ANENABLE); ++ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ++ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); ++ asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000, ++ ADVERTISE_1000FULL); ++ ++ mii_nway_restart(&dev->mii); ++ ++ ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT); ++ if (ret < 0) ++ return ret; ++ ++ /* Rewrite MAC address */ ++ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, ++ data->mac_addr); ++ if (ret < 0) ++ return ret; ++ ++ ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int ax88178_link_reset(struct usbnet *dev) ++{ ++ u16 mode; ++ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; ++ struct asix_data *data = (struct asix_data *)&dev->data; ++ u32 speed; ++ ++ netdev_dbg(dev->net, "ax88178_link_reset()\n"); ++ ++ mii_check_media(&dev->mii, 1, 1); ++ mii_ethtool_gset(&dev->mii, &ecmd); ++ mode = AX88178_MEDIUM_DEFAULT; ++ speed = ethtool_cmd_speed(&ecmd); ++ ++ if (speed == SPEED_1000) ++ mode |= AX_MEDIUM_GM; ++ else if (speed == SPEED_100) ++ mode |= AX_MEDIUM_PS; ++ else ++ mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM); ++ ++ mode |= AX_MEDIUM_ENCK; ++ ++ if (ecmd.duplex == DUPLEX_FULL) ++ mode |= AX_MEDIUM_FD; ++ else ++ mode &= ~AX_MEDIUM_FD; ++ ++ netdev_dbg(dev->net, "ax88178_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n", ++ speed, ecmd.duplex, mode); ++ ++ asix_write_medium_mode(dev, mode); ++ ++ if (data->phymode == PHY_MODE_MARVELL && data->ledmode) ++ marvell_led_status(dev, speed); ++ ++ return 0; ++} ++ ++static void ax88178_set_mfb(struct usbnet *dev) ++{ ++ u16 mfb = AX_RX_CTL_MFB_16384; ++ u16 rxctl; ++ u16 medium; ++ int old_rx_urb_size = dev->rx_urb_size; ++ ++ if (dev->hard_mtu < 2048) { ++ dev->rx_urb_size = 2048; ++ mfb = AX_RX_CTL_MFB_2048; ++ } else if (dev->hard_mtu < 4096) { ++ dev->rx_urb_size = 4096; ++ mfb = AX_RX_CTL_MFB_4096; ++ } else if (dev->hard_mtu < 8192) { ++ dev->rx_urb_size = 8192; ++ mfb = AX_RX_CTL_MFB_8192; ++ } else if (dev->hard_mtu < 16384) { ++ dev->rx_urb_size = 16384; ++ mfb = AX_RX_CTL_MFB_16384; ++ } ++ ++ rxctl = asix_read_rx_ctl(dev); ++ asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb); ++ ++ medium = asix_read_medium_status(dev); ++ if (dev->net->mtu > 1500) ++ medium |= AX_MEDIUM_JFE; ++ else ++ medium &= ~AX_MEDIUM_JFE; ++ asix_write_medium_mode(dev, medium); ++ ++ if (dev->rx_urb_size > old_rx_urb_size) ++ usbnet_unlink_rx_urbs(dev); ++} ++ ++static int ax88178_change_mtu(struct net_device *net, int new_mtu) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ int ll_mtu = new_mtu + net->hard_header_len + 4; ++ ++ netdev_dbg(dev->net, "ax88178_change_mtu() new_mtu=%d\n", new_mtu); ++ ++ if (new_mtu <= 0 || ll_mtu > 16384) ++ return -EINVAL; ++ ++ if ((ll_mtu % dev->maxpacket) == 0) ++ return -EDOM; ++ ++ net->mtu = new_mtu; ++ dev->hard_mtu = net->mtu + net->hard_header_len; ++ ax88178_set_mfb(dev); ++ ++ /* max qlen depend on hard_mtu and rx_urb_size */ ++ usbnet_update_max_qlen(dev); ++ ++ return 0; ++} ++ ++static const struct net_device_ops ax88178_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_set_mac_address = asix_set_mac_address, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_set_rx_mode = asix_set_multicast, ++ .ndo_do_ioctl = asix_ioctl, ++ .ndo_change_mtu = ax88178_change_mtu, ++}; ++ ++static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int ret; ++ u8 buf[ETH_ALEN]; ++ ++ usbnet_get_endpoints(dev,intf); ++ ++ /* Get the MAC address */ ++ ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf); ++ if (ret < 0) { ++ netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret); ++ return ret; ++ } ++ ++ asix_set_netdev_dev_addr(dev, buf); ++ ++ /* Initialize MII structure */ ++ dev->mii.dev = dev->net; ++ dev->mii.mdio_read = asix_mdio_read; ++ dev->mii.mdio_write = asix_mdio_write; ++ dev->mii.phy_id_mask = 0x1f; ++ dev->mii.reg_num_mask = 0xff; ++ dev->mii.supports_gmii = 1; ++ dev->mii.phy_id = asix_get_phy_addr(dev); ++ ++ dev->net->netdev_ops = &ax88178_netdev_ops; ++ dev->net->ethtool_ops = &ax88178_ethtool_ops; ++ ++ /* Blink LEDS so users know driver saw dongle */ ++ asix_sw_reset(dev, 0); ++ msleep(150); ++ ++ asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD); ++ msleep(150); ++ ++ /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */ ++ if (dev->driver_info->flags & FLAG_FRAMING_AX) { ++ /* hard_mtu is still the default - the device does not support ++ jumbo eth frames */ ++ dev->rx_urb_size = 2048; ++ } ++ ++ dev->driver_priv = kzalloc(sizeof(struct asix_common_private), GFP_KERNEL); ++ if (!dev->driver_priv) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static const struct driver_info ax8817x_info = { ++ .description = "ASIX AX8817x USB 2.0 Ethernet", ++ .bind = ax88172_bind, ++ .status = asix_status, ++ .link_reset = ax88172_link_reset, ++ .reset = ax88172_link_reset, ++ .flags = FLAG_ETHER | FLAG_LINK_INTR, ++ .data = 0x00130103, ++}; ++ ++static const struct driver_info dlink_dub_e100_info = { ++ .description = "DLink DUB-E100 USB Ethernet", ++ .bind = ax88172_bind, ++ .status = asix_status, ++ .link_reset = ax88172_link_reset, ++ .reset = ax88172_link_reset, ++ .flags = FLAG_ETHER | FLAG_LINK_INTR, ++ .data = 0x009f9d9f, ++}; ++ ++static const struct driver_info netgear_fa120_info = { ++ .description = "Netgear FA-120 USB Ethernet", ++ .bind = ax88172_bind, ++ .status = asix_status, ++ .link_reset = ax88172_link_reset, ++ .reset = ax88172_link_reset, ++ .flags = FLAG_ETHER | FLAG_LINK_INTR, ++ .data = 0x00130103, ++}; ++ ++static const struct driver_info hawking_uf200_info = { ++ .description = "Hawking UF200 USB Ethernet", ++ .bind = ax88172_bind, ++ .status = asix_status, ++ .link_reset = ax88172_link_reset, ++ .reset = ax88172_link_reset, ++ .flags = FLAG_ETHER | FLAG_LINK_INTR, ++ .data = 0x001f1d1f, ++}; ++ ++static const struct driver_info ax88772_info = { ++ .description = "ASIX AX88772 USB 2.0 Ethernet", ++ .bind = ax88772_bind, ++ .unbind = ax88772_unbind, ++ .status = asix_status, ++ .link_reset = ax88772_link_reset, ++ .reset = ax88772_link_reset, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET, ++ .rx_fixup = asix_rx_fixup_common, ++ .tx_fixup = asix_tx_fixup, ++}; ++ ++static const struct driver_info ax88772b_info = { ++ .description = "ASIX AX88772B USB 2.0 Ethernet", ++ .bind = ax88772_bind, ++ .unbind = ax88772_unbind, ++ .status = asix_status, ++ .link_reset = ax88772_link_reset, ++ .reset = ax88772_reset, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | ++ FLAG_MULTI_PACKET, ++ .rx_fixup = asix_rx_fixup_common, ++ .tx_fixup = asix_tx_fixup, ++ .data = FLAG_EEPROM_MAC, ++}; ++ ++static const struct driver_info ax88178_info = { ++ .description = "ASIX AX88178 USB 2.0 Ethernet", ++ .bind = ax88178_bind, ++ .unbind = ax88772_unbind, ++ .status = asix_status, ++ .link_reset = ax88178_link_reset, ++ .reset = ax88178_reset, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | ++ FLAG_MULTI_PACKET, ++ .rx_fixup = asix_rx_fixup_common, ++ .tx_fixup = asix_tx_fixup, ++}; ++ ++/* ++ * USBLINK 20F9 "USB 2.0 LAN" USB ethernet adapter, typically found in ++ * no-name packaging. ++ * USB device strings are: ++ * 1: Manufacturer: USBLINK ++ * 2: Product: HG20F9 USB2.0 ++ * 3: Serial: 000003 ++ * Appears to be compatible with Asix 88772B. ++ */ ++static const struct driver_info hg20f9_info = { ++ .description = "HG20F9 USB 2.0 Ethernet", ++ .bind = ax88772_bind, ++ .unbind = ax88772_unbind, ++ .status = asix_status, ++ .link_reset = ax88772_link_reset, ++ .reset = ax88772_reset, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | ++ FLAG_MULTI_PACKET, ++ .rx_fixup = asix_rx_fixup_common, ++ .tx_fixup = asix_tx_fixup, ++ .data = FLAG_EEPROM_MAC, ++}; ++ ++static const struct usb_device_id products [] = { ++{ ++ // Linksys USB200M ++ USB_DEVICE (0x077b, 0x2226), ++ .driver_info = (unsigned long) &ax8817x_info, ++}, { ++ // Netgear FA120 ++ USB_DEVICE (0x0846, 0x1040), ++ .driver_info = (unsigned long) &netgear_fa120_info, ++}, { ++ // DLink DUB-E100 ++ USB_DEVICE (0x2001, 0x1a00), ++ .driver_info = (unsigned long) &dlink_dub_e100_info, ++}, { ++ // Intellinet, ST Lab USB Ethernet ++ USB_DEVICE (0x0b95, 0x1720), ++ .driver_info = (unsigned long) &ax8817x_info, ++}, { ++ // Hawking UF200, TrendNet TU2-ET100 ++ USB_DEVICE (0x07b8, 0x420a), ++ .driver_info = (unsigned long) &hawking_uf200_info, ++}, { ++ // Billionton Systems, USB2AR ++ USB_DEVICE (0x08dd, 0x90ff), ++ .driver_info = (unsigned long) &ax8817x_info, ++}, { ++ // ATEN UC210T ++ USB_DEVICE (0x0557, 0x2009), ++ .driver_info = (unsigned long) &ax8817x_info, ++}, { ++ // Buffalo LUA-U2-KTX ++ USB_DEVICE (0x0411, 0x003d), ++ .driver_info = (unsigned long) &ax8817x_info, ++}, { ++ // Buffalo LUA-U2-GT 10/100/1000 ++ USB_DEVICE (0x0411, 0x006e), ++ .driver_info = (unsigned long) &ax88178_info, ++}, { ++ // Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter" ++ USB_DEVICE (0x6189, 0x182d), ++ .driver_info = (unsigned long) &ax8817x_info, ++}, { ++ // Sitecom LN-031 "USB 2.0 10/100/1000 Ethernet adapter" ++ USB_DEVICE (0x0df6, 0x0056), ++ .driver_info = (unsigned long) &ax88178_info, ++}, { ++ // corega FEther USB2-TX ++ USB_DEVICE (0x07aa, 0x0017), ++ .driver_info = (unsigned long) &ax8817x_info, ++}, { ++ // Surecom EP-1427X-2 ++ USB_DEVICE (0x1189, 0x0893), ++ .driver_info = (unsigned long) &ax8817x_info, ++}, { ++ // goodway corp usb gwusb2e ++ USB_DEVICE (0x1631, 0x6200), ++ .driver_info = (unsigned long) &ax8817x_info, ++}, { ++ // JVC MP-PRX1 Port Replicator ++ USB_DEVICE (0x04f1, 0x3008), ++ .driver_info = (unsigned long) &ax8817x_info, ++}, { ++ // Lenovo U2L100P 10/100 ++ USB_DEVICE (0x17ef, 0x7203), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ // ASIX AX88772B 10/100 ++ USB_DEVICE (0x0b95, 0x772b), ++ .driver_info = (unsigned long) &ax88772b_info, ++}, { ++ // ASIX AX88772 10/100 ++ USB_DEVICE (0x0b95, 0x7720), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ // ASIX AX88178 10/100/1000 ++ USB_DEVICE (0x0b95, 0x1780), ++ .driver_info = (unsigned long) &ax88178_info, ++}, { ++ // Logitec LAN-GTJ/U2A ++ USB_DEVICE (0x0789, 0x0160), ++ .driver_info = (unsigned long) &ax88178_info, ++}, { ++ // Linksys USB200M Rev 2 ++ USB_DEVICE (0x13b1, 0x0018), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ // 0Q0 cable ethernet ++ USB_DEVICE (0x1557, 0x7720), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ // DLink DUB-E100 H/W Ver B1 ++ USB_DEVICE (0x07d1, 0x3c05), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ // DLink DUB-E100 H/W Ver B1 Alternate ++ USB_DEVICE (0x2001, 0x3c05), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ // DLink DUB-E100 H/W Ver C1 ++ USB_DEVICE (0x2001, 0x1a02), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ // Linksys USB1000 ++ USB_DEVICE (0x1737, 0x0039), ++ .driver_info = (unsigned long) &ax88178_info, ++}, { ++ // IO-DATA ETG-US2 ++ USB_DEVICE (0x04bb, 0x0930), ++ .driver_info = (unsigned long) &ax88178_info, ++}, { ++ // Belkin F5D5055 ++ USB_DEVICE(0x050d, 0x5055), ++ .driver_info = (unsigned long) &ax88178_info, ++}, { ++ // Apple USB Ethernet Adapter ++ USB_DEVICE(0x05ac, 0x1402), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ // Cables-to-Go USB Ethernet Adapter ++ USB_DEVICE(0x0b95, 0x772a), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ // ABOCOM for pci ++ USB_DEVICE(0x14ea, 0xab11), ++ .driver_info = (unsigned long) &ax88178_info, ++}, { ++ // ASIX 88772a ++ USB_DEVICE(0x0db0, 0xa877), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ // Asus USB Ethernet Adapter ++ USB_DEVICE (0x0b95, 0x7e2b), ++ .driver_info = (unsigned long) &ax88772_info, ++}, { ++ /* ASIX 88172a demo board */ ++ USB_DEVICE(0x0b95, 0x172a), ++ .driver_info = (unsigned long) &ax88172a_info, ++}, { ++ /* ++ * USBLINK HG20F9 "USB 2.0 LAN" ++ * Appears to have gazumped Linksys's manufacturer ID but ++ * doesn't (yet) conflict with any known Linksys product. ++ */ ++ USB_DEVICE(0x066b, 0x20f9), ++ .driver_info = (unsigned long) &hg20f9_info, ++}, ++ { }, // END ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver asix_driver = { ++ .name = DRIVER_NAME, ++ .id_table = products, ++ .probe = usbnet_probe, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disconnect = usbnet_disconnect, ++ .supports_autosuspend = 1, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(asix_driver); ++ ++MODULE_AUTHOR("David Hollis"); ++MODULE_VERSION(DRIVER_VERSION); ++MODULE_DESCRIPTION("ASIX AX8817X based USB 2.0 Ethernet Devices"); ++MODULE_LICENSE("GPL"); ++ +diff -Naur backports-3.18.1-1.org/drivers/net/usb/asix.h backports-3.18.1-1/drivers/net/usb/asix.h +--- backports-3.18.1-1.org/drivers/net/usb/asix.h 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/asix.h 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,234 @@ ++/* ++ * ASIX AX8817X based USB 2.0 Ethernet Devices ++ * Copyright (C) 2003-2006 David Hollis ++ * Copyright (C) 2005 Phil Chang ++ * Copyright (C) 2006 James Painter ++ * Copyright (c) 2002-2003 TiVo Inc. ++ * ++ * 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, see . ++ */ ++ ++#ifndef _ASIX_H ++#define _ASIX_H ++ ++// #define DEBUG // error path messages, extra info ++// #define VERBOSE // more; success messages ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_VERSION "22-Dec-2011" ++#define DRIVER_NAME "asix" ++ ++/* ASIX AX8817X based USB 2.0 Ethernet Devices */ ++ ++#define AX_CMD_SET_SW_MII 0x06 ++#define AX_CMD_READ_MII_REG 0x07 ++#define AX_CMD_WRITE_MII_REG 0x08 ++#define AX_CMD_SET_HW_MII 0x0a ++#define AX_CMD_READ_EEPROM 0x0b ++#define AX_CMD_WRITE_EEPROM 0x0c ++#define AX_CMD_WRITE_ENABLE 0x0d ++#define AX_CMD_WRITE_DISABLE 0x0e ++#define AX_CMD_READ_RX_CTL 0x0f ++#define AX_CMD_WRITE_RX_CTL 0x10 ++#define AX_CMD_READ_IPG012 0x11 ++#define AX_CMD_WRITE_IPG0 0x12 ++#define AX_CMD_WRITE_IPG1 0x13 ++#define AX_CMD_READ_NODE_ID 0x13 ++#define AX_CMD_WRITE_NODE_ID 0x14 ++#define AX_CMD_WRITE_IPG2 0x14 ++#define AX_CMD_WRITE_MULTI_FILTER 0x16 ++#define AX88172_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_CMD_READ_GPIOS 0x1e ++#define AX_CMD_WRITE_GPIOS 0x1f ++#define AX_CMD_SW_RESET 0x20 ++#define AX_CMD_SW_PHY_STATUS 0x21 ++#define AX_CMD_SW_PHY_SELECT 0x22 ++ ++#define AX_PHY_SELECT_MASK (BIT(3) | BIT(2)) ++#define AX_PHY_SELECT_INTERNAL 0 ++#define AX_PHY_SELECT_EXTERNAL BIT(2) ++ ++#define AX_MONITOR_MODE 0x01 ++#define AX_MONITOR_LINK 0x02 ++#define AX_MONITOR_MAGIC 0x04 ++#define AX_MONITOR_HSFS 0x10 ++ ++/* AX88172 Medium Status Register values */ ++#define AX88172_MEDIUM_FD 0x02 ++#define AX88172_MEDIUM_TX 0x04 ++#define AX88172_MEDIUM_FC 0x10 ++#define AX88172_MEDIUM_DEFAULT \ ++ ( AX88172_MEDIUM_FD | AX88172_MEDIUM_TX | AX88172_MEDIUM_FC ) ++ ++#define AX_MCAST_FILTER_SIZE 8 ++#define AX_MAX_MCAST 64 ++ ++#define AX_SWRESET_CLEAR 0x00 ++#define AX_SWRESET_RR 0x01 ++#define AX_SWRESET_RT 0x02 ++#define AX_SWRESET_PRTE 0x04 ++#define AX_SWRESET_PRL 0x08 ++#define AX_SWRESET_BZ 0x10 ++#define AX_SWRESET_IPRL 0x20 ++#define AX_SWRESET_IPPD 0x40 ++ ++#define AX88772_IPG0_DEFAULT 0x15 ++#define AX88772_IPG1_DEFAULT 0x0c ++#define AX88772_IPG2_DEFAULT 0x12 ++ ++/* AX88772 & AX88178 Medium Mode Register */ ++#define AX_MEDIUM_PF 0x0080 ++#define AX_MEDIUM_JFE 0x0040 ++#define AX_MEDIUM_TFC 0x0020 ++#define AX_MEDIUM_RFC 0x0010 ++#define AX_MEDIUM_ENCK 0x0008 ++#define AX_MEDIUM_AC 0x0004 ++#define AX_MEDIUM_FD 0x0002 ++#define AX_MEDIUM_GM 0x0001 ++#define AX_MEDIUM_SM 0x1000 ++#define AX_MEDIUM_SBP 0x0800 ++#define AX_MEDIUM_PS 0x0200 ++#define AX_MEDIUM_RE 0x0100 ++ ++#define AX88178_MEDIUM_DEFAULT \ ++ (AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \ ++ AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \ ++ AX_MEDIUM_RE) ++ ++#define AX88772_MEDIUM_DEFAULT \ ++ (AX_MEDIUM_FD | AX_MEDIUM_RFC | \ ++ AX_MEDIUM_TFC | AX_MEDIUM_PS | \ ++ AX_MEDIUM_AC | AX_MEDIUM_RE) ++ ++/* AX88772 & AX88178 RX_CTL values */ ++#define AX_RX_CTL_SO 0x0080 ++#define AX_RX_CTL_AP 0x0020 ++#define AX_RX_CTL_AM 0x0010 ++#define AX_RX_CTL_AB 0x0008 ++#define AX_RX_CTL_SEP 0x0004 ++#define AX_RX_CTL_AMALL 0x0002 ++#define AX_RX_CTL_PRO 0x0001 ++#define AX_RX_CTL_MFB_2048 0x0000 ++#define AX_RX_CTL_MFB_4096 0x0100 ++#define AX_RX_CTL_MFB_8192 0x0200 ++#define AX_RX_CTL_MFB_16384 0x0300 ++ ++#define AX_DEFAULT_RX_CTL (AX_RX_CTL_SO | AX_RX_CTL_AB) ++ ++/* GPIO 0 .. 2 toggles */ ++#define AX_GPIO_GPO0EN 0x01 /* GPIO0 Output enable */ ++#define AX_GPIO_GPO_0 0x02 /* GPIO0 Output value */ ++#define AX_GPIO_GPO1EN 0x04 /* GPIO1 Output enable */ ++#define AX_GPIO_GPO_1 0x08 /* GPIO1 Output value */ ++#define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */ ++#define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */ ++#define AX_GPIO_RESERVED 0x40 /* Reserved */ ++#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */ ++ ++#define AX_EEPROM_MAGIC 0xdeadbeef ++#define AX_EEPROM_LEN 0x200 ++ ++/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */ ++struct asix_data { ++ u8 multi_filter[AX_MCAST_FILTER_SIZE]; ++ u8 mac_addr[ETH_ALEN]; ++ u8 phymode; ++ u8 ledmode; ++ u8 res; ++}; ++ ++struct asix_rx_fixup_info { ++ struct sk_buff *ax_skb; ++ u32 header; ++ u16 size; ++ bool split_head; ++}; ++ ++struct asix_common_private { ++ struct asix_rx_fixup_info rx_fixup_info; ++}; ++ ++extern const struct driver_info ax88172a_info; ++ ++/* ASIX specific flags */ ++#define FLAG_EEPROM_MAC (1UL << 0) /* init device MAC from eeprom */ ++ ++int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data); ++ ++int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data); ++ ++void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, ++ u16 index, u16 size, void *data); ++ ++int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, ++ struct asix_rx_fixup_info *rx); ++int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb); ++ ++struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, ++ gfp_t flags); ++ ++int asix_set_sw_mii(struct usbnet *dev); ++int asix_set_hw_mii(struct usbnet *dev); ++ ++int asix_read_phy_addr(struct usbnet *dev, int internal); ++int asix_get_phy_addr(struct usbnet *dev); ++ ++int asix_sw_reset(struct usbnet *dev, u8 flags); ++ ++u16 asix_read_rx_ctl(struct usbnet *dev); ++int asix_write_rx_ctl(struct usbnet *dev, u16 mode); ++ ++u16 asix_read_medium_status(struct usbnet *dev); ++int asix_write_medium_mode(struct usbnet *dev, u16 mode); ++ ++int asix_write_gpio(struct usbnet *dev, u16 value, int sleep); ++ ++void asix_set_multicast(struct net_device *net); ++ ++int asix_mdio_read(struct net_device *netdev, int phy_id, int loc); ++void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val); ++ ++void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo); ++int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo); ++ ++int asix_get_eeprom_len(struct net_device *net); ++int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, ++ u8 *data); ++int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, ++ u8 *data); ++ ++void asix_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info); ++ ++int asix_set_mac_address(struct net_device *net, void *p); ++ ++#endif /* _ASIX_H */ +diff -Naur backports-3.18.1-1.org/drivers/net/usb/ax88172a.c backports-3.18.1-1/drivers/net/usb/ax88172a.c +--- backports-3.18.1-1.org/drivers/net/usb/ax88172a.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/ax88172a.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,422 @@ ++/* ++ * ASIX AX88172A based USB 2.0 Ethernet Devices ++ * Copyright (C) 2012 OMICRON electronics GmbH ++ * ++ * Supports external PHYs via phylib. Based on the driver for the ++ * AX88772. Original copyrights follow: ++ * ++ * Copyright (C) 2003-2006 David Hollis ++ * Copyright (C) 2005 Phil Chang ++ * Copyright (C) 2006 James Painter ++ * Copyright (c) 2002-2003 TiVo Inc. ++ * ++ * 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, see . ++ */ ++ ++#include "asix.h" ++#include ++ ++struct ax88172a_private { ++ struct mii_bus *mdio; ++ struct phy_device *phydev; ++ char phy_name[20]; ++ u16 phy_addr; ++ u16 oldmode; ++ int use_embdphy; ++ struct asix_rx_fixup_info rx_fixup_info; ++}; ++ ++/* MDIO read and write wrappers for phylib */ ++static int asix_mdio_bus_read(struct mii_bus *bus, int phy_id, int regnum) ++{ ++ return asix_mdio_read(((struct usbnet *)bus->priv)->net, phy_id, ++ regnum); ++} ++ ++static int asix_mdio_bus_write(struct mii_bus *bus, int phy_id, int regnum, ++ u16 val) ++{ ++ asix_mdio_write(((struct usbnet *)bus->priv)->net, phy_id, regnum, val); ++ return 0; ++} ++ ++static int ax88172a_ioctl(struct net_device *net, struct ifreq *rq, int cmd) ++{ ++ if (!netif_running(net)) ++ return -EINVAL; ++ ++ if (!net->phydev) ++ return -ENODEV; ++ ++ return phy_mii_ioctl(net->phydev, rq, cmd); ++} ++ ++/* set MAC link settings according to information from phylib */ ++static void ax88172a_adjust_link(struct net_device *netdev) ++{ ++ struct phy_device *phydev = netdev->phydev; ++ struct usbnet *dev = netdev_priv(netdev); ++ struct ax88172a_private *priv = dev->driver_priv; ++ u16 mode = 0; ++ ++ if (phydev->link) { ++ mode = AX88772_MEDIUM_DEFAULT; ++ ++ if (phydev->duplex == DUPLEX_HALF) ++ mode &= ~AX_MEDIUM_FD; ++ ++ if (phydev->speed != SPEED_100) ++ mode &= ~AX_MEDIUM_PS; ++ } ++ ++ if (mode != priv->oldmode) { ++ asix_write_medium_mode(dev, mode); ++ priv->oldmode = mode; ++ netdev_dbg(netdev, "speed %u duplex %d, setting mode to 0x%04x\n", ++ phydev->speed, phydev->duplex, mode); ++ phy_print_status(phydev); ++ } ++} ++ ++static void ax88172a_status(struct usbnet *dev, struct urb *urb) ++{ ++ /* link changes are detected by polling the phy */ ++} ++ ++/* use phylib infrastructure */ ++static int ax88172a_init_mdio(struct usbnet *dev) ++{ ++ struct ax88172a_private *priv = dev->driver_priv; ++ int ret, i; ++ ++ priv->mdio = mdiobus_alloc(); ++ if (!priv->mdio) { ++ netdev_err(dev->net, "Could not allocate MDIO bus\n"); ++ return -ENOMEM; ++ } ++ ++ priv->mdio->priv = (void *)dev; ++ priv->mdio->read = &asix_mdio_bus_read; ++ priv->mdio->write = &asix_mdio_bus_write; ++ priv->mdio->name = "Asix MDIO Bus"; ++ /* mii bus name is usb-- */ ++ snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d", ++ dev->udev->bus->busnum, dev->udev->devnum); ++ ++ priv->mdio->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); ++ if (!priv->mdio->irq) { ++ ret = -ENOMEM; ++ goto mfree; ++ } ++ for (i = 0; i < PHY_MAX_ADDR; i++) ++ priv->mdio->irq[i] = PHY_POLL; ++ ++ ret = mdiobus_register(priv->mdio); ++ if (ret) { ++ netdev_err(dev->net, "Could not register MDIO bus\n"); ++ goto ifree; ++ } ++ ++ netdev_info(dev->net, "registered mdio bus %s\n", priv->mdio->id); ++ return 0; ++ ++ifree: ++ kfree(priv->mdio->irq); ++mfree: ++ mdiobus_free(priv->mdio); ++ return ret; ++} ++ ++static void ax88172a_remove_mdio(struct usbnet *dev) ++{ ++ struct ax88172a_private *priv = dev->driver_priv; ++ ++ netdev_info(dev->net, "deregistering mdio bus %s\n", priv->mdio->id); ++ mdiobus_unregister(priv->mdio); ++ kfree(priv->mdio->irq); ++ mdiobus_free(priv->mdio); ++} ++ ++static const struct net_device_ops ax88172a_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = usbnet_change_mtu, ++ .ndo_set_mac_address = asix_set_mac_address, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_do_ioctl = ax88172a_ioctl, ++ .ndo_set_rx_mode = asix_set_multicast, ++}; ++ ++static int ax88172a_get_settings(struct net_device *net, ++ struct ethtool_cmd *cmd) ++{ ++ if (!net->phydev) ++ return -ENODEV; ++ ++ return phy_ethtool_gset(net->phydev, cmd); ++} ++ ++static int ax88172a_set_settings(struct net_device *net, ++ struct ethtool_cmd *cmd) ++{ ++ if (!net->phydev) ++ return -ENODEV; ++ ++ return phy_ethtool_sset(net->phydev, cmd); ++} ++ ++static int ax88172a_nway_reset(struct net_device *net) ++{ ++ if (!net->phydev) ++ return -ENODEV; ++ ++ return phy_start_aneg(net->phydev); ++} ++ ++static const struct ethtool_ops ax88172a_ethtool_ops = { ++ .get_drvinfo = asix_get_drvinfo, ++ .get_link = usbnet_get_link, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_wol = asix_get_wol, ++ .set_wol = asix_set_wol, ++ .get_eeprom_len = asix_get_eeprom_len, ++ .get_eeprom = asix_get_eeprom, ++ .set_eeprom = asix_set_eeprom, ++ .get_settings = ax88172a_get_settings, ++ .set_settings = ax88172a_set_settings, ++ .nway_reset = ax88172a_nway_reset, ++}; ++ ++static int ax88172a_reset_phy(struct usbnet *dev, int embd_phy) ++{ ++ int ret; ++ ++ ret = asix_sw_reset(dev, AX_SWRESET_IPPD); ++ if (ret < 0) ++ goto err; ++ ++ msleep(150); ++ ret = asix_sw_reset(dev, AX_SWRESET_CLEAR); ++ if (ret < 0) ++ goto err; ++ ++ msleep(150); ++ ++ ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD); ++ if (ret < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ return ret; ++} ++ ++ ++static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int ret; ++ u8 buf[ETH_ALEN]; ++ struct ax88172a_private *priv; ++ ++ usbnet_get_endpoints(dev, intf); ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ dev->driver_priv = priv; ++ ++ /* Get the MAC address */ ++ ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf); ++ if (ret < 0) { ++ netdev_err(dev->net, "Failed to read MAC address: %d\n", ret); ++ goto free; ++ } ++ memcpy(dev->net->dev_addr, buf, ETH_ALEN); ++ ++ dev->net->netdev_ops = &ax88172a_netdev_ops; ++ dev->net->ethtool_ops = &ax88172a_ethtool_ops; ++ ++ /* are we using the internal or the external phy? */ ++ ret = asix_read_cmd(dev, AX_CMD_SW_PHY_STATUS, 0, 0, 1, buf); ++ if (ret < 0) { ++ netdev_err(dev->net, "Failed to read software interface selection register: %d\n", ++ ret); ++ goto free; ++ } ++ ++ netdev_dbg(dev->net, "AX_CMD_SW_PHY_STATUS = 0x%02x\n", buf[0]); ++ switch (buf[0] & AX_PHY_SELECT_MASK) { ++ case AX_PHY_SELECT_INTERNAL: ++ netdev_dbg(dev->net, "use internal phy\n"); ++ priv->use_embdphy = 1; ++ break; ++ case AX_PHY_SELECT_EXTERNAL: ++ netdev_dbg(dev->net, "use external phy\n"); ++ priv->use_embdphy = 0; ++ break; ++ default: ++ netdev_err(dev->net, "Interface mode not supported by driver\n"); ++ ret = -ENOTSUPP; ++ goto free; ++ } ++ ++ priv->phy_addr = asix_read_phy_addr(dev, priv->use_embdphy); ++ ax88172a_reset_phy(dev, priv->use_embdphy); ++ ++ /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */ ++ if (dev->driver_info->flags & FLAG_FRAMING_AX) { ++ /* hard_mtu is still the default - the device does not support ++ jumbo eth frames */ ++ dev->rx_urb_size = 2048; ++ } ++ ++ /* init MDIO bus */ ++ ret = ax88172a_init_mdio(dev); ++ if (ret) ++ goto free; ++ ++ return 0; ++ ++free: ++ kfree(priv); ++ return ret; ++} ++ ++static int ax88172a_stop(struct usbnet *dev) ++{ ++ struct ax88172a_private *priv = dev->driver_priv; ++ ++ netdev_dbg(dev->net, "Stopping interface\n"); ++ ++ if (priv->phydev) { ++ netdev_info(dev->net, "Disconnecting from phy %s\n", ++ priv->phy_name); ++ phy_stop(priv->phydev); ++ phy_disconnect(priv->phydev); ++ } ++ ++ return 0; ++} ++ ++static void ax88172a_unbind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ struct ax88172a_private *priv = dev->driver_priv; ++ ++ ax88172a_remove_mdio(dev); ++ kfree(priv); ++} ++ ++static int ax88172a_reset(struct usbnet *dev) ++{ ++ struct asix_data *data = (struct asix_data *)&dev->data; ++ struct ax88172a_private *priv = dev->driver_priv; ++ int ret; ++ u16 rx_ctl; ++ ++ ax88172a_reset_phy(dev, priv->use_embdphy); ++ ++ msleep(150); ++ rx_ctl = asix_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); ++ ret = asix_write_rx_ctl(dev, 0x0000); ++ if (ret < 0) ++ goto out; ++ ++ rx_ctl = asix_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); ++ ++ msleep(150); ++ ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0, ++ AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, ++ AX88772_IPG2_DEFAULT, 0, NULL); ++ if (ret < 0) { ++ netdev_err(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret); ++ goto out; ++ } ++ ++ /* Rewrite MAC address */ ++ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); ++ ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, ++ data->mac_addr); ++ if (ret < 0) ++ goto out; ++ ++ /* Set RX_CTL to default values with 2k buffer, and enable cactus */ ++ ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL); ++ if (ret < 0) ++ goto out; ++ ++ rx_ctl = asix_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n", ++ rx_ctl); ++ ++ rx_ctl = asix_read_medium_status(dev); ++ netdev_dbg(dev->net, "Medium Status is 0x%04x after all initializations\n", ++ rx_ctl); ++ ++ /* Connect to PHY */ ++ snprintf(priv->phy_name, 20, PHY_ID_FMT, ++ priv->mdio->id, priv->phy_addr); ++ ++ priv->phydev = phy_connect(dev->net, priv->phy_name, ++ &ax88172a_adjust_link, ++ PHY_INTERFACE_MODE_MII); ++ if (IS_ERR(priv->phydev)) { ++ netdev_err(dev->net, "Could not connect to PHY device %s\n", ++ priv->phy_name); ++ ret = PTR_ERR(priv->phydev); ++ goto out; ++ } ++ ++ netdev_info(dev->net, "Connected to phy %s\n", priv->phy_name); ++ ++ /* During power-up, the AX88172A set the power down (BMCR_PDOWN) ++ * bit of the PHY. Bring the PHY up again. ++ */ ++ genphy_resume(priv->phydev); ++ phy_start(priv->phydev); ++ ++ return 0; ++ ++out: ++ return ret; ++ ++} ++ ++static int ax88172a_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ struct ax88172a_private *dp = dev->driver_priv; ++ struct asix_rx_fixup_info *rx = &dp->rx_fixup_info; ++ ++ return asix_rx_fixup_internal(dev, skb, rx); ++} ++ ++const struct driver_info ax88172a_info = { ++ .description = "ASIX AX88172A USB 2.0 Ethernet", ++ .bind = ax88172a_bind, ++ .reset = ax88172a_reset, ++ .stop = ax88172a_stop, ++ .unbind = ax88172a_unbind, ++ .status = ax88172a_status, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | ++ FLAG_MULTI_PACKET, ++ .rx_fixup = ax88172a_rx_fixup, ++ .tx_fixup = asix_tx_fixup, ++}; +diff -Naur backports-3.18.1-1.org/drivers/net/usb/ax88179_178a.c backports-3.18.1-1/drivers/net/usb/ax88179_178a.c +--- backports-3.18.1-1.org/drivers/net/usb/ax88179_178a.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/ax88179_178a.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,1756 @@ ++/* ++ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices ++ * ++ * Copyright (C) 2011-2013 ASIX ++ * ++ * 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, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AX88179_PHY_ID 0x03 ++#define AX_EEPROM_LEN 0x100 ++#define AX88179_EEPROM_MAGIC 0x17900b95 ++#define AX_MCAST_FLTSIZE 8 ++#define AX_MAX_MCAST 64 ++#define AX_INT_PPLS_LINK ((u32)BIT(16)) ++#define AX_RXHDR_L4_TYPE_MASK 0x1c ++#define AX_RXHDR_L4_TYPE_UDP 4 ++#define AX_RXHDR_L4_TYPE_TCP 16 ++#define AX_RXHDR_L3CSUM_ERR 2 ++#define AX_RXHDR_L4CSUM_ERR 1 ++#define AX_RXHDR_CRC_ERR ((u32)BIT(29)) ++#define AX_RXHDR_DROP_ERR ((u32)BIT(31)) ++#define AX_ACCESS_MAC 0x01 ++#define AX_ACCESS_PHY 0x02 ++#define AX_ACCESS_EEPROM 0x04 ++#define AX_ACCESS_EFUS 0x05 ++#define AX_PAUSE_WATERLVL_HIGH 0x54 ++#define AX_PAUSE_WATERLVL_LOW 0x55 ++ ++#define PHYSICAL_LINK_STATUS 0x02 ++ #define AX_USB_SS 0x04 ++ #define AX_USB_HS 0x02 ++ ++#define GENERAL_STATUS 0x03 ++/* Check AX88179 version. UA1:Bit2 = 0, UA2:Bit2 = 1 */ ++ #define AX_SECLD 0x04 ++ ++#define AX_SROM_ADDR 0x07 ++#define AX_SROM_CMD 0x0a ++ #define EEP_RD 0x04 ++ #define EEP_BUSY 0x10 ++ ++#define AX_SROM_DATA_LOW 0x08 ++#define AX_SROM_DATA_HIGH 0x09 ++ ++#define AX_RX_CTL 0x0b ++ #define AX_RX_CTL_DROPCRCERR 0x0100 ++ #define AX_RX_CTL_IPE 0x0200 ++ #define AX_RX_CTL_START 0x0080 ++ #define AX_RX_CTL_AP 0x0020 ++ #define AX_RX_CTL_AM 0x0010 ++ #define AX_RX_CTL_AB 0x0008 ++ #define AX_RX_CTL_AMALL 0x0002 ++ #define AX_RX_CTL_PRO 0x0001 ++ #define AX_RX_CTL_STOP 0x0000 ++ ++#define AX_NODE_ID 0x10 ++#define AX_MULFLTARY 0x16 ++ ++#define AX_MEDIUM_STATUS_MODE 0x22 ++ #define AX_MEDIUM_GIGAMODE 0x01 ++ #define AX_MEDIUM_FULL_DUPLEX 0x02 ++ #define AX_MEDIUM_EN_125MHZ 0x08 ++ #define AX_MEDIUM_RXFLOW_CTRLEN 0x10 ++ #define AX_MEDIUM_TXFLOW_CTRLEN 0x20 ++ #define AX_MEDIUM_RECEIVE_EN 0x100 ++ #define AX_MEDIUM_PS 0x200 ++ #define AX_MEDIUM_JUMBO_EN 0x8040 ++ ++#define AX_MONITOR_MOD 0x24 ++ #define AX_MONITOR_MODE_RWLC 0x02 ++ #define AX_MONITOR_MODE_RWMP 0x04 ++ #define AX_MONITOR_MODE_PMEPOL 0x20 ++ #define AX_MONITOR_MODE_PMETYPE 0x40 ++ ++#define AX_GPIO_CTRL 0x25 ++ #define AX_GPIO_CTRL_GPIO3EN 0x80 ++ #define AX_GPIO_CTRL_GPIO2EN 0x40 ++ #define AX_GPIO_CTRL_GPIO1EN 0x20 ++ ++#define AX_PHYPWR_RSTCTL 0x26 ++ #define AX_PHYPWR_RSTCTL_BZ 0x0010 ++ #define AX_PHYPWR_RSTCTL_IPRL 0x0020 ++ #define AX_PHYPWR_RSTCTL_AT 0x1000 ++ ++#define AX_RX_BULKIN_QCTRL 0x2e ++#define AX_CLK_SELECT 0x33 ++ #define AX_CLK_SELECT_BCS 0x01 ++ #define AX_CLK_SELECT_ACS 0x02 ++ #define AX_CLK_SELECT_ULR 0x08 ++ ++#define AX_RXCOE_CTL 0x34 ++ #define AX_RXCOE_IP 0x01 ++ #define AX_RXCOE_TCP 0x02 ++ #define AX_RXCOE_UDP 0x04 ++ #define AX_RXCOE_TCPV6 0x20 ++ #define AX_RXCOE_UDPV6 0x40 ++ ++#define AX_TXCOE_CTL 0x35 ++ #define AX_TXCOE_IP 0x01 ++ #define AX_TXCOE_TCP 0x02 ++ #define AX_TXCOE_UDP 0x04 ++ #define AX_TXCOE_TCPV6 0x20 ++ #define AX_TXCOE_UDPV6 0x40 ++ ++#define AX_LEDCTRL 0x73 ++ ++#define GMII_PHY_PHYSR 0x11 ++ #define GMII_PHY_PHYSR_SMASK 0xc000 ++ #define GMII_PHY_PHYSR_GIGA 0x8000 ++ #define GMII_PHY_PHYSR_100 0x4000 ++ #define GMII_PHY_PHYSR_FULL 0x2000 ++ #define GMII_PHY_PHYSR_LINK 0x400 ++ ++#define GMII_LED_ACT 0x1a ++ #define GMII_LED_ACTIVE_MASK 0xff8f ++ #define GMII_LED0_ACTIVE BIT(4) ++ #define GMII_LED1_ACTIVE BIT(5) ++ #define GMII_LED2_ACTIVE BIT(6) ++ ++#define GMII_LED_LINK 0x1c ++ #define GMII_LED_LINK_MASK 0xf888 ++ #define GMII_LED0_LINK_10 BIT(0) ++ #define GMII_LED0_LINK_100 BIT(1) ++ #define GMII_LED0_LINK_1000 BIT(2) ++ #define GMII_LED1_LINK_10 BIT(4) ++ #define GMII_LED1_LINK_100 BIT(5) ++ #define GMII_LED1_LINK_1000 BIT(6) ++ #define GMII_LED2_LINK_10 BIT(8) ++ #define GMII_LED2_LINK_100 BIT(9) ++ #define GMII_LED2_LINK_1000 BIT(10) ++ #define LED0_ACTIVE BIT(0) ++ #define LED0_LINK_10 BIT(1) ++ #define LED0_LINK_100 BIT(2) ++ #define LED0_LINK_1000 BIT(3) ++ #define LED0_FD BIT(4) ++ #define LED0_USB3_MASK 0x001f ++ #define LED1_ACTIVE BIT(5) ++ #define LED1_LINK_10 BIT(6) ++ #define LED1_LINK_100 BIT(7) ++ #define LED1_LINK_1000 BIT(8) ++ #define LED1_FD BIT(9) ++ #define LED1_USB3_MASK 0x03e0 ++ #define LED2_ACTIVE BIT(10) ++ #define LED2_LINK_1000 BIT(13) ++ #define LED2_LINK_100 BIT(12) ++ #define LED2_LINK_10 BIT(11) ++ #define LED2_FD BIT(14) ++ #define LED_VALID BIT(15) ++ #define LED2_USB3_MASK 0x7c00 ++ ++#define GMII_PHYPAGE 0x1e ++#define GMII_PHY_PAGE_SELECT 0x1f ++ #define GMII_PHY_PGSEL_EXT 0x0007 ++ #define GMII_PHY_PGSEL_PAGE0 0x0000 ++ #define GMII_PHY_PGSEL_PAGE3 0x0003 ++ #define GMII_PHY_PGSEL_PAGE5 0x0005 ++ ++struct ax88179_data { ++ u8 eee_enabled; ++ u8 eee_active; ++ u16 rxctl; ++ u16 reserved; ++}; ++ ++struct ax88179_int_data { ++ __le32 intdata1; ++ __le32 intdata2; ++}; ++ ++static const struct { ++ unsigned char ctrl, timer_l, timer_h, size, ifg; ++} AX88179_BULKIN_SIZE[] = { ++ {7, 0x4f, 0, 0x12, 0xff}, ++ {7, 0x20, 3, 0x16, 0xff}, ++ {7, 0xae, 7, 0x18, 0xff}, ++ {7, 0xcc, 0x4c, 0x18, 8}, ++}; ++ ++static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data, int in_pm) ++{ ++ int ret; ++ int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); ++ ++ BUG_ON(!dev); ++ ++ if (!in_pm) ++ fn = usbnet_read_cmd; ++ else ++ fn = usbnet_read_cmd_nopm; ++ ++ ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ value, index, data, size); ++ ++ if (unlikely(ret < 0)) ++ netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n", ++ index, ret); ++ ++ return ret; ++} ++ ++static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data, int in_pm) ++{ ++ int ret; ++ int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); ++ ++ BUG_ON(!dev); ++ ++ if (!in_pm) ++ fn = usbnet_write_cmd; ++ else ++ fn = usbnet_write_cmd_nopm; ++ ++ ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ value, index, data, size); ++ ++ if (unlikely(ret < 0)) ++ netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n", ++ index, ret); ++ ++ return ret; ++} ++ ++static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, ++ u16 index, u16 size, void *data) ++{ ++ u16 buf; ++ ++ if (2 == size) { ++ buf = *((u16 *)data); ++ cpu_to_le16s(&buf); ++ usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_DEVICE, value, index, &buf, ++ size); ++ } else { ++ usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_DEVICE, value, index, data, ++ size); ++ } ++} ++ ++static int ax88179_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, ++ u16 index, u16 size, void *data) ++{ ++ int ret; ++ ++ if (2 == size) { ++ u16 buf; ++ ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1); ++ le16_to_cpus(&buf); ++ *((u16 *)data) = buf; ++ } else if (4 == size) { ++ u32 buf; ++ ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1); ++ le32_to_cpus(&buf); ++ *((u32 *)data) = buf; ++ } else { ++ ret = __ax88179_read_cmd(dev, cmd, value, index, size, data, 1); ++ } ++ ++ return ret; ++} ++ ++static int ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, ++ u16 index, u16 size, void *data) ++{ ++ int ret; ++ ++ if (2 == size) { ++ u16 buf; ++ buf = *((u16 *)data); ++ cpu_to_le16s(&buf); ++ ret = __ax88179_write_cmd(dev, cmd, value, index, ++ size, &buf, 1); ++ } else { ++ ret = __ax88179_write_cmd(dev, cmd, value, index, ++ size, data, 1); ++ } ++ ++ return ret; ++} ++ ++static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data) ++{ ++ int ret; ++ ++ if (2 == size) { ++ u16 buf; ++ ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0); ++ le16_to_cpus(&buf); ++ *((u16 *)data) = buf; ++ } else if (4 == size) { ++ u32 buf; ++ ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0); ++ le32_to_cpus(&buf); ++ *((u32 *)data) = buf; ++ } else { ++ ret = __ax88179_read_cmd(dev, cmd, value, index, size, data, 0); ++ } ++ ++ return ret; ++} ++ ++static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data) ++{ ++ int ret; ++ ++ if (2 == size) { ++ u16 buf; ++ buf = *((u16 *)data); ++ cpu_to_le16s(&buf); ++ ret = __ax88179_write_cmd(dev, cmd, value, index, ++ size, &buf, 0); ++ } else { ++ ret = __ax88179_write_cmd(dev, cmd, value, index, ++ size, data, 0); ++ } ++ ++ return ret; ++} ++ ++static void ax88179_status(struct usbnet *dev, struct urb *urb) ++{ ++ struct ax88179_int_data *event; ++ u32 link; ++ ++ if (urb->actual_length < 8) ++ return; ++ ++ event = urb->transfer_buffer; ++ le32_to_cpus((void *)&event->intdata1); ++ ++ link = (((__force u32)event->intdata1) & AX_INT_PPLS_LINK) >> 16; ++ ++ if (netif_carrier_ok(dev->net) != link) { ++ usbnet_link_change(dev, link, 1); ++ netdev_info(dev->net, "ax88179 - Link status is: %d\n", link); ++ } ++} ++ ++static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ u16 res; ++ ++ ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res); ++ return res; ++} ++ ++static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc, ++ int val) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ u16 res = (u16) val; ++ ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res); ++} ++ ++static inline int ax88179_phy_mmd_indirect(struct usbnet *dev, u16 prtad, ++ u16 devad) ++{ ++ u16 tmp16; ++ int ret; ++ ++ tmp16 = devad; ++ ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ MII_MMD_CTRL, 2, &tmp16); ++ ++ tmp16 = prtad; ++ ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ MII_MMD_DATA, 2, &tmp16); ++ ++ tmp16 = devad | MII_MMD_CTRL_NOINCR; ++ ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ MII_MMD_CTRL, 2, &tmp16); ++ ++ return ret; ++} ++ ++static int ++ax88179_phy_read_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad) ++{ ++ int ret; ++ u16 tmp16; ++ ++ ax88179_phy_mmd_indirect(dev, prtad, devad); ++ ++ ret = ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ MII_MMD_DATA, 2, &tmp16); ++ if (ret < 0) ++ return ret; ++ ++ return tmp16; ++} ++ ++static int ++ax88179_phy_write_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad, ++ u16 data) ++{ ++ int ret; ++ ++ ax88179_phy_mmd_indirect(dev, prtad, devad); ++ ++ ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ MII_MMD_DATA, 2, &data); ++ ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int ax88179_suspend(struct usb_interface *intf, pm_message_t message) ++{ ++ struct usbnet *dev = usb_get_intfdata(intf); ++ u16 tmp16; ++ u8 tmp8; ++ ++ usbnet_suspend(intf, message); ++ ++ /* Disable RX path */ ++ ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, &tmp16); ++ tmp16 &= ~AX_MEDIUM_RECEIVE_EN; ++ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, &tmp16); ++ ++ /* Force bulk-in zero length */ ++ ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, ++ 2, 2, &tmp16); ++ ++ tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL; ++ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, ++ 2, 2, &tmp16); ++ ++ /* change clock */ ++ tmp8 = 0; ++ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); ++ ++ /* Configure RX control register => stop operation */ ++ tmp16 = AX_RX_CTL_STOP; ++ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); ++ ++ return 0; ++} ++ ++/* This function is used to enable the autodetach function. */ ++/* This function is determined by offset 0x43 of EEPROM */ ++static int ax88179_auto_detach(struct usbnet *dev, int in_pm) ++{ ++ u16 tmp16; ++ u8 tmp8; ++ int (*fnr)(struct usbnet *, u8, u16, u16, u16, void *); ++ int (*fnw)(struct usbnet *, u8, u16, u16, u16, void *); ++ ++ if (!in_pm) { ++ fnr = ax88179_read_cmd; ++ fnw = ax88179_write_cmd; ++ } else { ++ fnr = ax88179_read_cmd_nopm; ++ fnw = ax88179_write_cmd_nopm; ++ } ++ ++ if (fnr(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16) < 0) ++ return 0; ++ ++ if ((tmp16 == 0xFFFF) || (!(tmp16 & 0x0100))) ++ return 0; ++ ++ /* Enable Auto Detach bit */ ++ tmp8 = 0; ++ fnr(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); ++ tmp8 |= AX_CLK_SELECT_ULR; ++ fnw(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); ++ ++ fnr(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); ++ tmp16 |= AX_PHYPWR_RSTCTL_AT; ++ fnw(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); ++ ++ return 0; ++} ++ ++static int ax88179_resume(struct usb_interface *intf) ++{ ++ struct usbnet *dev = usb_get_intfdata(intf); ++ u16 tmp16; ++ u8 tmp8; ++ ++ usbnet_link_change(dev, 0, 0); ++ ++ /* Power up ethernet PHY */ ++ tmp16 = 0; ++ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, ++ 2, 2, &tmp16); ++ udelay(1000); ++ ++ tmp16 = AX_PHYPWR_RSTCTL_IPRL; ++ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, ++ 2, 2, &tmp16); ++ msleep(200); ++ ++ /* Ethernet PHY Auto Detach*/ ++ ax88179_auto_detach(dev, 1); ++ ++ /* Enable clock */ ++ ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); ++ tmp8 |= AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; ++ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8); ++ msleep(100); ++ ++ /* Configure RX control register => start operation */ ++ tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | ++ AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; ++ ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); ++ ++ return usbnet_resume(intf); ++} ++ ++static void ++ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ u8 opt; ++ ++ if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, ++ 1, 1, &opt) < 0) { ++ wolinfo->supported = 0; ++ wolinfo->wolopts = 0; ++ return; ++ } ++ ++ wolinfo->supported = WAKE_PHY | WAKE_MAGIC; ++ wolinfo->wolopts = 0; ++ if (opt & AX_MONITOR_MODE_RWLC) ++ wolinfo->wolopts |= WAKE_PHY; ++ if (opt & AX_MONITOR_MODE_RWMP) ++ wolinfo->wolopts |= WAKE_MAGIC; ++} ++ ++static int ++ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ u8 opt = 0; ++ ++ if (wolinfo->wolopts & WAKE_PHY) ++ opt |= AX_MONITOR_MODE_RWLC; ++ if (wolinfo->wolopts & WAKE_MAGIC) ++ opt |= AX_MONITOR_MODE_RWMP; ++ ++ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, ++ 1, 1, &opt) < 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int ax88179_get_eeprom_len(struct net_device *net) ++{ ++ return AX_EEPROM_LEN; ++} ++ ++static int ++ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, ++ u8 *data) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ u16 *eeprom_buff; ++ int first_word, last_word; ++ int i, ret; ++ ++ if (eeprom->len == 0) ++ return -EINVAL; ++ ++ eeprom->magic = AX88179_EEPROM_MAGIC; ++ ++ first_word = eeprom->offset >> 1; ++ last_word = (eeprom->offset + eeprom->len - 1) >> 1; ++ eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1), ++ GFP_KERNEL); ++ if (!eeprom_buff) ++ return -ENOMEM; ++ ++ /* ax88179/178A returns 2 bytes from eeprom on read */ ++ for (i = first_word; i <= last_word; i++) { ++ ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2, ++ &eeprom_buff[i - first_word], ++ 0); ++ if (ret < 0) { ++ kfree(eeprom_buff); ++ return -EIO; ++ } ++ } ++ ++ memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len); ++ kfree(eeprom_buff); ++ return 0; ++} ++ ++static int ax88179_get_settings(struct net_device *net, struct ethtool_cmd *cmd) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ return mii_ethtool_gset(&dev->mii, cmd); ++} ++ ++static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ return mii_ethtool_sset(&dev->mii, cmd); ++} ++ ++static int ++ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_eee *data) ++{ ++ int val; ++ ++ /* Get Supported EEE */ ++ val = ax88179_phy_read_mmd_indirect(dev, MDIO_PCS_EEE_ABLE, ++ MDIO_MMD_PCS); ++ if (val < 0) ++ return val; ++ data->supported = mmd_eee_cap_to_ethtool_sup_t(val); ++ ++ /* Get advertisement EEE */ ++ val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_ADV, ++ MDIO_MMD_AN); ++ if (val < 0) ++ return val; ++ data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); ++ ++ /* Get LP advertisement EEE */ ++ val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_LPABLE, ++ MDIO_MMD_AN); ++ if (val < 0) ++ return val; ++ data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); ++ ++ return 0; ++} ++ ++static int ++ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_eee *data) ++{ ++ u16 tmp16 = ethtool_adv_to_mmd_eee_adv_t(data->advertised); ++ ++ return ax88179_phy_write_mmd_indirect(dev, MDIO_AN_EEE_ADV, ++ MDIO_MMD_AN, tmp16); ++} ++ ++static int ax88179_chk_eee(struct usbnet *dev) ++{ ++ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; ++ struct ax88179_data *priv = (struct ax88179_data *)dev->data; ++ ++ mii_ethtool_gset(&dev->mii, &ecmd); ++ ++ if (ecmd.duplex & DUPLEX_FULL) { ++ int eee_lp, eee_cap, eee_adv; ++ u32 lp, cap, adv, supported = 0; ++ ++ eee_cap = ax88179_phy_read_mmd_indirect(dev, ++ MDIO_PCS_EEE_ABLE, ++ MDIO_MMD_PCS); ++ if (eee_cap < 0) { ++ priv->eee_active = 0; ++ return false; ++ } ++ ++ cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap); ++ if (!cap) { ++ priv->eee_active = 0; ++ return false; ++ } ++ ++ eee_lp = ax88179_phy_read_mmd_indirect(dev, ++ MDIO_AN_EEE_LPABLE, ++ MDIO_MMD_AN); ++ if (eee_lp < 0) { ++ priv->eee_active = 0; ++ return false; ++ } ++ ++ eee_adv = ax88179_phy_read_mmd_indirect(dev, ++ MDIO_AN_EEE_ADV, ++ MDIO_MMD_AN); ++ ++ if (eee_adv < 0) { ++ priv->eee_active = 0; ++ return false; ++ } ++ ++ adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv); ++ lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp); ++ supported = (ecmd.speed == SPEED_1000) ? ++ SUPPORTED_1000baseT_Full : ++ SUPPORTED_100baseT_Full; ++ ++ if (!(lp & adv & supported)) { ++ priv->eee_active = 0; ++ return false; ++ } ++ ++ priv->eee_active = 1; ++ return true; ++ } ++ ++ priv->eee_active = 0; ++ return false; ++} ++ ++static void ax88179_disable_eee(struct usbnet *dev) ++{ ++ u16 tmp16; ++ ++ tmp16 = GMII_PHY_PGSEL_PAGE3; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_PHY_PAGE_SELECT, 2, &tmp16); ++ ++ tmp16 = 0x3246; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ MII_PHYADDR, 2, &tmp16); ++ ++ tmp16 = GMII_PHY_PGSEL_PAGE0; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_PHY_PAGE_SELECT, 2, &tmp16); ++} ++ ++static void ax88179_enable_eee(struct usbnet *dev) ++{ ++ u16 tmp16; ++ ++ tmp16 = GMII_PHY_PGSEL_PAGE3; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_PHY_PAGE_SELECT, 2, &tmp16); ++ ++ tmp16 = 0x3247; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ MII_PHYADDR, 2, &tmp16); ++ ++ tmp16 = GMII_PHY_PGSEL_PAGE5; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_PHY_PAGE_SELECT, 2, &tmp16); ++ ++ tmp16 = 0x0680; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ MII_BMSR, 2, &tmp16); ++ ++ tmp16 = GMII_PHY_PGSEL_PAGE0; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_PHY_PAGE_SELECT, 2, &tmp16); ++} ++ ++static int ax88179_get_eee(struct net_device *net, struct ethtool_eee *edata) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct ax88179_data *priv = (struct ax88179_data *)dev->data; ++ ++ edata->eee_enabled = priv->eee_enabled; ++ edata->eee_active = priv->eee_active; ++ ++ return ax88179_ethtool_get_eee(dev, edata); ++} ++ ++static int ax88179_set_eee(struct net_device *net, struct ethtool_eee *edata) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct ax88179_data *priv = (struct ax88179_data *)dev->data; ++ int ret = -EOPNOTSUPP; ++ ++ priv->eee_enabled = edata->eee_enabled; ++ if (!priv->eee_enabled) { ++ ax88179_disable_eee(dev); ++ } else { ++ priv->eee_enabled = ax88179_chk_eee(dev); ++ if (!priv->eee_enabled) ++ return -EOPNOTSUPP; ++ ++ ax88179_enable_eee(dev); ++ } ++ ++ ret = ax88179_ethtool_set_eee(dev, edata); ++ if (ret) ++ return ret; ++ ++ mii_nway_restart(&dev->mii); ++ ++ usbnet_link_change(dev, 0, 0); ++ ++ return ret; ++} ++ ++static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); ++} ++ ++static const struct ethtool_ops ax88179_ethtool_ops = { ++ .get_link = ethtool_op_get_link, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_wol = ax88179_get_wol, ++ .set_wol = ax88179_set_wol, ++ .get_eeprom_len = ax88179_get_eeprom_len, ++ .get_eeprom = ax88179_get_eeprom, ++ .get_settings = ax88179_get_settings, ++ .set_settings = ax88179_set_settings, ++ .get_eee = ax88179_get_eee, ++ .set_eee = ax88179_set_eee, ++ .nway_reset = usbnet_nway_reset, ++}; ++ ++static void ax88179_set_multicast(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct ax88179_data *data = (struct ax88179_data *)dev->data; ++ u8 *m_filter = ((u8 *)dev->data) + 12; ++ ++ data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE); ++ ++ if (net->flags & IFF_PROMISC) { ++ data->rxctl |= AX_RX_CTL_PRO; ++ } else if (net->flags & IFF_ALLMULTI || ++ netdev_mc_count(net) > AX_MAX_MCAST) { ++ data->rxctl |= AX_RX_CTL_AMALL; ++ } else if (netdev_mc_empty(net)) { ++ /* just broadcast and directed */ ++ } else { ++ /* We use the 20 byte dev->data for our 8 byte filter buffer ++ * to avoid allocating memory that is tricky to free later ++ */ ++ u32 crc_bits; ++ struct netdev_hw_addr *ha; ++ ++ memset(m_filter, 0, AX_MCAST_FLTSIZE); ++ ++ netdev_for_each_mc_addr(ha, net) { ++ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; ++ *(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7)); ++ } ++ ++ ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY, ++ AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE, ++ m_filter); ++ ++ data->rxctl |= AX_RX_CTL_AM; ++ } ++ ++ ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL, ++ 2, 2, &data->rxctl); ++} ++ ++static int ++ax88179_set_features(struct net_device *net, netdev_features_t features) ++{ ++ u8 tmp; ++ struct usbnet *dev = netdev_priv(net); ++ netdev_features_t changed = net->features ^ features; ++ ++ if (changed & NETIF_F_IP_CSUM) { ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); ++ tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); ++ } ++ ++ if (changed & NETIF_F_IPV6_CSUM) { ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); ++ tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); ++ } ++ ++ if (changed & NETIF_F_RXCSUM) { ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp); ++ tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | ++ AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp); ++ } ++ ++ return 0; ++} ++ ++static int ax88179_change_mtu(struct net_device *net, int new_mtu) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ u16 tmp16; ++ ++ if (new_mtu <= 0 || new_mtu > 4088) ++ return -EINVAL; ++ ++ net->mtu = new_mtu; ++ dev->hard_mtu = net->mtu + net->hard_header_len; ++ ++ if (net->mtu > 1500) { ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, &tmp16); ++ tmp16 |= AX_MEDIUM_JUMBO_EN; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, &tmp16); ++ } else { ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, &tmp16); ++ tmp16 &= ~AX_MEDIUM_JUMBO_EN; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, &tmp16); ++ } ++ ++ /* max qlen depend on hard_mtu and rx_urb_size */ ++ usbnet_update_max_qlen(dev); ++ ++ return 0; ++} ++ ++static int ax88179_set_mac_addr(struct net_device *net, void *p) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct sockaddr *addr = p; ++ int ret; ++ ++ if (netif_running(net)) ++ return -EBUSY; ++ if (!is_valid_ether_addr(addr->sa_data)) ++ return -EADDRNOTAVAIL; ++ ++ memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); ++ ++ /* Set the MAC address */ ++ ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ++ ETH_ALEN, net->dev_addr); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static const struct net_device_ops ax88179_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = ax88179_change_mtu, ++ .ndo_set_mac_address = ax88179_set_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_do_ioctl = ax88179_ioctl, ++ .ndo_set_rx_mode = ax88179_set_multicast, ++ .ndo_set_features = ax88179_set_features, ++}; ++ ++static int ax88179_check_eeprom(struct usbnet *dev) ++{ ++ u8 i, buf, eeprom[20]; ++ u16 csum, delay = HZ / 10; ++ unsigned long jtimeout; ++ ++ /* Read EEPROM content */ ++ for (i = 0; i < 6; i++) { ++ buf = i; ++ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR, ++ 1, 1, &buf) < 0) ++ return -EINVAL; ++ ++ buf = EEP_RD; ++ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, ++ 1, 1, &buf) < 0) ++ return -EINVAL; ++ ++ jtimeout = jiffies + delay; ++ do { ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, ++ 1, 1, &buf); ++ ++ if (time_after(jiffies, jtimeout)) ++ return -EINVAL; ++ ++ } while (buf & EEP_BUSY); ++ ++ __ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW, ++ 2, 2, &eeprom[i * 2], 0); ++ ++ if ((i == 0) && (eeprom[0] == 0xFF)) ++ return -EINVAL; ++ } ++ ++ csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9]; ++ csum = (csum >> 8) + (csum & 0xff); ++ if ((csum + eeprom[10]) != 0xff) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode) ++{ ++ u8 i; ++ u8 efuse[64]; ++ u16 csum = 0; ++ ++ if (ax88179_read_cmd(dev, AX_ACCESS_EFUS, 0, 64, 64, efuse) < 0) ++ return -EINVAL; ++ ++ if (*efuse == 0xFF) ++ return -EINVAL; ++ ++ for (i = 0; i < 64; i++) ++ csum = csum + efuse[i]; ++ ++ while (csum > 255) ++ csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF); ++ ++ if (csum != 0xFF) ++ return -EINVAL; ++ ++ *ledmode = (efuse[51] << 8) | efuse[52]; ++ ++ return 0; ++} ++ ++static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue) ++{ ++ u16 led; ++ ++ /* Loaded the old eFuse LED Mode */ ++ if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0) ++ return -EINVAL; ++ ++ led >>= 8; ++ switch (led) { ++ case 0xFF: ++ led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 | ++ LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 | ++ LED2_LINK_100 | LED2_LINK_1000 | LED_VALID; ++ break; ++ case 0xFE: ++ led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | LED_VALID; ++ break; ++ case 0xFD: ++ led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | ++ LED2_LINK_10 | LED_VALID; ++ break; ++ case 0xFC: ++ led = LED0_ACTIVE | LED1_ACTIVE | LED1_LINK_1000 | LED2_ACTIVE | ++ LED2_LINK_100 | LED2_LINK_10 | LED_VALID; ++ break; ++ default: ++ led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 | ++ LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 | ++ LED2_LINK_100 | LED2_LINK_1000 | LED_VALID; ++ break; ++ } ++ ++ *ledvalue = led; ++ ++ return 0; ++} ++ ++static int ax88179_led_setting(struct usbnet *dev) ++{ ++ u8 ledfd, value = 0; ++ u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10; ++ unsigned long jtimeout; ++ ++ /* Check AX88179 version. UA1 or UA2*/ ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value); ++ ++ if (!(value & AX_SECLD)) { /* UA1 */ ++ value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN | ++ AX_GPIO_CTRL_GPIO1EN; ++ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_GPIO_CTRL, ++ 1, 1, &value) < 0) ++ return -EINVAL; ++ } ++ ++ /* Check EEPROM */ ++ if (!ax88179_check_eeprom(dev)) { ++ value = 0x42; ++ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR, ++ 1, 1, &value) < 0) ++ return -EINVAL; ++ ++ value = EEP_RD; ++ if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, ++ 1, 1, &value) < 0) ++ return -EINVAL; ++ ++ jtimeout = jiffies + delay; ++ do { ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD, ++ 1, 1, &value); ++ ++ if (time_after(jiffies, jtimeout)) ++ return -EINVAL; ++ ++ } while (value & EEP_BUSY); ++ ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_HIGH, ++ 1, 1, &value); ++ ledvalue = (value << 8); ++ ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW, ++ 1, 1, &value); ++ ledvalue |= value; ++ ++ /* load internal ROM for defaule setting */ ++ if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0)) ++ ax88179_convert_old_led(dev, &ledvalue); ++ ++ } else if (!ax88179_check_efuse(dev, &ledvalue)) { ++ if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0)) ++ ax88179_convert_old_led(dev, &ledvalue); ++ } else { ++ ax88179_convert_old_led(dev, &ledvalue); ++ } ++ ++ tmp = GMII_PHY_PGSEL_EXT; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_PHY_PAGE_SELECT, 2, &tmp); ++ ++ tmp = 0x2c; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_PHYPAGE, 2, &tmp); ++ ++ ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_LED_ACT, 2, &ledact); ++ ++ ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_LED_LINK, 2, &ledlink); ++ ++ ledact &= GMII_LED_ACTIVE_MASK; ++ ledlink &= GMII_LED_LINK_MASK; ++ ++ if (ledvalue & LED0_ACTIVE) ++ ledact |= GMII_LED0_ACTIVE; ++ ++ if (ledvalue & LED1_ACTIVE) ++ ledact |= GMII_LED1_ACTIVE; ++ ++ if (ledvalue & LED2_ACTIVE) ++ ledact |= GMII_LED2_ACTIVE; ++ ++ if (ledvalue & LED0_LINK_10) ++ ledlink |= GMII_LED0_LINK_10; ++ ++ if (ledvalue & LED1_LINK_10) ++ ledlink |= GMII_LED1_LINK_10; ++ ++ if (ledvalue & LED2_LINK_10) ++ ledlink |= GMII_LED2_LINK_10; ++ ++ if (ledvalue & LED0_LINK_100) ++ ledlink |= GMII_LED0_LINK_100; ++ ++ if (ledvalue & LED1_LINK_100) ++ ledlink |= GMII_LED1_LINK_100; ++ ++ if (ledvalue & LED2_LINK_100) ++ ledlink |= GMII_LED2_LINK_100; ++ ++ if (ledvalue & LED0_LINK_1000) ++ ledlink |= GMII_LED0_LINK_1000; ++ ++ if (ledvalue & LED1_LINK_1000) ++ ledlink |= GMII_LED1_LINK_1000; ++ ++ if (ledvalue & LED2_LINK_1000) ++ ledlink |= GMII_LED2_LINK_1000; ++ ++ tmp = ledact; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_LED_ACT, 2, &tmp); ++ ++ tmp = ledlink; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_LED_LINK, 2, &tmp); ++ ++ tmp = GMII_PHY_PGSEL_PAGE0; ++ ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_PHY_PAGE_SELECT, 2, &tmp); ++ ++ /* LED full duplex setting */ ++ ledfd = 0; ++ if (ledvalue & LED0_FD) ++ ledfd |= 0x01; ++ else if ((ledvalue & LED0_USB3_MASK) == 0) ++ ledfd |= 0x02; ++ ++ if (ledvalue & LED1_FD) ++ ledfd |= 0x04; ++ else if ((ledvalue & LED1_USB3_MASK) == 0) ++ ledfd |= 0x08; ++ ++ if (ledvalue & LED2_FD) ++ ledfd |= 0x10; ++ else if ((ledvalue & LED2_USB3_MASK) == 0) ++ ledfd |= 0x20; ++ ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd); ++ ++ return 0; ++} ++ ++static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ u8 buf[5]; ++ u16 *tmp16; ++ u8 *tmp; ++ struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; ++ struct ethtool_eee eee_data; ++ ++ usbnet_get_endpoints(dev, intf); ++ ++ tmp16 = (u16 *)buf; ++ tmp = (u8 *)buf; ++ ++ memset(ax179_data, 0, sizeof(*ax179_data)); ++ ++ /* Power up ethernet PHY */ ++ *tmp16 = 0; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); ++ *tmp16 = AX_PHYPWR_RSTCTL_IPRL; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); ++ msleep(200); ++ ++ *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); ++ msleep(100); ++ ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ++ ETH_ALEN, dev->net->dev_addr); ++ memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN); ++ ++ /* RX bulk configuration */ ++ memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); ++ ++ dev->rx_urb_size = 1024 * 20; ++ ++ *tmp = 0x34; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp); ++ ++ *tmp = 0x52; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, ++ 1, 1, tmp); ++ ++ dev->net->netdev_ops = &ax88179_netdev_ops; ++ dev->net->ethtool_ops = &ax88179_ethtool_ops; ++ dev->net->needed_headroom = 8; ++ ++ /* Initialize MII structure */ ++ dev->mii.dev = dev->net; ++ dev->mii.mdio_read = ax88179_mdio_read; ++ dev->mii.mdio_write = ax88179_mdio_write; ++ dev->mii.phy_id_mask = 0xff; ++ dev->mii.reg_num_mask = 0xff; ++ dev->mii.phy_id = 0x03; ++ dev->mii.supports_gmii = 1; ++ ++ dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_RXCSUM; ++ ++ dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_RXCSUM; ++ ++ /* Enable checksum offload */ ++ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | ++ AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp); ++ ++ *tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP | ++ AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp); ++ ++ /* Configure RX control register => start operation */ ++ *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | ++ AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16); ++ ++ *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | ++ AX_MONITOR_MODE_RWMP; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp); ++ ++ /* Configure default medium type => giga */ ++ *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | ++ AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | ++ AX_MEDIUM_GIGAMODE; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, tmp16); ++ ++ ax88179_led_setting(dev); ++ ++ ax179_data->eee_enabled = 0; ++ ax179_data->eee_active = 0; ++ ++ ax88179_disable_eee(dev); ++ ++ ax88179_ethtool_get_eee(dev, &eee_data); ++ eee_data.advertised = 0; ++ ax88179_ethtool_set_eee(dev, &eee_data); ++ ++ /* Restart autoneg */ ++ mii_nway_restart(&dev->mii); ++ ++ usbnet_link_change(dev, 0, 0); ++ ++ return 0; ++} ++ ++static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ u16 tmp16; ++ ++ /* Configure RX control register => stop operation */ ++ tmp16 = AX_RX_CTL_STOP; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); ++ ++ tmp16 = 0; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16); ++ ++ /* Power down ethernet PHY */ ++ tmp16 = 0; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); ++} ++ ++static void ++ax88179_rx_checksum(struct sk_buff *skb, u32 *pkt_hdr) ++{ ++ skb->ip_summed = CHECKSUM_NONE; ++ ++ /* checksum error bit is set */ ++ if ((*pkt_hdr & AX_RXHDR_L3CSUM_ERR) || ++ (*pkt_hdr & AX_RXHDR_L4CSUM_ERR)) ++ return; ++ ++ /* It must be a TCP or UDP packet with a valid checksum */ ++ if (((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_TCP) || ++ ((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_UDP)) ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++} ++ ++static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ struct sk_buff *ax_skb; ++ int pkt_cnt; ++ u32 rx_hdr; ++ u16 hdr_off; ++ u32 *pkt_hdr; ++ ++ /* This check is no longer done by usbnet */ ++ if (skb->len < dev->net->hard_header_len) ++ return 0; ++ ++ skb_trim(skb, skb->len - 4); ++ memcpy(&rx_hdr, skb_tail_pointer(skb), 4); ++ le32_to_cpus(&rx_hdr); ++ ++ pkt_cnt = (u16)rx_hdr; ++ hdr_off = (u16)(rx_hdr >> 16); ++ pkt_hdr = (u32 *)(skb->data + hdr_off); ++ ++ while (pkt_cnt--) { ++ u16 pkt_len; ++ ++ le32_to_cpus(pkt_hdr); ++ pkt_len = (*pkt_hdr >> 16) & 0x1fff; ++ ++ /* Check CRC or runt packet */ ++ if ((*pkt_hdr & AX_RXHDR_CRC_ERR) || ++ (*pkt_hdr & AX_RXHDR_DROP_ERR)) { ++ skb_pull(skb, (pkt_len + 7) & 0xFFF8); ++ pkt_hdr++; ++ continue; ++ } ++ ++ if (pkt_cnt == 0) { ++ /* Skip IP alignment psudo header */ ++ skb_pull(skb, 2); ++ skb->len = pkt_len; ++ skb_set_tail_pointer(skb, pkt_len); ++ skb->truesize = pkt_len + sizeof(struct sk_buff); ++ ax88179_rx_checksum(skb, pkt_hdr); ++ return 1; ++ } ++ ++ ax_skb = skb_clone(skb, GFP_ATOMIC); ++ if (ax_skb) { ++ ax_skb->len = pkt_len; ++ ax_skb->data = skb->data + 2; ++ skb_set_tail_pointer(ax_skb, pkt_len); ++ ax_skb->truesize = pkt_len + sizeof(struct sk_buff); ++ ax88179_rx_checksum(ax_skb, pkt_hdr); ++ usbnet_skb_return(dev, ax_skb); ++ } else { ++ return 0; ++ } ++ ++ skb_pull(skb, (pkt_len + 7) & 0xFFF8); ++ pkt_hdr++; ++ } ++ return 1; ++} ++ ++static struct sk_buff * ++ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) ++{ ++ u32 tx_hdr1, tx_hdr2; ++ int frame_size = dev->maxpacket; ++ int mss = skb_shinfo(skb)->gso_size; ++ int headroom; ++ ++ tx_hdr1 = skb->len; ++ tx_hdr2 = mss; ++ if (((skb->len + 8) % frame_size) == 0) ++ tx_hdr2 |= 0x80008000; /* Enable padding */ ++ ++ headroom = skb_headroom(skb) - 8; ++ ++ if ((skb_header_cloned(skb) || headroom < 0) && ++ pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) { ++ dev_kfree_skb_any(skb); ++ return NULL; ++ } ++ ++ skb_push(skb, 4); ++ cpu_to_le32s(&tx_hdr2); ++ skb_copy_to_linear_data(skb, &tx_hdr2, 4); ++ ++ skb_push(skb, 4); ++ cpu_to_le32s(&tx_hdr1); ++ skb_copy_to_linear_data(skb, &tx_hdr1, 4); ++ ++ return skb; ++} ++ ++static int ax88179_link_reset(struct usbnet *dev) ++{ ++ struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; ++ u8 tmp[5], link_sts; ++ u16 mode, tmp16, delay = HZ / 10; ++ u32 tmp32 = 0x40000000; ++ unsigned long jtimeout; ++ ++ jtimeout = jiffies + delay; ++ while (tmp32 & 0x40000000) { ++ mode = 0; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &mode); ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, ++ &ax179_data->rxctl); ++ ++ /*link up, check the usb device control TX FIFO full or empty*/ ++ ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32); ++ ++ if (time_after(jiffies, jtimeout)) ++ return 0; ++ } ++ ++ mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | ++ AX_MEDIUM_RXFLOW_CTRLEN; ++ ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, ++ 1, 1, &link_sts); ++ ++ ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, ++ GMII_PHY_PHYSR, 2, &tmp16); ++ ++ if (!(tmp16 & GMII_PHY_PHYSR_LINK)) { ++ return 0; ++ } else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) { ++ mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ; ++ if (dev->net->mtu > 1500) ++ mode |= AX_MEDIUM_JUMBO_EN; ++ ++ if (link_sts & AX_USB_SS) ++ memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); ++ else if (link_sts & AX_USB_HS) ++ memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5); ++ else ++ memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); ++ } else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) { ++ mode |= AX_MEDIUM_PS; ++ ++ if (link_sts & (AX_USB_SS | AX_USB_HS)) ++ memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5); ++ else ++ memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); ++ } else { ++ memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); ++ } ++ ++ /* RX bulk configuration */ ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); ++ ++ dev->rx_urb_size = (1024 * (tmp[3] + 2)); ++ ++ if (tmp16 & GMII_PHY_PHYSR_FULL) ++ mode |= AX_MEDIUM_FULL_DUPLEX; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, &mode); ++ ++ ax179_data->eee_enabled = ax88179_chk_eee(dev); ++ ++ netif_carrier_on(dev->net); ++ ++ return 0; ++} ++ ++static int ax88179_reset(struct usbnet *dev) ++{ ++ u8 buf[5]; ++ u16 *tmp16; ++ u8 *tmp; ++ struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; ++ struct ethtool_eee eee_data; ++ ++ tmp16 = (u16 *)buf; ++ tmp = (u8 *)buf; ++ ++ /* Power up ethernet PHY */ ++ *tmp16 = 0; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); ++ ++ *tmp16 = AX_PHYPWR_RSTCTL_IPRL; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); ++ msleep(200); ++ ++ *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); ++ msleep(100); ++ ++ /* Ethernet PHY Auto Detach*/ ++ ax88179_auto_detach(dev, 0); ++ ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN, ++ dev->net->dev_addr); ++ memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN); ++ ++ /* RX bulk configuration */ ++ memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); ++ ++ dev->rx_urb_size = 1024 * 20; ++ ++ *tmp = 0x34; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp); ++ ++ *tmp = 0x52; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, ++ 1, 1, tmp); ++ ++ dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_RXCSUM; ++ ++ dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_RXCSUM; ++ ++ /* Enable checksum offload */ ++ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | ++ AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp); ++ ++ *tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP | ++ AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp); ++ ++ /* Configure RX control register => start operation */ ++ *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | ++ AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16); ++ ++ *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | ++ AX_MONITOR_MODE_RWMP; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp); ++ ++ /* Configure default medium type => giga */ ++ *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | ++ AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | ++ AX_MEDIUM_GIGAMODE; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, tmp16); ++ ++ ax88179_led_setting(dev); ++ ++ ax179_data->eee_enabled = 0; ++ ax179_data->eee_active = 0; ++ ++ ax88179_disable_eee(dev); ++ ++ ax88179_ethtool_get_eee(dev, &eee_data); ++ eee_data.advertised = 0; ++ ax88179_ethtool_set_eee(dev, &eee_data); ++ ++ /* Restart autoneg */ ++ mii_nway_restart(&dev->mii); ++ ++ usbnet_link_change(dev, 0, 0); ++ ++ return 0; ++} ++ ++static int ax88179_stop(struct usbnet *dev) ++{ ++ u16 tmp16; ++ ++ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, &tmp16); ++ tmp16 &= ~AX_MEDIUM_RECEIVE_EN; ++ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, ++ 2, 2, &tmp16); ++ ++ return 0; ++} ++ ++static const struct driver_info ax88179_info = { ++ .description = "ASIX AX88179 USB 3.0 Gigabit Ethernet", ++ .bind = ax88179_bind, ++ .unbind = ax88179_unbind, ++ .status = ax88179_status, ++ .link_reset = ax88179_link_reset, ++ .reset = ax88179_reset, ++ .stop = ax88179_stop, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX, ++ .rx_fixup = ax88179_rx_fixup, ++ .tx_fixup = ax88179_tx_fixup, ++}; ++ ++static const struct driver_info ax88178a_info = { ++ .description = "ASIX AX88178A USB 2.0 Gigabit Ethernet", ++ .bind = ax88179_bind, ++ .unbind = ax88179_unbind, ++ .status = ax88179_status, ++ .link_reset = ax88179_link_reset, ++ .reset = ax88179_reset, ++ .stop = ax88179_stop, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX, ++ .rx_fixup = ax88179_rx_fixup, ++ .tx_fixup = ax88179_tx_fixup, ++}; ++ ++static const struct driver_info dlink_dub1312_info = { ++ .description = "D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter", ++ .bind = ax88179_bind, ++ .unbind = ax88179_unbind, ++ .status = ax88179_status, ++ .link_reset = ax88179_link_reset, ++ .reset = ax88179_reset, ++ .stop = ax88179_stop, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX, ++ .rx_fixup = ax88179_rx_fixup, ++ .tx_fixup = ax88179_tx_fixup, ++}; ++ ++static const struct driver_info sitecom_info = { ++ .description = "Sitecom USB 3.0 to Gigabit Adapter", ++ .bind = ax88179_bind, ++ .unbind = ax88179_unbind, ++ .status = ax88179_status, ++ .link_reset = ax88179_link_reset, ++ .reset = ax88179_reset, ++ .stop = ax88179_stop, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX, ++ .rx_fixup = ax88179_rx_fixup, ++ .tx_fixup = ax88179_tx_fixup, ++}; ++ ++static const struct driver_info samsung_info = { ++ .description = "Samsung USB Ethernet Adapter", ++ .bind = ax88179_bind, ++ .unbind = ax88179_unbind, ++ .status = ax88179_status, ++ .link_reset = ax88179_link_reset, ++ .reset = ax88179_reset, ++ .stop = ax88179_stop, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX, ++ .rx_fixup = ax88179_rx_fixup, ++ .tx_fixup = ax88179_tx_fixup, ++}; ++ ++static const struct driver_info lenovo_info = { ++ .description = "Lenovo OneLinkDock Gigabit LAN", ++ .bind = ax88179_bind, ++ .unbind = ax88179_unbind, ++ .status = ax88179_status, ++ .link_reset = ax88179_link_reset, ++ .reset = ax88179_reset, ++ .stop = ax88179_stop, ++ .flags = FLAG_ETHER | FLAG_FRAMING_AX, ++ .rx_fixup = ax88179_rx_fixup, ++ .tx_fixup = ax88179_tx_fixup, ++}; ++ ++static const struct usb_device_id products[] = { ++{ ++ /* ASIX AX88179 10/100/1000 */ ++ USB_DEVICE(0x0b95, 0x1790), ++ .driver_info = (unsigned long)&ax88179_info, ++}, { ++ /* ASIX AX88178A 10/100/1000 */ ++ USB_DEVICE(0x0b95, 0x178a), ++ .driver_info = (unsigned long)&ax88178a_info, ++}, { ++ /* D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter */ ++ USB_DEVICE(0x2001, 0x4a00), ++ .driver_info = (unsigned long)&dlink_dub1312_info, ++}, { ++ /* Sitecom USB 3.0 to Gigabit Adapter */ ++ USB_DEVICE(0x0df6, 0x0072), ++ .driver_info = (unsigned long)&sitecom_info, ++}, { ++ /* Samsung USB Ethernet Adapter */ ++ USB_DEVICE(0x04e8, 0xa100), ++ .driver_info = (unsigned long)&samsung_info, ++}, { ++ /* Lenovo OneLinkDock Gigabit LAN */ ++ USB_DEVICE(0x17ef, 0x304b), ++ .driver_info = (unsigned long)&lenovo_info, ++}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver ax88179_178a_driver = { ++ .name = "ax88179_178a", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .suspend = ax88179_suspend, ++ .resume = ax88179_resume, ++ .reset_resume = ax88179_resume, ++ .disconnect = usbnet_disconnect, ++ .supports_autosuspend = 1, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(ax88179_178a_driver); ++ ++MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/catc.c backports-3.18.1-1/drivers/net/usb/catc.c +--- backports-3.18.1-1.org/drivers/net/usb/catc.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/catc.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,965 @@ ++/* ++ * Copyright (c) 2001 Vojtech Pavlik ++ * ++ * CATC EL1210A NetMate USB Ethernet driver ++ * ++ * Sponsored by SuSE ++ * ++ * Based on the work of ++ * Donald Becker ++ * ++ * Old chipset support added by Simon Evans 2002 ++ * - adds support for Belkin F5U011 ++ */ ++ ++/* ++ * 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, see . ++ * ++ * Should you need to contact me, the author, you can do so either by ++ * e-mail - mail your message to , or by paper mail: ++ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#undef DEBUG ++ ++#include ++ ++/* ++ * Version information. ++ */ ++ ++#define DRIVER_VERSION "v2.8" ++#define DRIVER_AUTHOR "Vojtech Pavlik " ++#define DRIVER_DESC "CATC EL1210A NetMate USB Ethernet driver" ++#define SHORT_DRIVER_DESC "EL1210A NetMate USB Ethernet" ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL"); ++ ++static const char driver_name[] = "catc"; ++ ++/* ++ * Some defines. ++ */ ++ ++#define STATS_UPDATE (HZ) /* Time between stats updates */ ++#define TX_TIMEOUT (5*HZ) /* Max time the queue can be stopped */ ++#define PKT_SZ 1536 /* Max Ethernet packet size */ ++#define RX_MAX_BURST 15 /* Max packets per rx buffer (> 0, < 16) */ ++#define TX_MAX_BURST 15 /* Max full sized packets per tx buffer (> 0) */ ++#define CTRL_QUEUE 16 /* Max control requests in flight (power of two) */ ++#define RX_PKT_SZ 1600 /* Max size of receive packet for F5U011 */ ++ ++/* ++ * Control requests. ++ */ ++ ++enum control_requests { ++ ReadMem = 0xf1, ++ GetMac = 0xf2, ++ Reset = 0xf4, ++ SetMac = 0xf5, ++ SetRxMode = 0xf5, /* F5U011 only */ ++ WriteROM = 0xf8, ++ SetReg = 0xfa, ++ GetReg = 0xfb, ++ WriteMem = 0xfc, ++ ReadROM = 0xfd, ++}; ++ ++/* ++ * Registers. ++ */ ++ ++enum register_offsets { ++ TxBufCount = 0x20, ++ RxBufCount = 0x21, ++ OpModes = 0x22, ++ TxQed = 0x23, ++ RxQed = 0x24, ++ MaxBurst = 0x25, ++ RxUnit = 0x60, ++ EthStatus = 0x61, ++ StationAddr0 = 0x67, ++ EthStats = 0x69, ++ LEDCtrl = 0x81, ++}; ++ ++enum eth_stats { ++ TxSingleColl = 0x00, ++ TxMultiColl = 0x02, ++ TxExcessColl = 0x04, ++ RxFramErr = 0x06, ++}; ++ ++enum op_mode_bits { ++ Op3MemWaits = 0x03, ++ OpLenInclude = 0x08, ++ OpRxMerge = 0x10, ++ OpTxMerge = 0x20, ++ OpWin95bugfix = 0x40, ++ OpLoopback = 0x80, ++}; ++ ++enum rx_filter_bits { ++ RxEnable = 0x01, ++ RxPolarity = 0x02, ++ RxForceOK = 0x04, ++ RxMultiCast = 0x08, ++ RxPromisc = 0x10, ++ AltRxPromisc = 0x20, /* F5U011 uses different bit */ ++}; ++ ++enum led_values { ++ LEDFast = 0x01, ++ LEDSlow = 0x02, ++ LEDFlash = 0x03, ++ LEDPulse = 0x04, ++ LEDLink = 0x08, ++}; ++ ++enum link_status { ++ LinkNoChange = 0, ++ LinkGood = 1, ++ LinkBad = 2 ++}; ++ ++/* ++ * The catc struct. ++ */ ++ ++#define CTRL_RUNNING 0 ++#define RX_RUNNING 1 ++#define TX_RUNNING 2 ++ ++struct catc { ++ struct net_device *netdev; ++ struct usb_device *usbdev; ++ ++ unsigned long flags; ++ ++ unsigned int tx_ptr, tx_idx; ++ unsigned int ctrl_head, ctrl_tail; ++ spinlock_t tx_lock, ctrl_lock; ++ ++ u8 tx_buf[2][TX_MAX_BURST * (PKT_SZ + 2)]; ++ u8 rx_buf[RX_MAX_BURST * (PKT_SZ + 2)]; ++ u8 irq_buf[2]; ++ u8 ctrl_buf[64]; ++ struct usb_ctrlrequest ctrl_dr; ++ ++ struct timer_list timer; ++ u8 stats_buf[8]; ++ u16 stats_vals[4]; ++ unsigned long last_stats; ++ ++ u8 multicast[64]; ++ ++ struct ctrl_queue { ++ u8 dir; ++ u8 request; ++ u16 value; ++ u16 index; ++ void *buf; ++ int len; ++ void (*callback)(struct catc *catc, struct ctrl_queue *q); ++ } ctrl_queue[CTRL_QUEUE]; ++ ++ struct urb *tx_urb, *rx_urb, *irq_urb, *ctrl_urb; ++ ++ u8 is_f5u011; /* Set if device is an F5U011 */ ++ u8 rxmode[2]; /* Used for F5U011 */ ++ atomic_t recq_sz; /* Used for F5U011 - counter of waiting rx packets */ ++}; ++ ++/* ++ * Useful macros. ++ */ ++ ++#define catc_get_mac(catc, mac) catc_ctrl_msg(catc, USB_DIR_IN, GetMac, 0, 0, mac, 6) ++#define catc_reset(catc) catc_ctrl_msg(catc, USB_DIR_OUT, Reset, 0, 0, NULL, 0) ++#define catc_set_reg(catc, reg, val) catc_ctrl_msg(catc, USB_DIR_OUT, SetReg, val, reg, NULL, 0) ++#define catc_get_reg(catc, reg, buf) catc_ctrl_msg(catc, USB_DIR_IN, GetReg, 0, reg, buf, 1) ++#define catc_write_mem(catc, addr, buf, size) catc_ctrl_msg(catc, USB_DIR_OUT, WriteMem, 0, addr, buf, size) ++#define catc_read_mem(catc, addr, buf, size) catc_ctrl_msg(catc, USB_DIR_IN, ReadMem, 0, addr, buf, size) ++ ++#define f5u011_rxmode(catc, rxmode) catc_ctrl_msg(catc, USB_DIR_OUT, SetRxMode, 0, 1, rxmode, 2) ++#define f5u011_rxmode_async(catc, rxmode) catc_ctrl_async(catc, USB_DIR_OUT, SetRxMode, 0, 1, &rxmode, 2, NULL) ++#define f5u011_mchash_async(catc, hash) catc_ctrl_async(catc, USB_DIR_OUT, SetRxMode, 0, 2, &hash, 8, NULL) ++ ++#define catc_set_reg_async(catc, reg, val) catc_ctrl_async(catc, USB_DIR_OUT, SetReg, val, reg, NULL, 0, NULL) ++#define catc_get_reg_async(catc, reg, cb) catc_ctrl_async(catc, USB_DIR_IN, GetReg, 0, reg, NULL, 1, cb) ++#define catc_write_mem_async(catc, addr, buf, size) catc_ctrl_async(catc, USB_DIR_OUT, WriteMem, 0, addr, buf, size, NULL) ++ ++/* ++ * Receive routines. ++ */ ++ ++static void catc_rx_done(struct urb *urb) ++{ ++ struct catc *catc = urb->context; ++ u8 *pkt_start = urb->transfer_buffer; ++ struct sk_buff *skb; ++ int pkt_len, pkt_offset = 0; ++ int status = urb->status; ++ ++ if (!catc->is_f5u011) { ++ clear_bit(RX_RUNNING, &catc->flags); ++ pkt_offset = 2; ++ } ++ ++ if (status) { ++ dev_dbg(&urb->dev->dev, "rx_done, status %d, length %d\n", ++ status, urb->actual_length); ++ return; ++ } ++ ++ do { ++ if(!catc->is_f5u011) { ++ pkt_len = le16_to_cpup((__le16*)pkt_start); ++ if (pkt_len > urb->actual_length) { ++ catc->netdev->stats.rx_length_errors++; ++ catc->netdev->stats.rx_errors++; ++ break; ++ } ++ } else { ++ pkt_len = urb->actual_length; ++ } ++ ++ if (!(skb = dev_alloc_skb(pkt_len))) ++ return; ++ ++ skb_copy_to_linear_data(skb, pkt_start + pkt_offset, pkt_len); ++ skb_put(skb, pkt_len); ++ ++ skb->protocol = eth_type_trans(skb, catc->netdev); ++ netif_rx(skb); ++ ++ catc->netdev->stats.rx_packets++; ++ catc->netdev->stats.rx_bytes += pkt_len; ++ ++ /* F5U011 only does one packet per RX */ ++ if (catc->is_f5u011) ++ break; ++ pkt_start += (((pkt_len + 1) >> 6) + 1) << 6; ++ ++ } while (pkt_start - (u8 *) urb->transfer_buffer < urb->actual_length); ++ ++ if (catc->is_f5u011) { ++ if (atomic_read(&catc->recq_sz)) { ++ int state; ++ atomic_dec(&catc->recq_sz); ++ netdev_dbg(catc->netdev, "getting extra packet\n"); ++ urb->dev = catc->usbdev; ++ if ((state = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { ++ netdev_dbg(catc->netdev, ++ "submit(rx_urb) status %d\n", state); ++ } ++ } else { ++ clear_bit(RX_RUNNING, &catc->flags); ++ } ++ } ++} ++ ++static void catc_irq_done(struct urb *urb) ++{ ++ struct catc *catc = urb->context; ++ u8 *data = urb->transfer_buffer; ++ int status = urb->status; ++ unsigned int hasdata = 0, linksts = LinkNoChange; ++ int res; ++ ++ if (!catc->is_f5u011) { ++ hasdata = data[1] & 0x80; ++ if (data[1] & 0x40) ++ linksts = LinkGood; ++ else if (data[1] & 0x20) ++ linksts = LinkBad; ++ } else { ++ hasdata = (unsigned int)(be16_to_cpup((__be16*)data) & 0x0fff); ++ if (data[0] == 0x90) ++ linksts = LinkGood; ++ else if (data[0] == 0xA0) ++ linksts = LinkBad; ++ } ++ ++ switch (status) { ++ case 0: /* success */ ++ break; ++ case -ECONNRESET: /* unlink */ ++ case -ENOENT: ++ case -ESHUTDOWN: ++ return; ++ /* -EPIPE: should clear the halt */ ++ default: /* error */ ++ dev_dbg(&urb->dev->dev, ++ "irq_done, status %d, data %02x %02x.\n", ++ status, data[0], data[1]); ++ goto resubmit; ++ } ++ ++ if (linksts == LinkGood) { ++ netif_carrier_on(catc->netdev); ++ netdev_dbg(catc->netdev, "link ok\n"); ++ } ++ ++ if (linksts == LinkBad) { ++ netif_carrier_off(catc->netdev); ++ netdev_dbg(catc->netdev, "link bad\n"); ++ } ++ ++ if (hasdata) { ++ if (test_and_set_bit(RX_RUNNING, &catc->flags)) { ++ if (catc->is_f5u011) ++ atomic_inc(&catc->recq_sz); ++ } else { ++ catc->rx_urb->dev = catc->usbdev; ++ if ((res = usb_submit_urb(catc->rx_urb, GFP_ATOMIC)) < 0) { ++ dev_err(&catc->usbdev->dev, ++ "submit(rx_urb) status %d\n", res); ++ } ++ } ++ } ++resubmit: ++ res = usb_submit_urb (urb, GFP_ATOMIC); ++ if (res) ++ dev_err(&catc->usbdev->dev, ++ "can't resubmit intr, %s-%s, status %d\n", ++ catc->usbdev->bus->bus_name, ++ catc->usbdev->devpath, res); ++} ++ ++/* ++ * Transmit routines. ++ */ ++ ++static int catc_tx_run(struct catc *catc) ++{ ++ int status; ++ ++ if (catc->is_f5u011) ++ catc->tx_ptr = (catc->tx_ptr + 63) & ~63; ++ ++ catc->tx_urb->transfer_buffer_length = catc->tx_ptr; ++ catc->tx_urb->transfer_buffer = catc->tx_buf[catc->tx_idx]; ++ catc->tx_urb->dev = catc->usbdev; ++ ++ if ((status = usb_submit_urb(catc->tx_urb, GFP_ATOMIC)) < 0) ++ dev_err(&catc->usbdev->dev, "submit(tx_urb), status %d\n", ++ status); ++ ++ catc->tx_idx = !catc->tx_idx; ++ catc->tx_ptr = 0; ++ ++ catc->netdev->trans_start = jiffies; ++ return status; ++} ++ ++static void catc_tx_done(struct urb *urb) ++{ ++ struct catc *catc = urb->context; ++ unsigned long flags; ++ int r, status = urb->status; ++ ++ if (status == -ECONNRESET) { ++ dev_dbg(&urb->dev->dev, "Tx Reset.\n"); ++ urb->status = 0; ++ catc->netdev->trans_start = jiffies; ++ catc->netdev->stats.tx_errors++; ++ clear_bit(TX_RUNNING, &catc->flags); ++ netif_wake_queue(catc->netdev); ++ return; ++ } ++ ++ if (status) { ++ dev_dbg(&urb->dev->dev, "tx_done, status %d, length %d\n", ++ status, urb->actual_length); ++ return; ++ } ++ ++ spin_lock_irqsave(&catc->tx_lock, flags); ++ ++ if (catc->tx_ptr) { ++ r = catc_tx_run(catc); ++ if (unlikely(r < 0)) ++ clear_bit(TX_RUNNING, &catc->flags); ++ } else { ++ clear_bit(TX_RUNNING, &catc->flags); ++ } ++ ++ netif_wake_queue(catc->netdev); ++ ++ spin_unlock_irqrestore(&catc->tx_lock, flags); ++} ++ ++static netdev_tx_t catc_start_xmit(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ struct catc *catc = netdev_priv(netdev); ++ unsigned long flags; ++ int r = 0; ++ char *tx_buf; ++ ++ spin_lock_irqsave(&catc->tx_lock, flags); ++ ++ catc->tx_ptr = (((catc->tx_ptr - 1) >> 6) + 1) << 6; ++ tx_buf = catc->tx_buf[catc->tx_idx] + catc->tx_ptr; ++ if (catc->is_f5u011) ++ *(__be16 *)tx_buf = cpu_to_be16(skb->len); ++ else ++ *(__le16 *)tx_buf = cpu_to_le16(skb->len); ++ skb_copy_from_linear_data(skb, tx_buf + 2, skb->len); ++ catc->tx_ptr += skb->len + 2; ++ ++ if (!test_and_set_bit(TX_RUNNING, &catc->flags)) { ++ r = catc_tx_run(catc); ++ if (r < 0) ++ clear_bit(TX_RUNNING, &catc->flags); ++ } ++ ++ if ((catc->is_f5u011 && catc->tx_ptr) || ++ (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2)))) ++ netif_stop_queue(netdev); ++ ++ spin_unlock_irqrestore(&catc->tx_lock, flags); ++ ++ if (r >= 0) { ++ catc->netdev->stats.tx_bytes += skb->len; ++ catc->netdev->stats.tx_packets++; ++ } ++ ++ dev_kfree_skb(skb); ++ ++ return NETDEV_TX_OK; ++} ++ ++static void catc_tx_timeout(struct net_device *netdev) ++{ ++ struct catc *catc = netdev_priv(netdev); ++ ++ dev_warn(&netdev->dev, "Transmit timed out.\n"); ++ usb_unlink_urb(catc->tx_urb); ++} ++ ++/* ++ * Control messages. ++ */ ++ ++static int catc_ctrl_msg(struct catc *catc, u8 dir, u8 request, u16 value, u16 index, void *buf, int len) ++{ ++ int retval = usb_control_msg(catc->usbdev, ++ dir ? usb_rcvctrlpipe(catc->usbdev, 0) : usb_sndctrlpipe(catc->usbdev, 0), ++ request, 0x40 | dir, value, index, buf, len, 1000); ++ return retval < 0 ? retval : 0; ++} ++ ++static void catc_ctrl_run(struct catc *catc) ++{ ++ struct ctrl_queue *q = catc->ctrl_queue + catc->ctrl_tail; ++ struct usb_device *usbdev = catc->usbdev; ++ struct urb *urb = catc->ctrl_urb; ++ struct usb_ctrlrequest *dr = &catc->ctrl_dr; ++ int status; ++ ++ dr->bRequest = q->request; ++ dr->bRequestType = 0x40 | q->dir; ++ dr->wValue = cpu_to_le16(q->value); ++ dr->wIndex = cpu_to_le16(q->index); ++ dr->wLength = cpu_to_le16(q->len); ++ ++ urb->pipe = q->dir ? usb_rcvctrlpipe(usbdev, 0) : usb_sndctrlpipe(usbdev, 0); ++ urb->transfer_buffer_length = q->len; ++ urb->transfer_buffer = catc->ctrl_buf; ++ urb->setup_packet = (void *) dr; ++ urb->dev = usbdev; ++ ++ if (!q->dir && q->buf && q->len) ++ memcpy(catc->ctrl_buf, q->buf, q->len); ++ ++ if ((status = usb_submit_urb(catc->ctrl_urb, GFP_ATOMIC))) ++ dev_err(&catc->usbdev->dev, "submit(ctrl_urb) status %d\n", ++ status); ++} ++ ++static void catc_ctrl_done(struct urb *urb) ++{ ++ struct catc *catc = urb->context; ++ struct ctrl_queue *q; ++ unsigned long flags; ++ int status = urb->status; ++ ++ if (status) ++ dev_dbg(&urb->dev->dev, "ctrl_done, status %d, len %d.\n", ++ status, urb->actual_length); ++ ++ spin_lock_irqsave(&catc->ctrl_lock, flags); ++ ++ q = catc->ctrl_queue + catc->ctrl_tail; ++ ++ if (q->dir) { ++ if (q->buf && q->len) ++ memcpy(q->buf, catc->ctrl_buf, q->len); ++ else ++ q->buf = catc->ctrl_buf; ++ } ++ ++ if (q->callback) ++ q->callback(catc, q); ++ ++ catc->ctrl_tail = (catc->ctrl_tail + 1) & (CTRL_QUEUE - 1); ++ ++ if (catc->ctrl_head != catc->ctrl_tail) ++ catc_ctrl_run(catc); ++ else ++ clear_bit(CTRL_RUNNING, &catc->flags); ++ ++ spin_unlock_irqrestore(&catc->ctrl_lock, flags); ++} ++ ++static int catc_ctrl_async(struct catc *catc, u8 dir, u8 request, u16 value, ++ u16 index, void *buf, int len, void (*callback)(struct catc *catc, struct ctrl_queue *q)) ++{ ++ struct ctrl_queue *q; ++ int retval = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&catc->ctrl_lock, flags); ++ ++ q = catc->ctrl_queue + catc->ctrl_head; ++ ++ q->dir = dir; ++ q->request = request; ++ q->value = value; ++ q->index = index; ++ q->buf = buf; ++ q->len = len; ++ q->callback = callback; ++ ++ catc->ctrl_head = (catc->ctrl_head + 1) & (CTRL_QUEUE - 1); ++ ++ if (catc->ctrl_head == catc->ctrl_tail) { ++ dev_err(&catc->usbdev->dev, "ctrl queue full\n"); ++ catc->ctrl_tail = (catc->ctrl_tail + 1) & (CTRL_QUEUE - 1); ++ retval = -1; ++ } ++ ++ if (!test_and_set_bit(CTRL_RUNNING, &catc->flags)) ++ catc_ctrl_run(catc); ++ ++ spin_unlock_irqrestore(&catc->ctrl_lock, flags); ++ ++ return retval; ++} ++ ++/* ++ * Statistics. ++ */ ++ ++static void catc_stats_done(struct catc *catc, struct ctrl_queue *q) ++{ ++ int index = q->index - EthStats; ++ u16 data, last; ++ ++ catc->stats_buf[index] = *((char *)q->buf); ++ ++ if (index & 1) ++ return; ++ ++ data = ((u16)catc->stats_buf[index] << 8) | catc->stats_buf[index + 1]; ++ last = catc->stats_vals[index >> 1]; ++ ++ switch (index) { ++ case TxSingleColl: ++ case TxMultiColl: ++ catc->netdev->stats.collisions += data - last; ++ break; ++ case TxExcessColl: ++ catc->netdev->stats.tx_aborted_errors += data - last; ++ catc->netdev->stats.tx_errors += data - last; ++ break; ++ case RxFramErr: ++ catc->netdev->stats.rx_frame_errors += data - last; ++ catc->netdev->stats.rx_errors += data - last; ++ break; ++ } ++ ++ catc->stats_vals[index >> 1] = data; ++} ++ ++static void catc_stats_timer(unsigned long data) ++{ ++ struct catc *catc = (void *) data; ++ int i; ++ ++ for (i = 0; i < 8; i++) ++ catc_get_reg_async(catc, EthStats + 7 - i, catc_stats_done); ++ ++ mod_timer(&catc->timer, jiffies + STATS_UPDATE); ++} ++ ++/* ++ * Receive modes. Broadcast, Multicast, Promisc. ++ */ ++ ++static void catc_multicast(unsigned char *addr, u8 *multicast) ++{ ++ u32 crc; ++ ++ crc = ether_crc_le(6, addr); ++ multicast[(crc >> 3) & 0x3f] |= 1 << (crc & 7); ++} ++ ++static void catc_set_multicast_list(struct net_device *netdev) ++{ ++ struct catc *catc = netdev_priv(netdev); ++ struct netdev_hw_addr *ha; ++ u8 broadcast[ETH_ALEN]; ++ u8 rx = RxEnable | RxPolarity | RxMultiCast; ++ ++ memset(broadcast, 0xff, ETH_ALEN); ++ memset(catc->multicast, 0, 64); ++ ++ catc_multicast(broadcast, catc->multicast); ++ catc_multicast(netdev->dev_addr, catc->multicast); ++ ++ if (netdev->flags & IFF_PROMISC) { ++ memset(catc->multicast, 0xff, 64); ++ rx |= (!catc->is_f5u011) ? RxPromisc : AltRxPromisc; ++ } ++ ++ if (netdev->flags & IFF_ALLMULTI) { ++ memset(catc->multicast, 0xff, 64); ++ } else { ++ netdev_for_each_mc_addr(ha, netdev) { ++ u32 crc = ether_crc_le(6, ha->addr); ++ if (!catc->is_f5u011) { ++ catc->multicast[(crc >> 3) & 0x3f] |= 1 << (crc & 7); ++ } else { ++ catc->multicast[7-(crc >> 29)] |= 1 << ((crc >> 26) & 7); ++ } ++ } ++ } ++ if (!catc->is_f5u011) { ++ catc_set_reg_async(catc, RxUnit, rx); ++ catc_write_mem_async(catc, 0xfa80, catc->multicast, 64); ++ } else { ++ f5u011_mchash_async(catc, catc->multicast); ++ if (catc->rxmode[0] != rx) { ++ catc->rxmode[0] = rx; ++ netdev_dbg(catc->netdev, ++ "Setting RX mode to %2.2X %2.2X\n", ++ catc->rxmode[0], catc->rxmode[1]); ++ f5u011_rxmode_async(catc, catc->rxmode); ++ } ++ } ++} ++ ++static void catc_get_drvinfo(struct net_device *dev, ++ struct ethtool_drvinfo *info) ++{ ++ struct catc *catc = netdev_priv(dev); ++ strlcpy(info->driver, driver_name, sizeof(info->driver)); ++ strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); ++ usb_make_path(catc->usbdev, info->bus_info, sizeof(info->bus_info)); ++} ++ ++static int catc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) ++{ ++ struct catc *catc = netdev_priv(dev); ++ if (!catc->is_f5u011) ++ return -EOPNOTSUPP; ++ ++ cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_TP; ++ cmd->advertising = ADVERTISED_10baseT_Half | ADVERTISED_TP; ++ ethtool_cmd_speed_set(cmd, SPEED_10); ++ cmd->duplex = DUPLEX_HALF; ++ cmd->port = PORT_TP; ++ cmd->phy_address = 0; ++ cmd->transceiver = XCVR_INTERNAL; ++ cmd->autoneg = AUTONEG_DISABLE; ++ cmd->maxtxpkt = 1; ++ cmd->maxrxpkt = 1; ++ return 0; ++} ++ ++static const struct ethtool_ops ops = { ++ .get_drvinfo = catc_get_drvinfo, ++ .get_settings = catc_get_settings, ++ .get_link = ethtool_op_get_link ++}; ++ ++/* ++ * Open, close. ++ */ ++ ++static int catc_open(struct net_device *netdev) ++{ ++ struct catc *catc = netdev_priv(netdev); ++ int status; ++ ++ catc->irq_urb->dev = catc->usbdev; ++ if ((status = usb_submit_urb(catc->irq_urb, GFP_KERNEL)) < 0) { ++ dev_err(&catc->usbdev->dev, "submit(irq_urb) status %d\n", ++ status); ++ return -1; ++ } ++ ++ netif_start_queue(netdev); ++ ++ if (!catc->is_f5u011) ++ mod_timer(&catc->timer, jiffies + STATS_UPDATE); ++ ++ return 0; ++} ++ ++static int catc_stop(struct net_device *netdev) ++{ ++ struct catc *catc = netdev_priv(netdev); ++ ++ netif_stop_queue(netdev); ++ ++ if (!catc->is_f5u011) ++ del_timer_sync(&catc->timer); ++ ++ usb_kill_urb(catc->rx_urb); ++ usb_kill_urb(catc->tx_urb); ++ usb_kill_urb(catc->irq_urb); ++ usb_kill_urb(catc->ctrl_urb); ++ ++ return 0; ++} ++ ++static const struct net_device_ops catc_netdev_ops = { ++ .ndo_open = catc_open, ++ .ndo_stop = catc_stop, ++ .ndo_start_xmit = catc_start_xmit, ++ ++ .ndo_tx_timeout = catc_tx_timeout, ++ .ndo_set_rx_mode = catc_set_multicast_list, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++}; ++ ++/* ++ * USB probe, disconnect. ++ */ ++ ++static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id) ++{ ++ struct device *dev = &intf->dev; ++ struct usb_device *usbdev = interface_to_usbdev(intf); ++ struct net_device *netdev; ++ struct catc *catc; ++ u8 broadcast[ETH_ALEN]; ++ int i, pktsz; ++ ++ if (usb_set_interface(usbdev, ++ intf->altsetting->desc.bInterfaceNumber, 1)) { ++ dev_err(dev, "Can't set altsetting 1.\n"); ++ return -EIO; ++ } ++ ++ netdev = alloc_etherdev(sizeof(struct catc)); ++ if (!netdev) ++ return -ENOMEM; ++ ++ catc = netdev_priv(netdev); ++ ++ netdev->netdev_ops = &catc_netdev_ops; ++ netdev->watchdog_timeo = TX_TIMEOUT; ++ netdev->ethtool_ops = &ops; ++ ++ catc->usbdev = usbdev; ++ catc->netdev = netdev; ++ ++ spin_lock_init(&catc->tx_lock); ++ spin_lock_init(&catc->ctrl_lock); ++ ++ init_timer(&catc->timer); ++ catc->timer.data = (long) catc; ++ catc->timer.function = catc_stats_timer; ++ ++ catc->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); ++ catc->tx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ catc->rx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ catc->irq_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if ((!catc->ctrl_urb) || (!catc->tx_urb) || ++ (!catc->rx_urb) || (!catc->irq_urb)) { ++ dev_err(&intf->dev, "No free urbs available.\n"); ++ usb_free_urb(catc->ctrl_urb); ++ usb_free_urb(catc->tx_urb); ++ usb_free_urb(catc->rx_urb); ++ usb_free_urb(catc->irq_urb); ++ free_netdev(netdev); ++ return -ENOMEM; ++ } ++ ++ /* The F5U011 has the same vendor/product as the netmate but a device version of 0x130 */ ++ if (le16_to_cpu(usbdev->descriptor.idVendor) == 0x0423 && ++ le16_to_cpu(usbdev->descriptor.idProduct) == 0xa && ++ le16_to_cpu(catc->usbdev->descriptor.bcdDevice) == 0x0130) { ++ dev_dbg(dev, "Testing for f5u011\n"); ++ catc->is_f5u011 = 1; ++ atomic_set(&catc->recq_sz, 0); ++ pktsz = RX_PKT_SZ; ++ } else { ++ pktsz = RX_MAX_BURST * (PKT_SZ + 2); ++ } ++ ++ usb_fill_control_urb(catc->ctrl_urb, usbdev, usb_sndctrlpipe(usbdev, 0), ++ NULL, NULL, 0, catc_ctrl_done, catc); ++ ++ usb_fill_bulk_urb(catc->tx_urb, usbdev, usb_sndbulkpipe(usbdev, 1), ++ NULL, 0, catc_tx_done, catc); ++ ++ usb_fill_bulk_urb(catc->rx_urb, usbdev, usb_rcvbulkpipe(usbdev, 1), ++ catc->rx_buf, pktsz, catc_rx_done, catc); ++ ++ usb_fill_int_urb(catc->irq_urb, usbdev, usb_rcvintpipe(usbdev, 2), ++ catc->irq_buf, 2, catc_irq_done, catc, 1); ++ ++ if (!catc->is_f5u011) { ++ dev_dbg(dev, "Checking memory size\n"); ++ ++ i = 0x12345678; ++ catc_write_mem(catc, 0x7a80, &i, 4); ++ i = 0x87654321; ++ catc_write_mem(catc, 0xfa80, &i, 4); ++ catc_read_mem(catc, 0x7a80, &i, 4); ++ ++ switch (i) { ++ case 0x12345678: ++ catc_set_reg(catc, TxBufCount, 8); ++ catc_set_reg(catc, RxBufCount, 32); ++ dev_dbg(dev, "64k Memory\n"); ++ break; ++ default: ++ dev_warn(&intf->dev, ++ "Couldn't detect memory size, assuming 32k\n"); ++ case 0x87654321: ++ catc_set_reg(catc, TxBufCount, 4); ++ catc_set_reg(catc, RxBufCount, 16); ++ dev_dbg(dev, "32k Memory\n"); ++ break; ++ } ++ ++ dev_dbg(dev, "Getting MAC from SEEROM.\n"); ++ ++ catc_get_mac(catc, netdev->dev_addr); ++ ++ dev_dbg(dev, "Setting MAC into registers.\n"); ++ ++ for (i = 0; i < 6; i++) ++ catc_set_reg(catc, StationAddr0 - i, netdev->dev_addr[i]); ++ ++ dev_dbg(dev, "Filling the multicast list.\n"); ++ ++ memset(broadcast, 0xff, ETH_ALEN); ++ catc_multicast(broadcast, catc->multicast); ++ catc_multicast(netdev->dev_addr, catc->multicast); ++ catc_write_mem(catc, 0xfa80, catc->multicast, 64); ++ ++ dev_dbg(dev, "Clearing error counters.\n"); ++ ++ for (i = 0; i < 8; i++) ++ catc_set_reg(catc, EthStats + i, 0); ++ catc->last_stats = jiffies; ++ ++ dev_dbg(dev, "Enabling.\n"); ++ ++ catc_set_reg(catc, MaxBurst, RX_MAX_BURST); ++ catc_set_reg(catc, OpModes, OpTxMerge | OpRxMerge | OpLenInclude | Op3MemWaits); ++ catc_set_reg(catc, LEDCtrl, LEDLink); ++ catc_set_reg(catc, RxUnit, RxEnable | RxPolarity | RxMultiCast); ++ } else { ++ dev_dbg(dev, "Performing reset\n"); ++ catc_reset(catc); ++ catc_get_mac(catc, netdev->dev_addr); ++ ++ dev_dbg(dev, "Setting RX Mode\n"); ++ catc->rxmode[0] = RxEnable | RxPolarity | RxMultiCast; ++ catc->rxmode[1] = 0; ++ f5u011_rxmode(catc, catc->rxmode); ++ } ++ dev_dbg(dev, "Init done.\n"); ++ printk(KERN_INFO "%s: %s USB Ethernet at usb-%s-%s, %pM.\n", ++ netdev->name, (catc->is_f5u011) ? "Belkin F5U011" : "CATC EL1210A NetMate", ++ usbdev->bus->bus_name, usbdev->devpath, netdev->dev_addr); ++ usb_set_intfdata(intf, catc); ++ ++ SET_NETDEV_DEV(netdev, &intf->dev); ++ if (register_netdev(netdev) != 0) { ++ usb_set_intfdata(intf, NULL); ++ usb_free_urb(catc->ctrl_urb); ++ usb_free_urb(catc->tx_urb); ++ usb_free_urb(catc->rx_urb); ++ usb_free_urb(catc->irq_urb); ++ free_netdev(netdev); ++ return -EIO; ++ } ++ return 0; ++} ++ ++static void catc_disconnect(struct usb_interface *intf) ++{ ++ struct catc *catc = usb_get_intfdata(intf); ++ ++ usb_set_intfdata(intf, NULL); ++ if (catc) { ++ unregister_netdev(catc->netdev); ++ usb_free_urb(catc->ctrl_urb); ++ usb_free_urb(catc->tx_urb); ++ usb_free_urb(catc->rx_urb); ++ usb_free_urb(catc->irq_urb); ++ free_netdev(catc->netdev); ++ } ++} ++ ++/* ++ * Module functions and tables. ++ */ ++ ++static struct usb_device_id catc_id_table [] = { ++ { USB_DEVICE(0x0423, 0xa) }, /* CATC Netmate, Belkin F5U011 */ ++ { USB_DEVICE(0x0423, 0xc) }, /* CATC Netmate II, Belkin F5U111 */ ++ { USB_DEVICE(0x08d1, 0x1) }, /* smartBridges smartNIC */ ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(usb, catc_id_table); ++ ++static struct usb_driver catc_driver = { ++ .name = driver_name, ++ .probe = catc_probe, ++ .disconnect = catc_disconnect, ++ .id_table = catc_id_table, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(catc_driver); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/cdc_eem.c backports-3.18.1-1/drivers/net/usb/cdc_eem.c +--- backports-3.18.1-1.org/drivers/net/usb/cdc_eem.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/cdc_eem.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,381 @@ ++/* ++ * USB CDC EEM network interface driver ++ * Copyright (C) 2009 Oberthur Technologies ++ * by Omar Laazimani, Olivier Condemine ++ * ++ * 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, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * This driver is an implementation of the CDC "Ethernet Emulation ++ * Model" (EEM) specification, which encapsulates Ethernet frames ++ * for transport over USB using a simpler USB device model than the ++ * previous CDC "Ethernet Control Model" (ECM, or "CDC Ethernet"). ++ * ++ * For details, see www.usb.org/developers/devclass_docs/CDC_EEM10.pdf ++ * ++ * This version has been tested with GIGAntIC WuaoW SIM Smart Card on 2.6.24, ++ * 2.6.27 and 2.6.30rc2 kernel. ++ * It has also been validated on Openmoko Om 2008.12 (based on 2.6.24 kernel). ++ * build on 23-April-2009 ++ */ ++ ++#define EEM_HEAD 2 /* 2 byte header */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void eem_linkcmd_complete(struct urb *urb) ++{ ++ dev_kfree_skb(urb->context); ++ usb_free_urb(urb); ++} ++ ++static void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb) ++{ ++ struct urb *urb; ++ int status; ++ ++ urb = usb_alloc_urb(0, GFP_ATOMIC); ++ if (!urb) ++ goto fail; ++ ++ usb_fill_bulk_urb(urb, dev->udev, dev->out, ++ skb->data, skb->len, eem_linkcmd_complete, skb); ++ ++ status = usb_submit_urb(urb, GFP_ATOMIC); ++ if (status) { ++ usb_free_urb(urb); ++fail: ++ dev_kfree_skb(skb); ++ netdev_warn(dev->net, "link cmd failure\n"); ++ return; ++ } ++} ++ ++static int eem_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int status = 0; ++ ++ status = usbnet_get_endpoints(dev, intf); ++ if (status < 0) { ++ usb_set_intfdata(intf, NULL); ++ usb_driver_release_interface(driver_of(intf), intf); ++ return status; ++ } ++ ++ /* no jumbogram (16K) support for now */ ++ ++ dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN + VLAN_HLEN; ++ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; ++ ++ return 0; ++} ++ ++/* ++ * EEM permits packing multiple Ethernet frames into USB transfers ++ * (a "bundle"), but for TX we don't try to do that. ++ */ ++static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb, ++ gfp_t flags) ++{ ++ struct sk_buff *skb2 = NULL; ++ u16 len = skb->len; ++ u32 crc = 0; ++ int padlen = 0; ++ ++ /* When ((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket) is ++ * zero, stick two bytes of zero length EEM packet on the end. ++ * Else the framework would add invalid single byte padding, ++ * since it can't know whether ZLPs will be handled right by ++ * all the relevant hardware and software. ++ */ ++ if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket)) ++ padlen += 2; ++ ++ if (!skb_cloned(skb)) { ++ int headroom = skb_headroom(skb); ++ int tailroom = skb_tailroom(skb); ++ ++ if ((tailroom >= ETH_FCS_LEN + padlen) && ++ (headroom >= EEM_HEAD)) ++ goto done; ++ ++ if ((headroom + tailroom) ++ > (EEM_HEAD + ETH_FCS_LEN + padlen)) { ++ skb->data = memmove(skb->head + ++ EEM_HEAD, ++ skb->data, ++ skb->len); ++ skb_set_tail_pointer(skb, len); ++ goto done; ++ } ++ } ++ ++ skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags); ++ if (!skb2) ++ return NULL; ++ ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ ++done: ++ /* we don't use the "no Ethernet CRC" option */ ++ crc = crc32_le(~0, skb->data, skb->len); ++ crc = ~crc; ++ ++ put_unaligned_le32(crc, skb_put(skb, 4)); ++ ++ /* EEM packet header format: ++ * b0..13: length of ethernet frame ++ * b14: bmCRC (1 == valid Ethernet CRC) ++ * b15: bmType (0 == data) ++ */ ++ len = skb->len; ++ put_unaligned_le16(BIT(14) | len, skb_push(skb, 2)); ++ ++ /* Bundle a zero length EEM packet if needed */ ++ if (padlen) ++ put_unaligned_le16(0, skb_put(skb, 2)); ++ ++ return skb; ++} ++ ++static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ /* ++ * Our task here is to strip off framing, leaving skb with one ++ * data frame for the usbnet framework code to process. But we ++ * may have received multiple EEM payloads, or command payloads. ++ * So we must process _everything_ as if it's a header, except ++ * maybe the last data payload ++ * ++ * REVISIT the framework needs updating so that when we consume ++ * all payloads (the last or only message was a command, or a ++ * zero length EEM packet) that is not accounted as an rx_error. ++ */ ++ do { ++ struct sk_buff *skb2 = NULL; ++ u16 header; ++ u16 len = 0; ++ ++ /* incomplete EEM header? */ ++ if (skb->len < EEM_HEAD) ++ return 0; ++ ++ /* ++ * EEM packet header format: ++ * b0..14: EEM type dependent (Data or Command) ++ * b15: bmType ++ */ ++ header = get_unaligned_le16(skb->data); ++ skb_pull(skb, EEM_HEAD); ++ ++ /* ++ * The bmType bit helps to denote when EEM ++ * packet is data or command : ++ * bmType = 0 : EEM data payload ++ * bmType = 1 : EEM (link) command ++ */ ++ if (header & BIT(15)) { ++ u16 bmEEMCmd; ++ ++ /* ++ * EEM (link) command packet: ++ * b0..10: bmEEMCmdParam ++ * b11..13: bmEEMCmd ++ * b14: bmReserved (must be 0) ++ * b15: 1 (EEM command) ++ */ ++ if (header & BIT(14)) { ++ netdev_dbg(dev->net, "reserved command %04x\n", ++ header); ++ continue; ++ } ++ ++ bmEEMCmd = (header >> 11) & 0x7; ++ switch (bmEEMCmd) { ++ ++ /* Responding to echo requests is mandatory. */ ++ case 0: /* Echo command */ ++ len = header & 0x7FF; ++ ++ /* bogus command? */ ++ if (skb->len < len) ++ return 0; ++ ++ skb2 = skb_clone(skb, GFP_ATOMIC); ++ if (unlikely(!skb2)) ++ goto next; ++ skb_trim(skb2, len); ++ put_unaligned_le16(BIT(15) | (1 << 11) | len, ++ skb_push(skb2, 2)); ++ eem_linkcmd(dev, skb2); ++ break; ++ ++ /* ++ * Host may choose to ignore hints. ++ * - suspend: peripheral ready to suspend ++ * - response: suggest N millisec polling ++ * - response complete: suggest N sec polling ++ * ++ * Suspend is reported and maybe heeded. ++ */ ++ case 2: /* Suspend hint */ ++ usbnet_device_suggests_idle(dev); ++ continue; ++ case 3: /* Response hint */ ++ case 4: /* Response complete hint */ ++ continue; ++ ++ /* ++ * Hosts should never receive host-to-peripheral ++ * or reserved command codes; or responses to an ++ * echo command we didn't send. ++ */ ++ case 1: /* Echo response */ ++ case 5: /* Tickle */ ++ default: /* reserved */ ++ netdev_warn(dev->net, ++ "unexpected link command %d\n", ++ bmEEMCmd); ++ continue; ++ } ++ ++ } else { ++ u32 crc, crc2; ++ int is_last; ++ ++ /* zero length EEM packet? */ ++ if (header == 0) ++ continue; ++ ++ /* ++ * EEM data packet header : ++ * b0..13: length of ethernet frame ++ * b14: bmCRC ++ * b15: 0 (EEM data) ++ */ ++ len = header & 0x3FFF; ++ ++ /* bogus EEM payload? */ ++ if (skb->len < len) ++ return 0; ++ ++ /* bogus ethernet frame? */ ++ if (len < (ETH_HLEN + ETH_FCS_LEN)) ++ goto next; ++ ++ /* ++ * Treat the last payload differently: framework ++ * code expects our "fixup" to have stripped off ++ * headers, so "skb" is a data packet (or error). ++ * Else if it's not the last payload, keep "skb" ++ * for further processing. ++ */ ++ is_last = (len == skb->len); ++ if (is_last) ++ skb2 = skb; ++ else { ++ skb2 = skb_clone(skb, GFP_ATOMIC); ++ if (unlikely(!skb2)) ++ return 0; ++ } ++ ++ /* ++ * The bmCRC helps to denote when the CRC field in ++ * the Ethernet frame contains a calculated CRC: ++ * bmCRC = 1 : CRC is calculated ++ * bmCRC = 0 : CRC = 0xDEADBEEF ++ */ ++ if (header & BIT(14)) { ++ crc = get_unaligned_le32(skb2->data ++ + len - ETH_FCS_LEN); ++ crc2 = ~crc32_le(~0, skb2->data, skb2->len ++ - ETH_FCS_LEN); ++ } else { ++ crc = get_unaligned_be32(skb2->data ++ + len - ETH_FCS_LEN); ++ crc2 = 0xdeadbeef; ++ } ++ skb_trim(skb2, len - ETH_FCS_LEN); ++ ++ if (is_last) ++ return crc == crc2; ++ ++ if (unlikely(crc != crc2)) { ++ dev->net->stats.rx_errors++; ++ dev_kfree_skb_any(skb2); ++ } else ++ usbnet_skb_return(dev, skb2); ++ } ++ ++next: ++ skb_pull(skb, len); ++ } while (skb->len); ++ ++ return 1; ++} ++ ++static const struct driver_info eem_info = { ++ .description = "CDC EEM Device", ++ .flags = FLAG_ETHER | FLAG_POINTTOPOINT, ++ .bind = eem_bind, ++ .rx_fixup = eem_rx_fixup, ++ .tx_fixup = eem_tx_fixup, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static const struct usb_device_id products[] = { ++{ ++ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM, ++ USB_CDC_PROTO_EEM), ++ .driver_info = (unsigned long) &eem_info, ++}, ++{ ++ /* EMPTY == end of list */ ++}, ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver eem_driver = { ++ .name = "cdc_eem", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(eem_driver); ++ ++MODULE_AUTHOR("Omar Laazimani "); ++MODULE_DESCRIPTION("USB CDC EEM"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/cdc-phonet.c backports-3.18.1-1/drivers/net/usb/cdc-phonet.c +--- backports-3.18.1-1.org/drivers/net/usb/cdc-phonet.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/cdc-phonet.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,466 @@ ++/* ++ * phonet.c -- USB CDC Phonet host driver ++ * ++ * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved. ++ * ++ * Author: Rémi Denis-Courmont ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * 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., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PN_MEDIA_USB 0x1B ++ ++static const unsigned rxq_size = 17; ++ ++struct usbpn_dev { ++ struct net_device *dev; ++ ++ struct usb_interface *intf, *data_intf; ++ struct usb_device *usb; ++ unsigned int tx_pipe, rx_pipe; ++ u8 active_setting; ++ u8 disconnected; ++ ++ unsigned tx_queue; ++ spinlock_t tx_lock; ++ ++ spinlock_t rx_lock; ++ struct sk_buff *rx_skb; ++ struct urb *urbs[0]; ++}; ++ ++static void tx_complete(struct urb *req); ++static void rx_complete(struct urb *req); ++ ++/* ++ * Network device callbacks ++ */ ++static netdev_tx_t usbpn_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct usbpn_dev *pnd = netdev_priv(dev); ++ struct urb *req = NULL; ++ unsigned long flags; ++ int err; ++ ++ if (skb->protocol != htons(ETH_P_PHONET)) ++ goto drop; ++ ++ req = usb_alloc_urb(0, GFP_ATOMIC); ++ if (!req) ++ goto drop; ++ usb_fill_bulk_urb(req, pnd->usb, pnd->tx_pipe, skb->data, skb->len, ++ tx_complete, skb); ++ req->transfer_flags = URB_ZERO_PACKET; ++ err = usb_submit_urb(req, GFP_ATOMIC); ++ if (err) { ++ usb_free_urb(req); ++ goto drop; ++ } ++ ++ spin_lock_irqsave(&pnd->tx_lock, flags); ++ pnd->tx_queue++; ++ if (pnd->tx_queue >= dev->tx_queue_len) ++ netif_stop_queue(dev); ++ spin_unlock_irqrestore(&pnd->tx_lock, flags); ++ return NETDEV_TX_OK; ++ ++drop: ++ dev_kfree_skb(skb); ++ dev->stats.tx_dropped++; ++ return NETDEV_TX_OK; ++} ++ ++static void tx_complete(struct urb *req) ++{ ++ struct sk_buff *skb = req->context; ++ struct net_device *dev = skb->dev; ++ struct usbpn_dev *pnd = netdev_priv(dev); ++ int status = req->status; ++ ++ switch (status) { ++ case 0: ++ dev->stats.tx_bytes += skb->len; ++ break; ++ ++ case -ENOENT: ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ dev->stats.tx_aborted_errors++; ++ default: ++ dev->stats.tx_errors++; ++ dev_dbg(&dev->dev, "TX error (%d)\n", status); ++ } ++ dev->stats.tx_packets++; ++ ++ spin_lock(&pnd->tx_lock); ++ pnd->tx_queue--; ++ netif_wake_queue(dev); ++ spin_unlock(&pnd->tx_lock); ++ ++ dev_kfree_skb_any(skb); ++ usb_free_urb(req); ++} ++ ++static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags) ++{ ++ struct net_device *dev = pnd->dev; ++ struct page *page; ++ int err; ++ ++ page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL); ++ if (!page) ++ return -ENOMEM; ++ ++ usb_fill_bulk_urb(req, pnd->usb, pnd->rx_pipe, page_address(page), ++ PAGE_SIZE, rx_complete, dev); ++ req->transfer_flags = 0; ++ err = usb_submit_urb(req, gfp_flags); ++ if (unlikely(err)) { ++ dev_dbg(&dev->dev, "RX submit error (%d)\n", err); ++ put_page(page); ++ } ++ return err; ++} ++ ++static void rx_complete(struct urb *req) ++{ ++ struct net_device *dev = req->context; ++ struct usbpn_dev *pnd = netdev_priv(dev); ++ struct page *page = virt_to_page(req->transfer_buffer); ++ struct sk_buff *skb; ++ unsigned long flags; ++ int status = req->status; ++ ++ switch (status) { ++ case 0: ++ spin_lock_irqsave(&pnd->rx_lock, flags); ++ skb = pnd->rx_skb; ++ if (!skb) { ++ skb = pnd->rx_skb = netdev_alloc_skb(dev, 12); ++ if (likely(skb)) { ++ /* Can't use pskb_pull() on page in IRQ */ ++ memcpy(skb_put(skb, 1), page_address(page), 1); ++ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, ++ page, 1, req->actual_length, ++ PAGE_SIZE); ++ page = NULL; ++ } ++ } else { ++ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, ++ page, 0, req->actual_length, ++ PAGE_SIZE); ++ page = NULL; ++ } ++ if (req->actual_length < PAGE_SIZE) ++ pnd->rx_skb = NULL; /* Last fragment */ ++ else ++ skb = NULL; ++ spin_unlock_irqrestore(&pnd->rx_lock, flags); ++ if (skb) { ++ skb->protocol = htons(ETH_P_PHONET); ++ skb_reset_mac_header(skb); ++ __skb_pull(skb, 1); ++ skb->dev = dev; ++ dev->stats.rx_packets++; ++ dev->stats.rx_bytes += skb->len; ++ ++ netif_rx(skb); ++ } ++ goto resubmit; ++ ++ case -ENOENT: ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ req = NULL; ++ break; ++ ++ case -EOVERFLOW: ++ dev->stats.rx_over_errors++; ++ dev_dbg(&dev->dev, "RX overflow\n"); ++ break; ++ ++ case -EILSEQ: ++ dev->stats.rx_crc_errors++; ++ break; ++ } ++ ++ dev->stats.rx_errors++; ++resubmit: ++ if (page) ++ put_page(page); ++ if (req) ++ rx_submit(pnd, req, GFP_ATOMIC | __GFP_COLD); ++} ++ ++static int usbpn_close(struct net_device *dev); ++ ++static int usbpn_open(struct net_device *dev) ++{ ++ struct usbpn_dev *pnd = netdev_priv(dev); ++ int err; ++ unsigned i; ++ unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; ++ ++ err = usb_set_interface(pnd->usb, num, pnd->active_setting); ++ if (err) ++ return err; ++ ++ for (i = 0; i < rxq_size; i++) { ++ struct urb *req = usb_alloc_urb(0, GFP_KERNEL); ++ ++ if (!req || rx_submit(pnd, req, GFP_KERNEL | __GFP_COLD)) { ++ usb_free_urb(req); ++ usbpn_close(dev); ++ return -ENOMEM; ++ } ++ pnd->urbs[i] = req; ++ } ++ ++ netif_wake_queue(dev); ++ return 0; ++} ++ ++static int usbpn_close(struct net_device *dev) ++{ ++ struct usbpn_dev *pnd = netdev_priv(dev); ++ unsigned i; ++ unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; ++ ++ netif_stop_queue(dev); ++ ++ for (i = 0; i < rxq_size; i++) { ++ struct urb *req = pnd->urbs[i]; ++ ++ if (!req) ++ continue; ++ usb_kill_urb(req); ++ usb_free_urb(req); ++ pnd->urbs[i] = NULL; ++ } ++ ++ return usb_set_interface(pnd->usb, num, !pnd->active_setting); ++} ++ ++static int usbpn_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ++{ ++ struct if_phonet_req *req = (struct if_phonet_req *)ifr; ++ ++ switch (cmd) { ++ case SIOCPNGAUTOCONF: ++ req->ifr_phonet_autoconf.device = PN_DEV_PC; ++ return 0; ++ } ++ return -ENOIOCTLCMD; ++} ++ ++static int usbpn_set_mtu(struct net_device *dev, int new_mtu) ++{ ++ if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU)) ++ return -EINVAL; ++ ++ dev->mtu = new_mtu; ++ return 0; ++} ++ ++static const struct net_device_ops usbpn_ops = { ++ .ndo_open = usbpn_open, ++ .ndo_stop = usbpn_close, ++ .ndo_start_xmit = usbpn_xmit, ++ .ndo_do_ioctl = usbpn_ioctl, ++ .ndo_change_mtu = usbpn_set_mtu, ++}; ++ ++static void usbpn_setup(struct net_device *dev) ++{ ++ dev->features = 0; ++ dev->netdev_ops = &usbpn_ops, ++ dev->header_ops = &phonet_header_ops; ++ dev->type = ARPHRD_PHONET; ++ dev->flags = IFF_POINTOPOINT | IFF_NOARP; ++ dev->mtu = PHONET_MAX_MTU; ++ dev->hard_header_len = 1; ++ dev->dev_addr[0] = PN_MEDIA_USB; ++ dev->addr_len = 1; ++ dev->tx_queue_len = 3; ++ ++ dev->destructor = free_netdev; ++} ++ ++/* ++ * USB driver callbacks ++ */ ++static struct usb_device_id usbpn_ids[] = { ++ { ++ .match_flags = USB_DEVICE_ID_MATCH_VENDOR ++ | USB_DEVICE_ID_MATCH_INT_CLASS ++ | USB_DEVICE_ID_MATCH_INT_SUBCLASS, ++ .idVendor = 0x0421, /* Nokia */ ++ .bInterfaceClass = USB_CLASS_COMM, ++ .bInterfaceSubClass = 0xFE, ++ }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(usb, usbpn_ids); ++ ++static struct usb_driver usbpn_driver; ++ ++static int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) ++{ ++ static const char ifname[] = "usbpn%d"; ++ const struct usb_cdc_union_desc *union_header = NULL; ++ const struct usb_host_interface *data_desc; ++ struct usb_interface *data_intf; ++ struct usb_device *usbdev = interface_to_usbdev(intf); ++ struct net_device *dev; ++ struct usbpn_dev *pnd; ++ u8 *data; ++ int phonet = 0; ++ int len, err; ++ ++ data = intf->altsetting->extra; ++ len = intf->altsetting->extralen; ++ while (len >= 3) { ++ u8 dlen = data[0]; ++ if (dlen < 3) ++ return -EINVAL; ++ ++ /* bDescriptorType */ ++ if (data[1] == USB_DT_CS_INTERFACE) { ++ /* bDescriptorSubType */ ++ switch (data[2]) { ++ case USB_CDC_UNION_TYPE: ++ if (union_header || dlen < 5) ++ break; ++ union_header = ++ (struct usb_cdc_union_desc *)data; ++ break; ++ case 0xAB: ++ phonet = 1; ++ break; ++ } ++ } ++ data += dlen; ++ len -= dlen; ++ } ++ ++ if (!union_header || !phonet) ++ return -EINVAL; ++ ++ data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0); ++ if (data_intf == NULL) ++ return -ENODEV; ++ /* Data interface has one inactive and one active setting */ ++ if (data_intf->num_altsetting != 2) ++ return -EINVAL; ++ if (data_intf->altsetting[0].desc.bNumEndpoints == 0 && ++ data_intf->altsetting[1].desc.bNumEndpoints == 2) ++ data_desc = data_intf->altsetting + 1; ++ else ++ if (data_intf->altsetting[0].desc.bNumEndpoints == 2 && ++ data_intf->altsetting[1].desc.bNumEndpoints == 0) ++ data_desc = data_intf->altsetting; ++ else ++ return -EINVAL; ++ ++ dev = alloc_netdev(sizeof(*pnd) + sizeof(pnd->urbs[0]) * rxq_size, ++ ifname, NET_NAME_UNKNOWN, usbpn_setup); ++ if (!dev) ++ return -ENOMEM; ++ ++ pnd = netdev_priv(dev); ++ SET_NETDEV_DEV(dev, &intf->dev); ++ ++ pnd->dev = dev; ++ pnd->usb = usbdev; ++ pnd->intf = intf; ++ pnd->data_intf = data_intf; ++ spin_lock_init(&pnd->tx_lock); ++ spin_lock_init(&pnd->rx_lock); ++ /* Endpoints */ ++ if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) { ++ pnd->rx_pipe = usb_rcvbulkpipe(usbdev, ++ data_desc->endpoint[0].desc.bEndpointAddress); ++ pnd->tx_pipe = usb_sndbulkpipe(usbdev, ++ data_desc->endpoint[1].desc.bEndpointAddress); ++ } else { ++ pnd->rx_pipe = usb_rcvbulkpipe(usbdev, ++ data_desc->endpoint[1].desc.bEndpointAddress); ++ pnd->tx_pipe = usb_sndbulkpipe(usbdev, ++ data_desc->endpoint[0].desc.bEndpointAddress); ++ } ++ pnd->active_setting = data_desc - data_intf->altsetting; ++ ++ err = usb_driver_claim_interface(&usbpn_driver, data_intf, pnd); ++ if (err) ++ goto out; ++ ++ /* Force inactive mode until the network device is brought UP */ ++ usb_set_interface(usbdev, union_header->bSlaveInterface0, ++ !pnd->active_setting); ++ usb_set_intfdata(intf, pnd); ++ ++ err = register_netdev(dev); ++ if (err) { ++ usb_driver_release_interface(&usbpn_driver, data_intf); ++ goto out; ++ } ++ ++ dev_dbg(&dev->dev, "USB CDC Phonet device found\n"); ++ return 0; ++ ++out: ++ usb_set_intfdata(intf, NULL); ++ free_netdev(dev); ++ return err; ++} ++ ++static void usbpn_disconnect(struct usb_interface *intf) ++{ ++ struct usbpn_dev *pnd = usb_get_intfdata(intf); ++ ++ if (pnd->disconnected) ++ return; ++ ++ pnd->disconnected = 1; ++ usb_driver_release_interface(&usbpn_driver, ++ (pnd->intf == intf) ? pnd->data_intf : pnd->intf); ++ unregister_netdev(pnd->dev); ++} ++ ++static struct usb_driver usbpn_driver = { ++ .name = "cdc_phonet", ++ .probe = usbpn_probe, ++ .disconnect = usbpn_disconnect, ++ .id_table = usbpn_ids, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(usbpn_driver); ++ ++MODULE_AUTHOR("Remi Denis-Courmont"); ++MODULE_DESCRIPTION("USB CDC Phonet host interface"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/cdc_subset.c backports-3.18.1-1/drivers/net/usb/cdc_subset.c +--- backports-3.18.1-1.org/drivers/net/usb/cdc_subset.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/cdc_subset.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,369 @@ ++/* ++ * Simple "CDC Subset" USB Networking Links ++ * Copyright (C) 2000-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, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * This supports simple USB network links that don't require any special ++ * framing or hardware control operations. The protocol used here is a ++ * strict subset of CDC Ethernet, with three basic differences reflecting ++ * the goal that almost any hardware should run it: ++ * ++ * - Minimal runtime control: one interface, no altsettings, and ++ * no vendor or class specific control requests. If a device is ++ * configured, it is allowed to exchange packets with the host. ++ * Fancier models would mean not working on some hardware. ++ * ++ * - Minimal manufacturing control: no IEEE "Organizationally ++ * Unique ID" required, or an EEPROMs to store one. Each host uses ++ * one random "locally assigned" Ethernet address instead, which can ++ * of course be overridden using standard tools like "ifconfig". ++ * (With 2^46 such addresses, same-net collisions are quite rare.) ++ * ++ * - There is no additional framing data for USB. Packets are written ++ * exactly as in CDC Ethernet, starting with an Ethernet header and ++ * terminated by a short packet. However, the host will never send a ++ * zero length packet; some systems can't handle those robustly. ++ * ++ * Anything that can transmit and receive USB bulk packets can implement ++ * this protocol. That includes both smart peripherals and quite a lot ++ * of "host-to-host" USB cables (which embed two devices back-to-back). ++ * ++ * Note that although Linux may use many of those host-to-host links ++ * with this "cdc_subset" framing, that doesn't mean there may not be a ++ * better approach. Handling the "other end unplugs/replugs" scenario ++ * well tends to require chip-specific vendor requests. Also, Windows ++ * peers at the other end of host-to-host cables may expect their own ++ * framing to be used rather than this "cdc_subset" model. ++ */ ++ ++#if defined(CONFIG_USB_EPSON2888) || defined(CONFIG_USB_ARMLINUX) ++/* PDA style devices are always connected if present */ ++static int always_connected (struct usbnet *dev) ++{ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_USB_ALI_M5632 ++#define HAVE_HARDWARE ++ ++/*------------------------------------------------------------------------- ++ * ++ * ALi M5632 driver ... does high speed ++ * ++ * NOTE that the MS-Windows drivers for this chip use some funky and ++ * (naturally) undocumented 7-byte prefix to each packet, so this is a ++ * case where we don't currently interoperate. Also, once you unplug ++ * one end of the cable, you need to replug the other end too ... since ++ * chip docs are unavailable, there's no way to reset the relevant state ++ * short of a power cycle. ++ * ++ *-------------------------------------------------------------------------*/ ++ ++static void m5632_recover(struct usbnet *dev) ++{ ++ struct usb_device *udev = dev->udev; ++ struct usb_interface *intf = dev->intf; ++ int r; ++ ++ r = usb_lock_device_for_reset(udev, intf); ++ if (r < 0) ++ return; ++ ++ usb_reset_device(udev); ++ usb_unlock_device(udev); ++} ++ ++static const struct driver_info ali_m5632_info = { ++ .description = "ALi M5632", ++ .flags = FLAG_POINTTOPOINT, ++ .recover = m5632_recover, ++}; ++ ++#endif ++ ++#ifdef CONFIG_USB_AN2720 ++#define HAVE_HARDWARE ++ ++/*------------------------------------------------------------------------- ++ * ++ * AnchorChips 2720 driver ... http://www.cypress.com ++ * ++ * This doesn't seem to have a way to detect whether the peer is ++ * connected, or need any reset handshaking. It's got pretty big ++ * internal buffers (handles most of a frame's worth of data). ++ * Chip data sheets don't describe any vendor control messages. ++ * ++ *-------------------------------------------------------------------------*/ ++ ++static const struct driver_info an2720_info = { ++ .description = "AnchorChips/Cypress 2720", ++ .flags = FLAG_POINTTOPOINT, ++ // no reset available! ++ // no check_connect available! ++ ++ .in = 2, .out = 2, // direction distinguishes these ++}; ++ ++#endif /* CONFIG_USB_AN2720 */ ++ ++ ++#ifdef CONFIG_USB_BELKIN ++#define HAVE_HARDWARE ++ ++/*------------------------------------------------------------------------- ++ * ++ * Belkin F5U104 ... two NetChip 2280 devices + Atmel AVR microcontroller ++ * ++ * ... also two eTEK designs, including one sold as "Advance USBNET" ++ * ++ *-------------------------------------------------------------------------*/ ++ ++static const struct driver_info belkin_info = { ++ .description = "Belkin, eTEK, or compatible", ++ .flags = FLAG_POINTTOPOINT, ++}; ++ ++#endif /* CONFIG_USB_BELKIN */ ++ ++ ++ ++#ifdef CONFIG_USB_EPSON2888 ++#define HAVE_HARDWARE ++ ++/*------------------------------------------------------------------------- ++ * ++ * EPSON USB clients ++ * ++ * This is the same idea as Linux PDAs (below) except the firmware in the ++ * device might not be Tux-powered. Epson provides reference firmware that ++ * implements this interface. Product developers can reuse or modify that ++ * code, such as by using their own product and vendor codes. ++ * ++ * Support was from Juro Bystricky ++ * ++ *-------------------------------------------------------------------------*/ ++ ++static const struct driver_info epson2888_info = { ++ .description = "Epson USB Device", ++ .check_connect = always_connected, ++ .flags = FLAG_POINTTOPOINT, ++ ++ .in = 4, .out = 3, ++}; ++ ++#endif /* CONFIG_USB_EPSON2888 */ ++ ++ ++/*------------------------------------------------------------------------- ++ * ++ * info from Jonathan McDowell ++ * ++ *-------------------------------------------------------------------------*/ ++#ifdef CONFIG_USB_KC2190 ++#define HAVE_HARDWARE ++static const struct driver_info kc2190_info = { ++ .description = "KC Technology KC-190", ++ .flags = FLAG_POINTTOPOINT, ++}; ++#endif /* CONFIG_USB_KC2190 */ ++ ++ ++#ifdef CONFIG_USB_ARMLINUX ++#define HAVE_HARDWARE ++ ++/*------------------------------------------------------------------------- ++ * ++ * Intel's SA-1100 chip integrates basic USB support, and is used ++ * in PDAs like some iPaqs, the Yopy, some Zaurus models, and more. ++ * When they run Linux, arch/arm/mach-sa1100/usb-eth.c may be used to ++ * network using minimal USB framing data. ++ * ++ * This describes the driver currently in standard ARM Linux kernels. ++ * The Zaurus uses a different driver (see later). ++ * ++ * PXA25x and PXA210 use XScale cores (ARM v5TE) with better USB support ++ * and different USB endpoint numbering than the SA1100 devices. The ++ * mach-pxa/usb-eth.c driver re-uses the device ids from mach-sa1100 ++ * so we rely on the endpoint descriptors. ++ * ++ *-------------------------------------------------------------------------*/ ++ ++static const struct driver_info linuxdev_info = { ++ .description = "Linux Device", ++ .check_connect = always_connected, ++ .flags = FLAG_POINTTOPOINT, ++}; ++ ++static const struct driver_info yopy_info = { ++ .description = "Yopy", ++ .check_connect = always_connected, ++ .flags = FLAG_POINTTOPOINT, ++}; ++ ++static const struct driver_info blob_info = { ++ .description = "Boot Loader OBject", ++ .check_connect = always_connected, ++ .flags = FLAG_POINTTOPOINT, ++}; ++ ++#endif /* CONFIG_USB_ARMLINUX */ ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifndef HAVE_HARDWARE ++#warning You need to configure some hardware for this driver ++#endif ++ ++/* ++ * chip vendor names won't normally be on the cables, and ++ * may not be on the device. ++ */ ++ ++static const struct usb_device_id products [] = { ++ ++#ifdef CONFIG_USB_ALI_M5632 ++{ ++ USB_DEVICE (0x0402, 0x5632), // ALi defaults ++ .driver_info = (unsigned long) &ali_m5632_info, ++}, ++{ ++ USB_DEVICE (0x182d,0x207c), // SiteCom CN-124 ++ .driver_info = (unsigned long) &ali_m5632_info, ++}, ++#endif ++ ++#ifdef CONFIG_USB_AN2720 ++{ ++ USB_DEVICE (0x0547, 0x2720), // AnchorChips defaults ++ .driver_info = (unsigned long) &an2720_info, ++}, { ++ USB_DEVICE (0x0547, 0x2727), // Xircom PGUNET ++ .driver_info = (unsigned long) &an2720_info, ++}, ++#endif ++ ++#ifdef CONFIG_USB_BELKIN ++{ ++ USB_DEVICE (0x050d, 0x0004), // Belkin ++ .driver_info = (unsigned long) &belkin_info, ++}, { ++ USB_DEVICE (0x056c, 0x8100), // eTEK ++ .driver_info = (unsigned long) &belkin_info, ++}, { ++ USB_DEVICE (0x0525, 0x9901), // Advance USBNET (eTEK) ++ .driver_info = (unsigned long) &belkin_info, ++}, ++#endif ++ ++#ifdef CONFIG_USB_EPSON2888 ++{ ++ USB_DEVICE (0x0525, 0x2888), // EPSON USB client ++ .driver_info = (unsigned long) &epson2888_info, ++}, ++#endif ++ ++#ifdef CONFIG_USB_KC2190 ++{ ++ USB_DEVICE (0x050f, 0x0190), // KC-190 ++ .driver_info = (unsigned long) &kc2190_info, ++}, ++#endif ++ ++#ifdef CONFIG_USB_ARMLINUX ++/* ++ * SA-1100 using standard ARM Linux kernels, or compatible. ++ * Often used when talking to Linux PDAs (iPaq, Yopy, etc). ++ * The sa-1100 "usb-eth" driver handles the basic framing. ++ * ++ * PXA25x or PXA210 ... these use a "usb-eth" driver much like ++ * the sa1100 one, but hardware uses different endpoint numbers. ++ * ++ * Or the Linux "Ethernet" gadget on hardware that can't talk ++ * CDC Ethernet (e.g., no altsettings), in either of two modes: ++ * - acting just like the old "usb-eth" firmware, though ++ * the implementation is different ++ * - supporting RNDIS as the first/default configuration for ++ * MS-Windows interop; Linux needs to use the other config ++ */ ++{ ++ // 1183 = 0x049F, both used as hex values? ++ // Compaq "Itsy" vendor/product id ++ USB_DEVICE (0x049F, 0x505A), // usb-eth, or compatible ++ .driver_info = (unsigned long) &linuxdev_info, ++}, { ++ USB_DEVICE (0x0E7E, 0x1001), // G.Mate "Yopy" ++ .driver_info = (unsigned long) &yopy_info, ++}, { ++ USB_DEVICE (0x8086, 0x07d3), // "blob" bootloader ++ .driver_info = (unsigned long) &blob_info, ++}, { ++ USB_DEVICE (0x1286, 0x8001), // "blob" bootloader ++ .driver_info = (unsigned long) &blob_info, ++}, { ++ // Linux Ethernet/RNDIS gadget, mostly on PXA, second config ++ // e.g. Gumstix, current OpenZaurus, ... or anything else ++ // that just enables this gadget option. ++ USB_DEVICE (0x0525, 0xa4a2), ++ .driver_info = (unsigned long) &linuxdev_info, ++}, ++#endif ++ ++ { }, // END ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++/*-------------------------------------------------------------------------*/ ++static int dummy_prereset(struct usb_interface *intf) ++{ ++ return 0; ++} ++ ++static int dummy_postreset(struct usb_interface *intf) ++{ ++ return 0; ++} ++ ++static struct usb_driver cdc_subset_driver = { ++ .name = "cdc_subset", ++ .probe = usbnet_probe, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .pre_reset = dummy_prereset, ++ .post_reset = dummy_postreset, ++ .disconnect = usbnet_disconnect, ++ .id_table = products, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(cdc_subset_driver); ++ ++MODULE_AUTHOR("David Brownell"); ++MODULE_DESCRIPTION("Simple 'CDC Subset' USB networking links"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/cx82310_eth.c backports-3.18.1-1/drivers/net/usb/cx82310_eth.c +--- backports-3.18.1-1.org/drivers/net/usb/cx82310_eth.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/cx82310_eth.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,326 @@ ++/* ++ * Driver for USB ethernet port of Conexant CX82310-based ADSL routers ++ * Copyright (C) 2010 by Ondrej Zary ++ * some parts inspired by the cxacru driver ++ * ++ * 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, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++enum cx82310_cmd { ++ CMD_START = 0x84, /* no effect? */ ++ CMD_STOP = 0x85, /* no effect? */ ++ CMD_GET_STATUS = 0x90, /* returns nothing? */ ++ CMD_GET_MAC_ADDR = 0x91, /* read MAC address */ ++ CMD_GET_LINK_STATUS = 0x92, /* not useful, link is always up */ ++ CMD_ETHERNET_MODE = 0x99, /* unknown, needed during init */ ++}; ++ ++enum cx82310_status { ++ STATUS_UNDEFINED, ++ STATUS_SUCCESS, ++ STATUS_ERROR, ++ STATUS_UNSUPPORTED, ++ STATUS_UNIMPLEMENTED, ++ STATUS_PARAMETER_ERROR, ++ STATUS_DBG_LOOPBACK, ++}; ++ ++#define CMD_PACKET_SIZE 64 ++/* first command after power on can take around 8 seconds */ ++#define CMD_TIMEOUT 15000 ++#define CMD_REPLY_RETRY 5 ++ ++#define CX82310_MTU 1514 ++#define CMD_EP 0x01 ++ ++/* ++ * execute control command ++ * - optionally send some data (command parameters) ++ * - optionally wait for the reply ++ * - optionally read some data from the reply ++ */ ++static int cx82310_cmd(struct usbnet *dev, enum cx82310_cmd cmd, bool reply, ++ u8 *wdata, int wlen, u8 *rdata, int rlen) ++{ ++ int actual_len, retries, ret; ++ struct usb_device *udev = dev->udev; ++ u8 *buf = kzalloc(CMD_PACKET_SIZE, GFP_KERNEL); ++ ++ if (!buf) ++ return -ENOMEM; ++ ++ /* create command packet */ ++ buf[0] = cmd; ++ if (wdata) ++ memcpy(buf + 4, wdata, min_t(int, wlen, CMD_PACKET_SIZE - 4)); ++ ++ /* send command packet */ ++ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, CMD_EP), buf, ++ CMD_PACKET_SIZE, &actual_len, CMD_TIMEOUT); ++ if (ret < 0) { ++ dev_err(&dev->udev->dev, "send command %#x: error %d\n", ++ cmd, ret); ++ goto end; ++ } ++ ++ if (reply) { ++ /* wait for reply, retry if it's empty */ ++ for (retries = 0; retries < CMD_REPLY_RETRY; retries++) { ++ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, CMD_EP), ++ buf, CMD_PACKET_SIZE, &actual_len, ++ CMD_TIMEOUT); ++ if (ret < 0) { ++ dev_err(&dev->udev->dev, ++ "reply receive error %d\n", ret); ++ goto end; ++ } ++ if (actual_len > 0) ++ break; ++ } ++ if (actual_len == 0) { ++ dev_err(&dev->udev->dev, "no reply to command %#x\n", ++ cmd); ++ ret = -EIO; ++ goto end; ++ } ++ if (buf[0] != cmd) { ++ dev_err(&dev->udev->dev, ++ "got reply to command %#x, expected: %#x\n", ++ buf[0], cmd); ++ ret = -EIO; ++ goto end; ++ } ++ if (buf[1] != STATUS_SUCCESS) { ++ dev_err(&dev->udev->dev, "command %#x failed: %#x\n", ++ cmd, buf[1]); ++ ret = -EIO; ++ goto end; ++ } ++ if (rdata) ++ memcpy(rdata, buf + 4, ++ min_t(int, rlen, CMD_PACKET_SIZE - 4)); ++ } ++end: ++ kfree(buf); ++ return ret; ++} ++ ++#define partial_len data[0] /* length of partial packet data */ ++#define partial_rem data[1] /* remaining (missing) data length */ ++#define partial_data data[2] /* partial packet data */ ++ ++static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int ret; ++ char buf[15]; ++ struct usb_device *udev = dev->udev; ++ ++ /* avoid ADSL modems - continue only if iProduct is "USB NET CARD" */ ++ if (usb_string(udev, udev->descriptor.iProduct, buf, sizeof(buf)) > 0 ++ && strcmp(buf, "USB NET CARD")) { ++ dev_info(&udev->dev, "ignoring: probably an ADSL modem\n"); ++ return -ENODEV; ++ } ++ ++ ret = usbnet_get_endpoints(dev, intf); ++ if (ret) ++ return ret; ++ ++ /* ++ * this must not include ethernet header as the device can send partial ++ * packets with no header (and sometimes even empty URBs) ++ */ ++ dev->net->hard_header_len = 0; ++ /* we can send at most 1514 bytes of data (+ 2-byte header) per URB */ ++ dev->hard_mtu = CX82310_MTU + 2; ++ /* we can receive URBs up to 4KB from the device */ ++ dev->rx_urb_size = 4096; ++ ++ dev->partial_data = (unsigned long) kmalloc(dev->hard_mtu, GFP_KERNEL); ++ if (!dev->partial_data) ++ return -ENOMEM; ++ ++ /* enable ethernet mode (?) */ ++ ret = cx82310_cmd(dev, CMD_ETHERNET_MODE, true, "\x01", 1, NULL, 0); ++ if (ret) { ++ dev_err(&udev->dev, "unable to enable ethernet mode: %d\n", ++ ret); ++ goto err; ++ } ++ ++ /* get the MAC address */ ++ ret = cx82310_cmd(dev, CMD_GET_MAC_ADDR, true, NULL, 0, ++ dev->net->dev_addr, ETH_ALEN); ++ if (ret) { ++ dev_err(&udev->dev, "unable to read MAC address: %d\n", ret); ++ goto err; ++ } ++ ++ /* start (does not seem to have any effect?) */ ++ ret = cx82310_cmd(dev, CMD_START, false, NULL, 0, NULL, 0); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ kfree((void *)dev->partial_data); ++ return ret; ++} ++ ++static void cx82310_unbind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ kfree((void *)dev->partial_data); ++} ++ ++/* ++ * RX is NOT easy - we can receive multiple packets per skb, each having 2-byte ++ * packet length at the beginning. ++ * The last packet might be incomplete (when it crosses the 4KB URB size), ++ * continuing in the next skb (without any headers). ++ * If a packet has odd length, there is one extra byte at the end (before next ++ * packet or at the end of the URB). ++ */ ++static int cx82310_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ int len; ++ struct sk_buff *skb2; ++ ++ /* ++ * If the last skb ended with an incomplete packet, this skb contains ++ * end of that packet at the beginning. ++ */ ++ if (dev->partial_rem) { ++ len = dev->partial_len + dev->partial_rem; ++ skb2 = alloc_skb(len, GFP_ATOMIC); ++ if (!skb2) ++ return 0; ++ skb_put(skb2, len); ++ memcpy(skb2->data, (void *)dev->partial_data, ++ dev->partial_len); ++ memcpy(skb2->data + dev->partial_len, skb->data, ++ dev->partial_rem); ++ usbnet_skb_return(dev, skb2); ++ skb_pull(skb, (dev->partial_rem + 1) & ~1); ++ dev->partial_rem = 0; ++ if (skb->len < 2) ++ return 1; ++ } ++ ++ /* a skb can contain multiple packets */ ++ while (skb->len > 1) { ++ /* first two bytes are packet length */ ++ len = skb->data[0] | (skb->data[1] << 8); ++ skb_pull(skb, 2); ++ ++ /* if last packet in the skb, let usbnet to process it */ ++ if (len == skb->len || len + 1 == skb->len) { ++ skb_trim(skb, len); ++ break; ++ } ++ ++ if (len > CX82310_MTU) { ++ dev_err(&dev->udev->dev, "RX packet too long: %d B\n", ++ len); ++ return 0; ++ } ++ ++ /* incomplete packet, save it for the next skb */ ++ if (len > skb->len) { ++ dev->partial_len = skb->len; ++ dev->partial_rem = len - skb->len; ++ memcpy((void *)dev->partial_data, skb->data, ++ dev->partial_len); ++ skb_pull(skb, skb->len); ++ break; ++ } ++ ++ skb2 = alloc_skb(len, GFP_ATOMIC); ++ if (!skb2) ++ return 0; ++ skb_put(skb2, len); ++ memcpy(skb2->data, skb->data, len); ++ /* process the packet */ ++ usbnet_skb_return(dev, skb2); ++ ++ skb_pull(skb, (len + 1) & ~1); ++ } ++ ++ /* let usbnet process the last packet */ ++ return 1; ++} ++ ++/* TX is easy, just add 2 bytes of length at the beginning */ ++static struct sk_buff *cx82310_tx_fixup(struct usbnet *dev, struct sk_buff *skb, ++ gfp_t flags) ++{ ++ int len = skb->len; ++ ++ if (skb_headroom(skb) < 2) { ++ struct sk_buff *skb2 = skb_copy_expand(skb, 2, 0, flags); ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ if (!skb) ++ return NULL; ++ } ++ skb_push(skb, 2); ++ ++ skb->data[0] = len; ++ skb->data[1] = len >> 8; ++ ++ return skb; ++} ++ ++ ++static const struct driver_info cx82310_info = { ++ .description = "Conexant CX82310 USB ethernet", ++ .flags = FLAG_ETHER, ++ .bind = cx82310_bind, ++ .unbind = cx82310_unbind, ++ .rx_fixup = cx82310_rx_fixup, ++ .tx_fixup = cx82310_tx_fixup, ++}; ++ ++static const struct usb_device_id products[] = { ++ { ++ USB_DEVICE_AND_INTERFACE_INFO(0x0572, 0xcb01, 0xff, 0, 0), ++ .driver_info = (unsigned long) &cx82310_info ++ }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver cx82310_driver = { ++ .name = "cx82310_eth", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(cx82310_driver); ++ ++MODULE_AUTHOR("Ondrej Zary"); ++MODULE_DESCRIPTION("Conexant CX82310-based ADSL router USB ethernet driver"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/dm9601.c backports-3.18.1-1/drivers/net/usb/dm9601.c +--- backports-3.18.1-1.org/drivers/net/usb/dm9601.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/dm9601.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,647 @@ ++/* ++ * Davicom DM96xx USB 10/100Mbps ethernet devices ++ * ++ * Peter Korsgaard ++ * ++ * This file is licensed under the terms of the GNU General Public License ++ * version 2. This program is licensed "as is" without any warranty of any ++ * kind, whether express or implied. ++ */ ++ ++//#define DEBUG ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* datasheet: ++ http://ptm2.cc.utu.fi/ftp/network/cards/DM9601/From_NET/DM9601-DS-P01-930914.pdf ++*/ ++ ++/* control requests */ ++#define DM_READ_REGS 0x00 ++#define DM_WRITE_REGS 0x01 ++#define DM_READ_MEMS 0x02 ++#define DM_WRITE_REG 0x03 ++#define DM_WRITE_MEMS 0x05 ++#define DM_WRITE_MEM 0x07 ++ ++/* registers */ ++#define DM_NET_CTRL 0x00 ++#define DM_RX_CTRL 0x05 ++#define DM_SHARED_CTRL 0x0b ++#define DM_SHARED_ADDR 0x0c ++#define DM_SHARED_DATA 0x0d /* low + high */ ++#define DM_PHY_ADDR 0x10 /* 6 bytes */ ++#define DM_MCAST_ADDR 0x16 /* 8 bytes */ ++#define DM_GPR_CTRL 0x1e ++#define DM_GPR_DATA 0x1f ++#define DM_CHIP_ID 0x2c ++#define DM_MODE_CTRL 0x91 /* only on dm9620 */ ++ ++/* chip id values */ ++#define ID_DM9601 0 ++#define ID_DM9620 1 ++ ++#define DM_MAX_MCAST 64 ++#define DM_MCAST_SIZE 8 ++#define DM_EEPROM_LEN 256 ++#define DM_TX_OVERHEAD 2 /* 2 byte header */ ++#define DM_RX_OVERHEAD 7 /* 3 byte header + 4 byte crc tail */ ++#define DM_TIMEOUT 1000 ++ ++static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data) ++{ ++ int err; ++ err = usbnet_read_cmd(dev, DM_READ_REGS, ++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ 0, reg, data, length); ++ if(err != length && err >= 0) ++ err = -EINVAL; ++ return err; ++} ++ ++static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value) ++{ ++ return dm_read(dev, reg, 1, value); ++} ++ ++static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data) ++{ ++ int err; ++ err = usbnet_write_cmd(dev, DM_WRITE_REGS, ++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ 0, reg, data, length); ++ ++ if (err >= 0 && err < length) ++ err = -EINVAL; ++ return err; ++} ++ ++static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value) ++{ ++ return usbnet_write_cmd(dev, DM_WRITE_REG, ++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ value, reg, NULL, 0); ++} ++ ++static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data) ++{ ++ usbnet_write_cmd_async(dev, DM_WRITE_REGS, ++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ 0, reg, data, length); ++} ++ ++static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value) ++{ ++ usbnet_write_cmd_async(dev, DM_WRITE_REG, ++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ value, reg, NULL, 0); ++} ++ ++static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 *value) ++{ ++ int ret, i; ++ ++ mutex_lock(&dev->phy_mutex); ++ ++ dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg); ++ dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0xc : 0x4); ++ ++ for (i = 0; i < DM_TIMEOUT; i++) { ++ u8 tmp = 0; ++ ++ udelay(1); ++ ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp); ++ if (ret < 0) ++ goto out; ++ ++ /* ready */ ++ if ((tmp & 1) == 0) ++ break; ++ } ++ ++ if (i == DM_TIMEOUT) { ++ netdev_err(dev->net, "%s read timed out!\n", phy ? "phy" : "eeprom"); ++ ret = -EIO; ++ goto out; ++ } ++ ++ dm_write_reg(dev, DM_SHARED_CTRL, 0x0); ++ ret = dm_read(dev, DM_SHARED_DATA, 2, value); ++ ++ netdev_dbg(dev->net, "read shared %d 0x%02x returned 0x%04x, %d\n", ++ phy, reg, *value, ret); ++ ++ out: ++ mutex_unlock(&dev->phy_mutex); ++ return ret; ++} ++ ++static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 value) ++{ ++ int ret, i; ++ ++ mutex_lock(&dev->phy_mutex); ++ ++ ret = dm_write(dev, DM_SHARED_DATA, 2, &value); ++ if (ret < 0) ++ goto out; ++ ++ dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg); ++ dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x1a : 0x12); ++ ++ for (i = 0; i < DM_TIMEOUT; i++) { ++ u8 tmp = 0; ++ ++ udelay(1); ++ ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp); ++ if (ret < 0) ++ goto out; ++ ++ /* ready */ ++ if ((tmp & 1) == 0) ++ break; ++ } ++ ++ if (i == DM_TIMEOUT) { ++ netdev_err(dev->net, "%s write timed out!\n", phy ? "phy" : "eeprom"); ++ ret = -EIO; ++ goto out; ++ } ++ ++ dm_write_reg(dev, DM_SHARED_CTRL, 0x0); ++ ++out: ++ mutex_unlock(&dev->phy_mutex); ++ return ret; ++} ++ ++static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value) ++{ ++ return dm_read_shared_word(dev, 0, offset, value); ++} ++ ++ ++ ++static int dm9601_get_eeprom_len(struct net_device *dev) ++{ ++ return DM_EEPROM_LEN; ++} ++ ++static int dm9601_get_eeprom(struct net_device *net, ++ struct ethtool_eeprom *eeprom, u8 * data) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ __le16 *ebuf = (__le16 *) data; ++ int i; ++ ++ /* access is 16bit */ ++ if ((eeprom->offset % 2) || (eeprom->len % 2)) ++ return -EINVAL; ++ ++ for (i = 0; i < eeprom->len / 2; i++) { ++ if (dm_read_eeprom_word(dev, eeprom->offset / 2 + i, ++ &ebuf[i]) < 0) ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int dm9601_mdio_read(struct net_device *netdev, int phy_id, int loc) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ ++ __le16 res; ++ ++ if (phy_id) { ++ netdev_dbg(dev->net, "Only internal phy supported\n"); ++ return 0; ++ } ++ ++ dm_read_shared_word(dev, 1, loc, &res); ++ ++ netdev_dbg(dev->net, ++ "dm9601_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", ++ phy_id, loc, le16_to_cpu(res)); ++ ++ return le16_to_cpu(res); ++} ++ ++static void dm9601_mdio_write(struct net_device *netdev, int phy_id, int loc, ++ int val) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ __le16 res = cpu_to_le16(val); ++ ++ if (phy_id) { ++ netdev_dbg(dev->net, "Only internal phy supported\n"); ++ return; ++ } ++ ++ netdev_dbg(dev->net, "dm9601_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", ++ phy_id, loc, val); ++ ++ dm_write_shared_word(dev, 1, loc, res); ++} ++ ++static void dm9601_get_drvinfo(struct net_device *net, ++ struct ethtool_drvinfo *info) ++{ ++ /* Inherit standard device info */ ++ usbnet_get_drvinfo(net, info); ++ info->eedump_len = DM_EEPROM_LEN; ++} ++ ++static u32 dm9601_get_link(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ ++ return mii_link_ok(&dev->mii); ++} ++ ++static int dm9601_ioctl(struct net_device *net, struct ifreq *rq, int cmd) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ ++ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); ++} ++ ++static const struct ethtool_ops dm9601_ethtool_ops = { ++ .get_drvinfo = dm9601_get_drvinfo, ++ .get_link = dm9601_get_link, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_eeprom_len = dm9601_get_eeprom_len, ++ .get_eeprom = dm9601_get_eeprom, ++ .get_settings = usbnet_get_settings, ++ .set_settings = usbnet_set_settings, ++ .nway_reset = usbnet_nway_reset, ++}; ++ ++static void dm9601_set_multicast(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ /* We use the 20 byte dev->data for our 8 byte filter buffer ++ * to avoid allocating memory that is tricky to free later */ ++ u8 *hashes = (u8 *) & dev->data; ++ u8 rx_ctl = 0x31; ++ ++ memset(hashes, 0x00, DM_MCAST_SIZE); ++ hashes[DM_MCAST_SIZE - 1] |= 0x80; /* broadcast address */ ++ ++ if (net->flags & IFF_PROMISC) { ++ rx_ctl |= 0x02; ++ } else if (net->flags & IFF_ALLMULTI || ++ netdev_mc_count(net) > DM_MAX_MCAST) { ++ rx_ctl |= 0x08; ++ } else if (!netdev_mc_empty(net)) { ++ struct netdev_hw_addr *ha; ++ ++ netdev_for_each_mc_addr(ha, net) { ++ u32 crc = ether_crc(ETH_ALEN, ha->addr) >> 26; ++ hashes[crc >> 3] |= 1 << (crc & 0x7); ++ } ++ } ++ ++ dm_write_async(dev, DM_MCAST_ADDR, DM_MCAST_SIZE, hashes); ++ dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl); ++} ++ ++static void __dm9601_set_mac_address(struct usbnet *dev) ++{ ++ dm_write_async(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr); ++} ++ ++static int dm9601_set_mac_address(struct net_device *net, void *p) ++{ ++ struct sockaddr *addr = p; ++ struct usbnet *dev = netdev_priv(net); ++ ++ if (!is_valid_ether_addr(addr->sa_data)) { ++ dev_err(&net->dev, "not setting invalid mac address %pM\n", ++ addr->sa_data); ++ return -EINVAL; ++ } ++ ++ memcpy(net->dev_addr, addr->sa_data, net->addr_len); ++ __dm9601_set_mac_address(dev); ++ ++ return 0; ++} ++ ++static const struct net_device_ops dm9601_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = usbnet_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_do_ioctl = dm9601_ioctl, ++ .ndo_set_rx_mode = dm9601_set_multicast, ++ .ndo_set_mac_address = dm9601_set_mac_address, ++}; ++ ++static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int ret; ++ u8 mac[ETH_ALEN], id; ++ ++ ret = usbnet_get_endpoints(dev, intf); ++ if (ret) ++ goto out; ++ ++ dev->net->netdev_ops = &dm9601_netdev_ops; ++ dev->net->ethtool_ops = &dm9601_ethtool_ops; ++ dev->net->hard_header_len += DM_TX_OVERHEAD; ++ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; ++ ++ /* dm9620/21a require room for 4 byte padding, even in dm9601 ++ * mode, so we need +1 to be able to receive full size ++ * ethernet frames. ++ */ ++ dev->rx_urb_size = dev->net->mtu + ETH_HLEN + DM_RX_OVERHEAD + 1; ++ ++ dev->mii.dev = dev->net; ++ dev->mii.mdio_read = dm9601_mdio_read; ++ dev->mii.mdio_write = dm9601_mdio_write; ++ dev->mii.phy_id_mask = 0x1f; ++ dev->mii.reg_num_mask = 0x1f; ++ ++ /* reset */ ++ dm_write_reg(dev, DM_NET_CTRL, 1); ++ udelay(20); ++ ++ /* read MAC */ ++ if (dm_read(dev, DM_PHY_ADDR, ETH_ALEN, mac) < 0) { ++ printk(KERN_ERR "Error reading MAC address\n"); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ /* ++ * Overwrite the auto-generated address only with good ones. ++ */ ++ if (is_valid_ether_addr(mac)) ++ memcpy(dev->net->dev_addr, mac, ETH_ALEN); ++ else { ++ printk(KERN_WARNING ++ "dm9601: No valid MAC address in EEPROM, using %pM\n", ++ dev->net->dev_addr); ++ __dm9601_set_mac_address(dev); ++ } ++ ++ if (dm_read_reg(dev, DM_CHIP_ID, &id) < 0) { ++ netdev_err(dev->net, "Error reading chip ID\n"); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ /* put dm9620 devices in dm9601 mode */ ++ if (id == ID_DM9620) { ++ u8 mode; ++ ++ if (dm_read_reg(dev, DM_MODE_CTRL, &mode) < 0) { ++ netdev_err(dev->net, "Error reading MODE_CTRL\n"); ++ ret = -ENODEV; ++ goto out; ++ } ++ dm_write_reg(dev, DM_MODE_CTRL, mode & 0x7f); ++ } ++ ++ /* power up phy */ ++ dm_write_reg(dev, DM_GPR_CTRL, 1); ++ dm_write_reg(dev, DM_GPR_DATA, 0); ++ ++ /* receive broadcast packets */ ++ dm9601_set_multicast(dev->net); ++ ++ dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); ++ dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ++ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); ++ mii_nway_restart(&dev->mii); ++ ++out: ++ return ret; ++} ++ ++static int dm9601_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ u8 status; ++ int len; ++ ++ /* format: ++ b1: rx status ++ b2: packet length (incl crc) low ++ b3: packet length (incl crc) high ++ b4..n-4: packet data ++ bn-3..bn: ethernet crc ++ */ ++ ++ if (unlikely(skb->len < DM_RX_OVERHEAD)) { ++ dev_err(&dev->udev->dev, "unexpected tiny rx frame\n"); ++ return 0; ++ } ++ ++ status = skb->data[0]; ++ len = (skb->data[1] | (skb->data[2] << 8)) - 4; ++ ++ if (unlikely(status & 0xbf)) { ++ if (status & 0x01) dev->net->stats.rx_fifo_errors++; ++ if (status & 0x02) dev->net->stats.rx_crc_errors++; ++ if (status & 0x04) dev->net->stats.rx_frame_errors++; ++ if (status & 0x20) dev->net->stats.rx_missed_errors++; ++ if (status & 0x90) dev->net->stats.rx_length_errors++; ++ return 0; ++ } ++ ++ skb_pull(skb, 3); ++ skb_trim(skb, len); ++ ++ return 1; ++} ++ ++static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb, ++ gfp_t flags) ++{ ++ int len, pad; ++ ++ /* format: ++ b1: packet length low ++ b2: packet length high ++ b3..n: packet data ++ */ ++ ++ len = skb->len + DM_TX_OVERHEAD; ++ ++ /* workaround for dm962x errata with tx fifo getting out of ++ * sync if a USB bulk transfer retry happens right after a ++ * packet with odd / maxpacket length by adding up to 3 bytes ++ * padding. ++ */ ++ while ((len & 1) || !(len % dev->maxpacket)) ++ len++; ++ ++ len -= DM_TX_OVERHEAD; /* hw header doesn't count as part of length */ ++ pad = len - skb->len; ++ ++ if (skb_headroom(skb) < DM_TX_OVERHEAD || skb_tailroom(skb) < pad) { ++ struct sk_buff *skb2; ++ ++ skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, pad, flags); ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ if (!skb) ++ return NULL; ++ } ++ ++ __skb_push(skb, DM_TX_OVERHEAD); ++ ++ if (pad) { ++ memset(skb->data + skb->len, 0, pad); ++ __skb_put(skb, pad); ++ } ++ ++ skb->data[0] = len; ++ skb->data[1] = len >> 8; ++ ++ return skb; ++} ++ ++static void dm9601_status(struct usbnet *dev, struct urb *urb) ++{ ++ int link; ++ u8 *buf; ++ ++ /* format: ++ b0: net status ++ b1: tx status 1 ++ b2: tx status 2 ++ b3: rx status ++ b4: rx overflow ++ b5: rx count ++ b6: tx count ++ b7: gpr ++ */ ++ ++ if (urb->actual_length < 8) ++ return; ++ ++ buf = urb->transfer_buffer; ++ ++ link = !!(buf[0] & 0x40); ++ if (netif_carrier_ok(dev->net) != link) { ++ usbnet_link_change(dev, link, 1); ++ netdev_dbg(dev->net, "Link Status is: %d\n", link); ++ } ++} ++ ++static int dm9601_link_reset(struct usbnet *dev) ++{ ++ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; ++ ++ mii_check_media(&dev->mii, 1, 1); ++ mii_ethtool_gset(&dev->mii, &ecmd); ++ ++ netdev_dbg(dev->net, "link_reset() speed: %u duplex: %d\n", ++ ethtool_cmd_speed(&ecmd), ecmd.duplex); ++ ++ return 0; ++} ++ ++static const struct driver_info dm9601_info = { ++ .description = "Davicom DM96xx USB 10/100 Ethernet", ++ .flags = FLAG_ETHER | FLAG_LINK_INTR, ++ .bind = dm9601_bind, ++ .rx_fixup = dm9601_rx_fixup, ++ .tx_fixup = dm9601_tx_fixup, ++ .status = dm9601_status, ++ .link_reset = dm9601_link_reset, ++ .reset = dm9601_link_reset, ++}; ++ ++static const struct usb_device_id products[] = { ++ { ++ USB_DEVICE(0x07aa, 0x9601), /* Corega FEther USB-TXC */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a46, 0x9601), /* Davicom USB-100 */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a46, 0x6688), /* ZT6688 USB NIC */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a46, 0x0268), /* ShanTou ST268 USB NIC */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a46, 0x8515), /* ADMtek ADM8515 USB NIC */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a47, 0x9601), /* Hirose USB-100 */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0fe6, 0x8101), /* DM9601 USB to Fast Ethernet Adapter */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0fe6, 0x9700), /* DM9601 USB to Fast Ethernet Adapter */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a46, 0x9000), /* DM9000E */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a46, 0x9620), /* DM9620 USB to Fast Ethernet Adapter */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a46, 0x9621), /* DM9621A USB to Fast Ethernet Adapter */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a46, 0x9622), /* DM9622 USB to Fast Ethernet Adapter */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a46, 0x0269), /* DM962OA USB to Fast Ethernet Adapter */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ { ++ USB_DEVICE(0x0a46, 0x1269), /* DM9621A USB to Fast Ethernet Adapter */ ++ .driver_info = (unsigned long)&dm9601_info, ++ }, ++ {}, // END ++}; ++ ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver dm9601_driver = { ++ .name = "dm9601", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(dm9601_driver); ++ ++MODULE_AUTHOR("Peter Korsgaard "); ++MODULE_DESCRIPTION("Davicom DM96xx USB 10/100 ethernet devices"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/gl620a.c backports-3.18.1-1/drivers/net/usb/gl620a.c +--- backports-3.18.1-1.org/drivers/net/usb/gl620a.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/gl620a.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,242 @@ ++/* ++ * GeneSys GL620USB-A based links ++ * Copyright (C) 2001 by Jiun-Jie Huang ++ * Copyright (C) 2001 by Stanislav Brabec ++ * ++ * 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, see . ++ */ ++ ++// #define DEBUG // error path messages, extra info ++// #define VERBOSE // more; success messages ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * GeneSys GL620USB-A (www.genesyslogic.com.tw) ++ * ++ * ... should partially interop with the Win32 driver for this hardware. ++ * The GeneSys docs imply there's some NDIS issue motivating this framing. ++ * ++ * Some info from GeneSys: ++ * - GL620USB-A is full duplex; GL620USB is only half duplex for bulk. ++ * (Some cables, like the BAFO-100c, use the half duplex version.) ++ * - For the full duplex model, the low bit of the version code says ++ * which side is which ("left/right"). ++ * - For the half duplex type, a control/interrupt handshake settles ++ * the transfer direction. (That's disabled here, partially coded.) ++ * A control URB would block until other side writes an interrupt. ++ * ++ * Original code from Jiun-Jie Huang ++ * and merged into "usbnet" by Stanislav Brabec . ++ */ ++ ++// control msg write command ++#define GENELINK_CONNECT_WRITE 0xF0 ++// interrupt pipe index ++#define GENELINK_INTERRUPT_PIPE 0x03 ++// interrupt read buffer size ++#define INTERRUPT_BUFSIZE 0x08 ++// interrupt pipe interval value ++#define GENELINK_INTERRUPT_INTERVAL 0x10 ++// max transmit packet number per transmit ++#define GL_MAX_TRANSMIT_PACKETS 32 ++// max packet length ++#define GL_MAX_PACKET_LEN 1514 ++// max receive buffer size ++#define GL_RCV_BUF_SIZE \ ++ (((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4) ++ ++struct gl_packet { ++ __le32 packet_length; ++ char packet_data [1]; ++}; ++ ++struct gl_header { ++ __le32 packet_count; ++ struct gl_packet packets; ++}; ++ ++static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ struct gl_header *header; ++ struct gl_packet *packet; ++ struct sk_buff *gl_skb; ++ u32 size; ++ u32 count; ++ ++ /* This check is no longer done by usbnet */ ++ if (skb->len < dev->net->hard_header_len) ++ return 0; ++ ++ header = (struct gl_header *) skb->data; ++ ++ // get the packet count of the received skb ++ count = le32_to_cpu(header->packet_count); ++ if (count > GL_MAX_TRANSMIT_PACKETS) { ++ netdev_dbg(dev->net, ++ "genelink: invalid received packet count %u\n", ++ count); ++ return 0; ++ } ++ ++ // set the current packet pointer to the first packet ++ packet = &header->packets; ++ ++ // decrement the length for the packet count size 4 bytes ++ skb_pull(skb, 4); ++ ++ while (count > 1) { ++ // get the packet length ++ size = le32_to_cpu(packet->packet_length); ++ ++ // this may be a broken packet ++ if (size > GL_MAX_PACKET_LEN) { ++ netdev_dbg(dev->net, "genelink: invalid rx length %d\n", ++ size); ++ return 0; ++ } ++ ++ // allocate the skb for the individual packet ++ gl_skb = alloc_skb(size, GFP_ATOMIC); ++ if (gl_skb) { ++ ++ // copy the packet data to the new skb ++ memcpy(skb_put(gl_skb, size), ++ packet->packet_data, size); ++ usbnet_skb_return(dev, gl_skb); ++ } ++ ++ // advance to the next packet ++ packet = (struct gl_packet *)&packet->packet_data[size]; ++ count--; ++ ++ // shift the data pointer to the next gl_packet ++ skb_pull(skb, size + 4); ++ } ++ ++ // skip the packet length field 4 bytes ++ skb_pull(skb, 4); ++ ++ if (skb->len > GL_MAX_PACKET_LEN) { ++ netdev_dbg(dev->net, "genelink: invalid rx length %d\n", ++ skb->len); ++ return 0; ++ } ++ return 1; ++} ++ ++static struct sk_buff * ++genelink_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) ++{ ++ int padlen; ++ int length = skb->len; ++ int headroom = skb_headroom(skb); ++ int tailroom = skb_tailroom(skb); ++ __le32 *packet_count; ++ __le32 *packet_len; ++ ++ // FIXME: magic numbers, bleech ++ padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1; ++ ++ if ((!skb_cloned(skb)) ++ && ((headroom + tailroom) >= (padlen + (4 + 4*1)))) { ++ if ((headroom < (4 + 4*1)) || (tailroom < padlen)) { ++ skb->data = memmove(skb->head + (4 + 4*1), ++ skb->data, skb->len); ++ skb_set_tail_pointer(skb, skb->len); ++ } ++ } else { ++ struct sk_buff *skb2; ++ skb2 = skb_copy_expand(skb, (4 + 4*1) , padlen, flags); ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ if (!skb) ++ return NULL; ++ } ++ ++ // attach the packet count to the header ++ packet_count = (__le32 *) skb_push(skb, (4 + 4*1)); ++ packet_len = packet_count + 1; ++ ++ *packet_count = cpu_to_le32(1); ++ *packet_len = cpu_to_le32(length); ++ ++ // add padding byte ++ if ((skb->len % dev->maxpacket) == 0) ++ skb_put(skb, 1); ++ ++ return skb; ++} ++ ++static int genelink_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ dev->hard_mtu = GL_RCV_BUF_SIZE; ++ dev->net->hard_header_len += 4; ++ dev->in = usb_rcvbulkpipe(dev->udev, dev->driver_info->in); ++ dev->out = usb_sndbulkpipe(dev->udev, dev->driver_info->out); ++ return 0; ++} ++ ++static const struct driver_info genelink_info = { ++ .description = "Genesys GeneLink", ++ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_GL | FLAG_NO_SETINT, ++ .bind = genelink_bind, ++ .rx_fixup = genelink_rx_fixup, ++ .tx_fixup = genelink_tx_fixup, ++ ++ .in = 1, .out = 2, ++ ++#ifdef GENELINK_ACK ++ .check_connect =genelink_check_connect, ++#endif ++}; ++ ++static const struct usb_device_id products [] = { ++ ++{ ++ USB_DEVICE(0x05e3, 0x0502), // GL620USB-A ++ .driver_info = (unsigned long) &genelink_info, ++}, ++ /* NOT: USB_DEVICE(0x05e3, 0x0501), // GL620USB ++ * that's half duplex, not currently supported ++ */ ++ { }, // END ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver gl620a_driver = { ++ .name = "gl620a", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(gl620a_driver); ++ ++MODULE_AUTHOR("Jiun-Jie Huang"); ++MODULE_DESCRIPTION("GL620-USB-A Host-to-Host Link cables"); ++MODULE_LICENSE("GPL"); ++ +diff -Naur backports-3.18.1-1.org/drivers/net/usb/hso.c backports-3.18.1-1/drivers/net/usb/hso.c +--- backports-3.18.1-1.org/drivers/net/usb/hso.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/hso.c 2015-01-03 13:42:25.000000000 +0100 +@@ -0,0 +1,3326 @@ ++/****************************************************************************** ++ * ++ * Driver for Option High Speed Mobile Devices. ++ * ++ * Copyright (C) 2008 Option International ++ * Filip Aben ++ * Denis Joseph Barrow ++ * Jan Dumon ++ * Copyright (C) 2007 Andrew Bird (Sphere Systems Ltd) ++ * ++ * Copyright (C) 2008 Greg Kroah-Hartman ++ * Copyright (C) 2008 Novell, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA ++ * ++ * ++ *****************************************************************************/ ++ ++/****************************************************************************** ++ * ++ * Description of the device: ++ * ++ * Interface 0: Contains the IP network interface on the bulk end points. ++ * The multiplexed serial ports are using the interrupt and ++ * control endpoints. ++ * Interrupt contains a bitmap telling which multiplexed ++ * serialport needs servicing. ++ * ++ * Interface 1: Diagnostics port, uses bulk only, do not submit urbs until the ++ * port is opened, as this have a huge impact on the network port ++ * throughput. ++ * ++ * Interface 2: Standard modem interface - circuit switched interface, this ++ * can be used to make a standard ppp connection however it ++ * should not be used in conjunction with the IP network interface ++ * enabled for USB performance reasons i.e. if using this set ++ * ideally disable_net=1. ++ * ++ *****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MOD_AUTHOR "Option Wireless" ++#define MOD_DESCRIPTION "USB High Speed Option driver" ++#define MOD_LICENSE "GPL" ++ ++#define HSO_MAX_NET_DEVICES 10 ++#define HSO__MAX_MTU 2048 ++#define DEFAULT_MTU 1500 ++#define DEFAULT_MRU 1500 ++ ++#define CTRL_URB_RX_SIZE 1024 ++#define CTRL_URB_TX_SIZE 64 ++ ++#define BULK_URB_RX_SIZE 4096 ++#define BULK_URB_TX_SIZE 8192 ++ ++#define MUX_BULK_RX_BUF_SIZE HSO__MAX_MTU ++#define MUX_BULK_TX_BUF_SIZE HSO__MAX_MTU ++#define MUX_BULK_RX_BUF_COUNT 4 ++#define USB_TYPE_OPTION_VENDOR 0x20 ++ ++/* These definitions are used with the struct hso_net flags element */ ++/* - use *_bit operations on it. (bit indices not values.) */ ++#define HSO_NET_RUNNING 0 ++ ++#define HSO_NET_TX_TIMEOUT (HZ*10) ++ ++#define HSO_SERIAL_MAGIC 0x48534f31 ++ ++/* Number of ttys to handle */ ++#define HSO_SERIAL_TTY_MINORS 256 ++ ++#define MAX_RX_URBS 2 ++ ++/*****************************************************************************/ ++/* Debugging functions */ ++/*****************************************************************************/ ++#define D__(lvl_, fmt, arg...) \ ++ do { \ ++ printk(lvl_ "[%d:%s]: " fmt "\n", \ ++ __LINE__, __func__, ## arg); \ ++ } while (0) ++ ++#define D_(lvl, args...) \ ++ do { \ ++ if (lvl & debug) \ ++ D__(KERN_INFO, args); \ ++ } while (0) ++ ++#define D1(args...) D_(0x01, ##args) ++#define D2(args...) D_(0x02, ##args) ++#define D3(args...) D_(0x04, ##args) ++#define D4(args...) D_(0x08, ##args) ++#define D5(args...) D_(0x10, ##args) ++ ++/*****************************************************************************/ ++/* Enumerators */ ++/*****************************************************************************/ ++enum pkt_parse_state { ++ WAIT_IP, ++ WAIT_DATA, ++ WAIT_SYNC ++}; ++ ++/*****************************************************************************/ ++/* Structs */ ++/*****************************************************************************/ ++ ++struct hso_shared_int { ++ struct usb_endpoint_descriptor *intr_endp; ++ void *shared_intr_buf; ++ struct urb *shared_intr_urb; ++ struct usb_device *usb; ++ int use_count; ++ int ref_count; ++ struct mutex shared_int_lock; ++}; ++ ++struct hso_net { ++ struct hso_device *parent; ++ struct net_device *net; ++ struct rfkill *rfkill; ++ ++ struct usb_endpoint_descriptor *in_endp; ++ struct usb_endpoint_descriptor *out_endp; ++ ++ struct urb *mux_bulk_rx_urb_pool[MUX_BULK_RX_BUF_COUNT]; ++ struct urb *mux_bulk_tx_urb; ++ void *mux_bulk_rx_buf_pool[MUX_BULK_RX_BUF_COUNT]; ++ void *mux_bulk_tx_buf; ++ ++ struct sk_buff *skb_rx_buf; ++ struct sk_buff *skb_tx_buf; ++ ++ enum pkt_parse_state rx_parse_state; ++ spinlock_t net_lock; ++ ++ unsigned short rx_buf_size; ++ unsigned short rx_buf_missing; ++ struct iphdr rx_ip_hdr; ++ ++ unsigned long flags; ++}; ++ ++enum rx_ctrl_state{ ++ RX_IDLE, ++ RX_SENT, ++ RX_PENDING ++}; ++ ++#define BM_REQUEST_TYPE (0xa1) ++#define B_NOTIFICATION (0x20) ++#define W_VALUE (0x0) ++#define W_LENGTH (0x2) ++ ++#define B_OVERRUN (0x1<<6) ++#define B_PARITY (0x1<<5) ++#define B_FRAMING (0x1<<4) ++#define B_RING_SIGNAL (0x1<<3) ++#define B_BREAK (0x1<<2) ++#define B_TX_CARRIER (0x1<<1) ++#define B_RX_CARRIER (0x1<<0) ++ ++struct hso_serial_state_notification { ++ u8 bmRequestType; ++ u8 bNotification; ++ u16 wValue; ++ u16 wIndex; ++ u16 wLength; ++ u16 UART_state_bitmap; ++} __packed; ++ ++struct hso_tiocmget { ++ struct mutex mutex; ++ wait_queue_head_t waitq; ++ int intr_completed; ++ struct usb_endpoint_descriptor *endp; ++ struct urb *urb; ++ struct hso_serial_state_notification serial_state_notification; ++ u16 prev_UART_state_bitmap; ++ struct uart_icount icount; ++}; ++ ++ ++struct hso_serial { ++ struct hso_device *parent; ++ int magic; ++ u8 minor; ++ ++ struct hso_shared_int *shared_int; ++ ++ /* rx/tx urb could be either a bulk urb or a control urb depending ++ on which serial port it is used on. */ ++ struct urb *rx_urb[MAX_RX_URBS]; ++ u8 num_rx_urbs; ++ u8 *rx_data[MAX_RX_URBS]; ++ u16 rx_data_length; /* should contain allocated length */ ++ ++ struct urb *tx_urb; ++ u8 *tx_data; ++ u8 *tx_buffer; ++ u16 tx_data_length; /* should contain allocated length */ ++ u16 tx_data_count; ++ u16 tx_buffer_count; ++ struct usb_ctrlrequest ctrl_req_tx; ++ struct usb_ctrlrequest ctrl_req_rx; ++ ++ struct usb_endpoint_descriptor *in_endp; ++ struct usb_endpoint_descriptor *out_endp; ++ ++ enum rx_ctrl_state rx_state; ++ u8 rts_state; ++ u8 dtr_state; ++ unsigned tx_urb_used:1; ++ ++ struct tty_port port; ++ /* from usb_serial_port */ ++ spinlock_t serial_lock; ++ ++ int (*write_data) (struct hso_serial *serial); ++ struct hso_tiocmget *tiocmget; ++ /* Hacks required to get flow control ++ * working on the serial receive buffers ++ * so as not to drop characters on the floor. ++ */ ++ int curr_rx_urb_idx; ++ u8 rx_urb_filled[MAX_RX_URBS]; ++ struct tasklet_struct unthrottle_tasklet; ++}; ++ ++struct hso_device { ++ union { ++ struct hso_serial *dev_serial; ++ struct hso_net *dev_net; ++ } port_data; ++ ++ u32 port_spec; ++ ++ u8 is_active; ++ u8 usb_gone; ++ struct work_struct async_get_intf; ++ struct work_struct async_put_intf; ++ struct work_struct reset_device; ++ ++ struct usb_device *usb; ++ struct usb_interface *interface; ++ ++ struct device *dev; ++ struct kref ref; ++ struct mutex mutex; ++}; ++ ++/* Type of interface */ ++#define HSO_INTF_MASK 0xFF00 ++#define HSO_INTF_MUX 0x0100 ++#define HSO_INTF_BULK 0x0200 ++ ++/* Type of port */ ++#define HSO_PORT_MASK 0xFF ++#define HSO_PORT_NO_PORT 0x0 ++#define HSO_PORT_CONTROL 0x1 ++#define HSO_PORT_APP 0x2 ++#define HSO_PORT_GPS 0x3 ++#define HSO_PORT_PCSC 0x4 ++#define HSO_PORT_APP2 0x5 ++#define HSO_PORT_GPS_CONTROL 0x6 ++#define HSO_PORT_MSD 0x7 ++#define HSO_PORT_VOICE 0x8 ++#define HSO_PORT_DIAG2 0x9 ++#define HSO_PORT_DIAG 0x10 ++#define HSO_PORT_MODEM 0x11 ++#define HSO_PORT_NETWORK 0x12 ++ ++/* Additional device info */ ++#define HSO_INFO_MASK 0xFF000000 ++#define HSO_INFO_CRC_BUG 0x01000000 ++ ++/*****************************************************************************/ ++/* Prototypes */ ++/*****************************************************************************/ ++/* Serial driver functions */ ++static int hso_serial_tiocmset(struct tty_struct *tty, ++ unsigned int set, unsigned int clear); ++static void ctrl_callback(struct urb *urb); ++static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial); ++static void hso_kick_transmit(struct hso_serial *serial); ++/* Helper functions */ ++static int hso_mux_submit_intr_urb(struct hso_shared_int *mux_int, ++ struct usb_device *usb, gfp_t gfp); ++static void handle_usb_error(int status, const char *function, ++ struct hso_device *hso_dev); ++static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf, ++ int type, int dir); ++static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports); ++static void hso_free_interface(struct usb_interface *intf); ++static int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags); ++static int hso_stop_serial_device(struct hso_device *hso_dev); ++static int hso_start_net_device(struct hso_device *hso_dev); ++static void hso_free_shared_int(struct hso_shared_int *shared_int); ++static int hso_stop_net_device(struct hso_device *hso_dev); ++static void hso_serial_ref_free(struct kref *ref); ++static void hso_std_serial_read_bulk_callback(struct urb *urb); ++static int hso_mux_serial_read(struct hso_serial *serial); ++static void async_get_intf(struct work_struct *data); ++static void async_put_intf(struct work_struct *data); ++static int hso_put_activity(struct hso_device *hso_dev); ++static int hso_get_activity(struct hso_device *hso_dev); ++static void tiocmget_intr_callback(struct urb *urb); ++static void reset_device(struct work_struct *data); ++/*****************************************************************************/ ++/* Helping functions */ ++/*****************************************************************************/ ++ ++/* #define DEBUG */ ++ ++static inline struct hso_net *dev2net(struct hso_device *hso_dev) ++{ ++ return hso_dev->port_data.dev_net; ++} ++ ++static inline struct hso_serial *dev2ser(struct hso_device *hso_dev) ++{ ++ return hso_dev->port_data.dev_serial; ++} ++ ++/* Debugging functions */ ++#ifdef DEBUG ++static void dbg_dump(int line_count, const char *func_name, unsigned char *buf, ++ unsigned int len) ++{ ++ static char name[255]; ++ ++ sprintf(name, "hso[%d:%s]", line_count, func_name); ++ print_hex_dump_bytes(name, DUMP_PREFIX_NONE, buf, len); ++} ++ ++#define DUMP(buf_, len_) \ ++ dbg_dump(__LINE__, __func__, (unsigned char *)buf_, len_) ++ ++#define DUMP1(buf_, len_) \ ++ do { \ ++ if (0x01 & debug) \ ++ DUMP(buf_, len_); \ ++ } while (0) ++#else ++#define DUMP(buf_, len_) ++#define DUMP1(buf_, len_) ++#endif ++ ++/* module parameters */ ++static int debug; ++static int tty_major; ++static int disable_net; ++ ++/* driver info */ ++static const char driver_name[] = "hso"; ++static const char tty_filename[] = "ttyHS"; ++static const char *version = __FILE__ ": " MOD_AUTHOR; ++/* the usb driver itself (registered in hso_init) */ ++static struct usb_driver hso_driver; ++/* serial structures */ ++static struct tty_driver *tty_drv; ++static struct hso_device *serial_table[HSO_SERIAL_TTY_MINORS]; ++static struct hso_device *network_table[HSO_MAX_NET_DEVICES]; ++static spinlock_t serial_table_lock; ++ ++static const s32 default_port_spec[] = { ++ HSO_INTF_MUX | HSO_PORT_NETWORK, ++ HSO_INTF_BULK | HSO_PORT_DIAG, ++ HSO_INTF_BULK | HSO_PORT_MODEM, ++ 0 ++}; ++ ++static const s32 icon321_port_spec[] = { ++ HSO_INTF_MUX | HSO_PORT_NETWORK, ++ HSO_INTF_BULK | HSO_PORT_DIAG2, ++ HSO_INTF_BULK | HSO_PORT_MODEM, ++ HSO_INTF_BULK | HSO_PORT_DIAG, ++ 0 ++}; ++ ++#define default_port_device(vendor, product) \ ++ USB_DEVICE(vendor, product), \ ++ .driver_info = (kernel_ulong_t)default_port_spec ++ ++#define icon321_port_device(vendor, product) \ ++ USB_DEVICE(vendor, product), \ ++ .driver_info = (kernel_ulong_t)icon321_port_spec ++ ++/* list of devices we support */ ++static const struct usb_device_id hso_ids[] = { ++ {default_port_device(0x0af0, 0x6711)}, ++ {default_port_device(0x0af0, 0x6731)}, ++ {default_port_device(0x0af0, 0x6751)}, ++ {default_port_device(0x0af0, 0x6771)}, ++ {default_port_device(0x0af0, 0x6791)}, ++ {default_port_device(0x0af0, 0x6811)}, ++ {default_port_device(0x0af0, 0x6911)}, ++ {default_port_device(0x0af0, 0x6951)}, ++ {default_port_device(0x0af0, 0x6971)}, ++ {default_port_device(0x0af0, 0x7011)}, ++ {default_port_device(0x0af0, 0x7031)}, ++ {default_port_device(0x0af0, 0x7051)}, ++ {default_port_device(0x0af0, 0x7071)}, ++ {default_port_device(0x0af0, 0x7111)}, ++ {default_port_device(0x0af0, 0x7211)}, ++ {default_port_device(0x0af0, 0x7251)}, ++ {default_port_device(0x0af0, 0x7271)}, ++ {default_port_device(0x0af0, 0x7311)}, ++ {default_port_device(0x0af0, 0xc031)}, /* Icon-Edge */ ++ {icon321_port_device(0x0af0, 0xd013)}, /* Module HSxPA */ ++ {icon321_port_device(0x0af0, 0xd031)}, /* Icon-321 */ ++ {icon321_port_device(0x0af0, 0xd033)}, /* Icon-322 */ ++ {USB_DEVICE(0x0af0, 0x7301)}, /* GE40x */ ++ {USB_DEVICE(0x0af0, 0x7361)}, /* GE40x */ ++ {USB_DEVICE(0x0af0, 0x7381)}, /* GE40x */ ++ {USB_DEVICE(0x0af0, 0x7401)}, /* GI 0401 */ ++ {USB_DEVICE(0x0af0, 0x7501)}, /* GTM 382 */ ++ {USB_DEVICE(0x0af0, 0x7601)}, /* GE40x */ ++ {USB_DEVICE(0x0af0, 0x7701)}, ++ {USB_DEVICE(0x0af0, 0x7706)}, ++ {USB_DEVICE(0x0af0, 0x7801)}, ++ {USB_DEVICE(0x0af0, 0x7901)}, ++ {USB_DEVICE(0x0af0, 0x7A01)}, ++ {USB_DEVICE(0x0af0, 0x7A05)}, ++ {USB_DEVICE(0x0af0, 0x8200)}, ++ {USB_DEVICE(0x0af0, 0x8201)}, ++ {USB_DEVICE(0x0af0, 0x8300)}, ++ {USB_DEVICE(0x0af0, 0x8302)}, ++ {USB_DEVICE(0x0af0, 0x8304)}, ++ {USB_DEVICE(0x0af0, 0x8400)}, ++ {USB_DEVICE(0x0af0, 0x8600)}, ++ {USB_DEVICE(0x0af0, 0x8800)}, ++ {USB_DEVICE(0x0af0, 0x8900)}, ++ {USB_DEVICE(0x0af0, 0x9000)}, ++ {USB_DEVICE(0x0af0, 0x9200)}, /* Option GTM671WFS */ ++ {USB_DEVICE(0x0af0, 0xd035)}, ++ {USB_DEVICE(0x0af0, 0xd055)}, ++ {USB_DEVICE(0x0af0, 0xd155)}, ++ {USB_DEVICE(0x0af0, 0xd255)}, ++ {USB_DEVICE(0x0af0, 0xd057)}, ++ {USB_DEVICE(0x0af0, 0xd157)}, ++ {USB_DEVICE(0x0af0, 0xd257)}, ++ {USB_DEVICE(0x0af0, 0xd357)}, ++ {USB_DEVICE(0x0af0, 0xd058)}, ++ {USB_DEVICE(0x0af0, 0xc100)}, ++ {} ++}; ++MODULE_DEVICE_TABLE(usb, hso_ids); ++ ++/* Sysfs attribute */ ++static ssize_t hso_sysfs_show_porttype(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct hso_device *hso_dev = dev_get_drvdata(dev); ++ char *port_name; ++ ++ if (!hso_dev) ++ return 0; ++ ++ switch (hso_dev->port_spec & HSO_PORT_MASK) { ++ case HSO_PORT_CONTROL: ++ port_name = "Control"; ++ break; ++ case HSO_PORT_APP: ++ port_name = "Application"; ++ break; ++ case HSO_PORT_APP2: ++ port_name = "Application2"; ++ break; ++ case HSO_PORT_GPS: ++ port_name = "GPS"; ++ break; ++ case HSO_PORT_GPS_CONTROL: ++ port_name = "GPS Control"; ++ break; ++ case HSO_PORT_PCSC: ++ port_name = "PCSC"; ++ break; ++ case HSO_PORT_DIAG: ++ port_name = "Diagnostic"; ++ break; ++ case HSO_PORT_DIAG2: ++ port_name = "Diagnostic2"; ++ break; ++ case HSO_PORT_MODEM: ++ port_name = "Modem"; ++ break; ++ case HSO_PORT_NETWORK: ++ port_name = "Network"; ++ break; ++ default: ++ port_name = "Unknown"; ++ break; ++ } ++ ++ return sprintf(buf, "%s\n", port_name); ++} ++static DEVICE_ATTR(hsotype, S_IRUGO, hso_sysfs_show_porttype, NULL); ++ ++static int hso_urb_to_index(struct hso_serial *serial, struct urb *urb) ++{ ++ int idx; ++ ++ for (idx = 0; idx < serial->num_rx_urbs; idx++) ++ if (serial->rx_urb[idx] == urb) ++ return idx; ++ dev_err(serial->parent->dev, "hso_urb_to_index failed\n"); ++ return -1; ++} ++ ++/* converts mux value to a port spec value */ ++static u32 hso_mux_to_port(int mux) ++{ ++ u32 result; ++ ++ switch (mux) { ++ case 0x1: ++ result = HSO_PORT_CONTROL; ++ break; ++ case 0x2: ++ result = HSO_PORT_APP; ++ break; ++ case 0x4: ++ result = HSO_PORT_PCSC; ++ break; ++ case 0x8: ++ result = HSO_PORT_GPS; ++ break; ++ case 0x10: ++ result = HSO_PORT_APP2; ++ break; ++ default: ++ result = HSO_PORT_NO_PORT; ++ } ++ return result; ++} ++ ++/* converts port spec value to a mux value */ ++static u32 hso_port_to_mux(int port) ++{ ++ u32 result; ++ ++ switch (port & HSO_PORT_MASK) { ++ case HSO_PORT_CONTROL: ++ result = 0x0; ++ break; ++ case HSO_PORT_APP: ++ result = 0x1; ++ break; ++ case HSO_PORT_PCSC: ++ result = 0x2; ++ break; ++ case HSO_PORT_GPS: ++ result = 0x3; ++ break; ++ case HSO_PORT_APP2: ++ result = 0x4; ++ break; ++ default: ++ result = 0x0; ++ } ++ return result; ++} ++ ++static struct hso_serial *get_serial_by_shared_int_and_type( ++ struct hso_shared_int *shared_int, ++ int mux) ++{ ++ int i, port; ++ ++ port = hso_mux_to_port(mux); ++ ++ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { ++ if (serial_table[i] && ++ (dev2ser(serial_table[i])->shared_int == shared_int) && ++ ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) { ++ return dev2ser(serial_table[i]); ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct hso_serial *get_serial_by_index(unsigned index) ++{ ++ struct hso_serial *serial = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&serial_table_lock, flags); ++ if (serial_table[index]) ++ serial = dev2ser(serial_table[index]); ++ spin_unlock_irqrestore(&serial_table_lock, flags); ++ ++ return serial; ++} ++ ++static int get_free_serial_index(void) ++{ ++ int index; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&serial_table_lock, flags); ++ for (index = 0; index < HSO_SERIAL_TTY_MINORS; index++) { ++ if (serial_table[index] == NULL) { ++ spin_unlock_irqrestore(&serial_table_lock, flags); ++ return index; ++ } ++ } ++ spin_unlock_irqrestore(&serial_table_lock, flags); ++ ++ printk(KERN_ERR "%s: no free serial devices in table\n", __func__); ++ return -1; ++} ++ ++static void set_serial_by_index(unsigned index, struct hso_serial *serial) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&serial_table_lock, flags); ++ if (serial) ++ serial_table[index] = serial->parent; ++ else ++ serial_table[index] = NULL; ++ spin_unlock_irqrestore(&serial_table_lock, flags); ++} ++ ++static void handle_usb_error(int status, const char *function, ++ struct hso_device *hso_dev) ++{ ++ char *explanation; ++ ++ switch (status) { ++ case -ENODEV: ++ explanation = "no device"; ++ break; ++ case -ENOENT: ++ explanation = "endpoint not enabled"; ++ break; ++ case -EPIPE: ++ explanation = "endpoint stalled"; ++ break; ++ case -ENOSPC: ++ explanation = "not enough bandwidth"; ++ break; ++ case -ESHUTDOWN: ++ explanation = "device disabled"; ++ break; ++ case -EHOSTUNREACH: ++ explanation = "device suspended"; ++ break; ++ case -EINVAL: ++ case -EAGAIN: ++ case -EFBIG: ++ case -EMSGSIZE: ++ explanation = "internal error"; ++ break; ++ case -EILSEQ: ++ case -EPROTO: ++ case -ETIME: ++ case -ETIMEDOUT: ++ explanation = "protocol error"; ++ if (hso_dev) ++ schedule_work(&hso_dev->reset_device); ++ break; ++ default: ++ explanation = "unknown status"; ++ break; ++ } ++ ++ /* log a meaningful explanation of an USB status */ ++ D1("%s: received USB status - %s (%d)", function, explanation, status); ++} ++ ++/* Network interface functions */ ++ ++/* called when net interface is brought up by ifconfig */ ++static int hso_net_open(struct net_device *net) ++{ ++ struct hso_net *odev = netdev_priv(net); ++ unsigned long flags = 0; ++ ++ if (!odev) { ++ dev_err(&net->dev, "No net device !\n"); ++ return -ENODEV; ++ } ++ ++ odev->skb_tx_buf = NULL; ++ ++ /* setup environment */ ++ spin_lock_irqsave(&odev->net_lock, flags); ++ odev->rx_parse_state = WAIT_IP; ++ odev->rx_buf_size = 0; ++ odev->rx_buf_missing = sizeof(struct iphdr); ++ spin_unlock_irqrestore(&odev->net_lock, flags); ++ ++ /* We are up and running. */ ++ set_bit(HSO_NET_RUNNING, &odev->flags); ++ hso_start_net_device(odev->parent); ++ ++ /* Tell the kernel we are ready to start receiving from it */ ++ netif_start_queue(net); ++ ++ return 0; ++} ++ ++/* called when interface is brought down by ifconfig */ ++static int hso_net_close(struct net_device *net) ++{ ++ struct hso_net *odev = netdev_priv(net); ++ ++ /* we don't need the queue anymore */ ++ netif_stop_queue(net); ++ /* no longer running */ ++ clear_bit(HSO_NET_RUNNING, &odev->flags); ++ ++ hso_stop_net_device(odev->parent); ++ ++ /* done */ ++ return 0; ++} ++ ++/* USB tells is xmit done, we should start the netqueue again */ ++static void write_bulk_callback(struct urb *urb) ++{ ++ struct hso_net *odev = urb->context; ++ int status = urb->status; ++ ++ /* Sanity check */ ++ if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) { ++ dev_err(&urb->dev->dev, "%s: device not running\n", __func__); ++ return; ++ } ++ ++ /* Do we still have a valid kernel network device? */ ++ if (!netif_device_present(odev->net)) { ++ dev_err(&urb->dev->dev, "%s: net device not present\n", ++ __func__); ++ return; ++ } ++ ++ /* log status, but don't act on it, we don't need to resubmit anything ++ * anyhow */ ++ if (status) ++ handle_usb_error(status, __func__, odev->parent); ++ ++ hso_put_activity(odev->parent); ++ ++ /* Tell the network interface we are ready for another frame */ ++ netif_wake_queue(odev->net); ++} ++ ++/* called by kernel when we need to transmit a packet */ ++static netdev_tx_t hso_net_start_xmit(struct sk_buff *skb, ++ struct net_device *net) ++{ ++ struct hso_net *odev = netdev_priv(net); ++ int result; ++ ++ /* Tell the kernel, "No more frames 'til we are done with this one." */ ++ netif_stop_queue(net); ++ if (hso_get_activity(odev->parent) == -EAGAIN) { ++ odev->skb_tx_buf = skb; ++ return NETDEV_TX_OK; ++ } ++ ++ /* log if asked */ ++ DUMP1(skb->data, skb->len); ++ /* Copy it from kernel memory to OUR memory */ ++ memcpy(odev->mux_bulk_tx_buf, skb->data, skb->len); ++ D1("len: %d/%d", skb->len, MUX_BULK_TX_BUF_SIZE); ++ ++ /* Fill in the URB for shipping it out. */ ++ usb_fill_bulk_urb(odev->mux_bulk_tx_urb, ++ odev->parent->usb, ++ usb_sndbulkpipe(odev->parent->usb, ++ odev->out_endp-> ++ bEndpointAddress & 0x7F), ++ odev->mux_bulk_tx_buf, skb->len, write_bulk_callback, ++ odev); ++ ++ /* Deal with the Zero Length packet problem, I hope */ ++ odev->mux_bulk_tx_urb->transfer_flags |= URB_ZERO_PACKET; ++ ++ /* Send the URB on its merry way. */ ++ result = usb_submit_urb(odev->mux_bulk_tx_urb, GFP_ATOMIC); ++ if (result) { ++ dev_warn(&odev->parent->interface->dev, ++ "failed mux_bulk_tx_urb %d\n", result); ++ net->stats.tx_errors++; ++ netif_start_queue(net); ++ } else { ++ net->stats.tx_packets++; ++ net->stats.tx_bytes += skb->len; ++ } ++ dev_kfree_skb(skb); ++ /* we're done */ ++ return NETDEV_TX_OK; ++} ++ ++static const struct ethtool_ops ops = { ++ .get_link = ethtool_op_get_link ++}; ++ ++/* called when a packet did not ack after watchdogtimeout */ ++static void hso_net_tx_timeout(struct net_device *net) ++{ ++ struct hso_net *odev = netdev_priv(net); ++ ++ if (!odev) ++ return; ++ ++ /* Tell syslog we are hosed. */ ++ dev_warn(&net->dev, "Tx timed out.\n"); ++ ++ /* Tear the waiting frame off the list */ ++ if (odev->mux_bulk_tx_urb && ++ (odev->mux_bulk_tx_urb->status == -EINPROGRESS)) ++ usb_unlink_urb(odev->mux_bulk_tx_urb); ++ ++ /* Update statistics */ ++ net->stats.tx_errors++; ++} ++ ++/* make a real packet from the received USB buffer */ ++static void packetizeRx(struct hso_net *odev, unsigned char *ip_pkt, ++ unsigned int count, unsigned char is_eop) ++{ ++ unsigned short temp_bytes; ++ unsigned short buffer_offset = 0; ++ unsigned short frame_len; ++ unsigned char *tmp_rx_buf; ++ ++ /* log if needed */ ++ D1("Rx %d bytes", count); ++ DUMP(ip_pkt, min(128, (int)count)); ++ ++ while (count) { ++ switch (odev->rx_parse_state) { ++ case WAIT_IP: ++ /* waiting for IP header. */ ++ /* wanted bytes - size of ip header */ ++ temp_bytes = ++ (count < ++ odev->rx_buf_missing) ? count : odev-> ++ rx_buf_missing; ++ ++ memcpy(((unsigned char *)(&odev->rx_ip_hdr)) + ++ odev->rx_buf_size, ip_pkt + buffer_offset, ++ temp_bytes); ++ ++ odev->rx_buf_size += temp_bytes; ++ buffer_offset += temp_bytes; ++ odev->rx_buf_missing -= temp_bytes; ++ count -= temp_bytes; ++ ++ if (!odev->rx_buf_missing) { ++ /* header is complete allocate an sk_buffer and ++ * continue to WAIT_DATA */ ++ frame_len = ntohs(odev->rx_ip_hdr.tot_len); ++ ++ if ((frame_len > DEFAULT_MRU) || ++ (frame_len < sizeof(struct iphdr))) { ++ dev_err(&odev->net->dev, ++ "Invalid frame (%d) length\n", ++ frame_len); ++ odev->rx_parse_state = WAIT_SYNC; ++ continue; ++ } ++ /* Allocate an sk_buff */ ++ odev->skb_rx_buf = netdev_alloc_skb(odev->net, ++ frame_len); ++ if (!odev->skb_rx_buf) { ++ /* We got no receive buffer. */ ++ D1("could not allocate memory"); ++ odev->rx_parse_state = WAIT_SYNC; ++ return; ++ } ++ ++ /* Copy what we got so far. make room for iphdr ++ * after tail. */ ++ tmp_rx_buf = ++ skb_put(odev->skb_rx_buf, ++ sizeof(struct iphdr)); ++ memcpy(tmp_rx_buf, (char *)&(odev->rx_ip_hdr), ++ sizeof(struct iphdr)); ++ ++ /* ETH_HLEN */ ++ odev->rx_buf_size = sizeof(struct iphdr); ++ ++ /* Filip actually use .tot_len */ ++ odev->rx_buf_missing = ++ frame_len - sizeof(struct iphdr); ++ odev->rx_parse_state = WAIT_DATA; ++ } ++ break; ++ ++ case WAIT_DATA: ++ temp_bytes = (count < odev->rx_buf_missing) ++ ? count : odev->rx_buf_missing; ++ ++ /* Copy the rest of the bytes that are left in the ++ * buffer into the waiting sk_buf. */ ++ /* Make room for temp_bytes after tail. */ ++ tmp_rx_buf = skb_put(odev->skb_rx_buf, temp_bytes); ++ memcpy(tmp_rx_buf, ip_pkt + buffer_offset, temp_bytes); ++ ++ odev->rx_buf_missing -= temp_bytes; ++ count -= temp_bytes; ++ buffer_offset += temp_bytes; ++ odev->rx_buf_size += temp_bytes; ++ if (!odev->rx_buf_missing) { ++ /* Packet is complete. Inject into stack. */ ++ /* We have IP packet here */ ++ odev->skb_rx_buf->protocol = cpu_to_be16(ETH_P_IP); ++ skb_reset_mac_header(odev->skb_rx_buf); ++ ++ /* Ship it off to the kernel */ ++ netif_rx(odev->skb_rx_buf); ++ /* No longer our buffer. */ ++ odev->skb_rx_buf = NULL; ++ ++ /* update out statistics */ ++ odev->net->stats.rx_packets++; ++ ++ odev->net->stats.rx_bytes += odev->rx_buf_size; ++ ++ odev->rx_buf_size = 0; ++ odev->rx_buf_missing = sizeof(struct iphdr); ++ odev->rx_parse_state = WAIT_IP; ++ } ++ break; ++ ++ case WAIT_SYNC: ++ D1(" W_S"); ++ count = 0; ++ break; ++ default: ++ D1(" "); ++ count--; ++ break; ++ } ++ } ++ ++ /* Recovery mechanism for WAIT_SYNC state. */ ++ if (is_eop) { ++ if (odev->rx_parse_state == WAIT_SYNC) { ++ odev->rx_parse_state = WAIT_IP; ++ odev->rx_buf_size = 0; ++ odev->rx_buf_missing = sizeof(struct iphdr); ++ } ++ } ++} ++ ++static void fix_crc_bug(struct urb *urb, __le16 max_packet_size) ++{ ++ static const u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF }; ++ u32 rest = urb->actual_length % le16_to_cpu(max_packet_size); ++ ++ if (((rest == 5) || (rest == 6)) && ++ !memcmp(((u8 *)urb->transfer_buffer) + urb->actual_length - 4, ++ crc_check, 4)) { ++ urb->actual_length -= 4; ++ } ++} ++ ++/* Moving data from usb to kernel (in interrupt state) */ ++static void read_bulk_callback(struct urb *urb) ++{ ++ struct hso_net *odev = urb->context; ++ struct net_device *net; ++ int result; ++ int status = urb->status; ++ ++ /* is al ok? (Filip: Who's Al ?) */ ++ if (status) { ++ handle_usb_error(status, __func__, odev->parent); ++ return; ++ } ++ ++ /* Sanity check */ ++ if (!odev || !test_bit(HSO_NET_RUNNING, &odev->flags)) { ++ D1("BULK IN callback but driver is not active!"); ++ return; ++ } ++ usb_mark_last_busy(urb->dev); ++ ++ net = odev->net; ++ ++ if (!netif_device_present(net)) { ++ /* Somebody killed our network interface... */ ++ return; ++ } ++ ++ if (odev->parent->port_spec & HSO_INFO_CRC_BUG) ++ fix_crc_bug(urb, odev->in_endp->wMaxPacketSize); ++ ++ /* do we even have a packet? */ ++ if (urb->actual_length) { ++ /* Handle the IP stream, add header and push it onto network ++ * stack if the packet is complete. */ ++ spin_lock(&odev->net_lock); ++ packetizeRx(odev, urb->transfer_buffer, urb->actual_length, ++ (urb->transfer_buffer_length > ++ urb->actual_length) ? 1 : 0); ++ spin_unlock(&odev->net_lock); ++ } ++ ++ /* We are done with this URB, resubmit it. Prep the USB to wait for ++ * another frame. Reuse same as received. */ ++ usb_fill_bulk_urb(urb, ++ odev->parent->usb, ++ usb_rcvbulkpipe(odev->parent->usb, ++ odev->in_endp-> ++ bEndpointAddress & 0x7F), ++ urb->transfer_buffer, MUX_BULK_RX_BUF_SIZE, ++ read_bulk_callback, odev); ++ ++ /* Give this to the USB subsystem so it can tell us when more data ++ * arrives. */ ++ result = usb_submit_urb(urb, GFP_ATOMIC); ++ if (result) ++ dev_warn(&odev->parent->interface->dev, ++ "%s failed submit mux_bulk_rx_urb %d\n", __func__, ++ result); ++} ++ ++/* Serial driver functions */ ++ ++static void hso_init_termios(struct ktermios *termios) ++{ ++ /* ++ * The default requirements for this device are: ++ */ ++ termios->c_iflag &= ++ ~(IGNBRK /* disable ignore break */ ++ | BRKINT /* disable break causes interrupt */ ++ | PARMRK /* disable mark parity errors */ ++ | ISTRIP /* disable clear high bit of input characters */ ++ | INLCR /* disable translate NL to CR */ ++ | IGNCR /* disable ignore CR */ ++ | ICRNL /* disable translate CR to NL */ ++ | IXON); /* disable enable XON/XOFF flow control */ ++ ++ /* disable postprocess output characters */ ++ termios->c_oflag &= ~OPOST; ++ ++ termios->c_lflag &= ++ ~(ECHO /* disable echo input characters */ ++ | ECHONL /* disable echo new line */ ++ | ICANON /* disable erase, kill, werase, and rprnt ++ special characters */ ++ | ISIG /* disable interrupt, quit, and suspend special ++ characters */ ++ | IEXTEN); /* disable non-POSIX special characters */ ++ ++ termios->c_cflag &= ++ ~(CSIZE /* no size */ ++ | PARENB /* disable parity bit */ ++ | CBAUD /* clear current baud rate */ ++ | CBAUDEX); /* clear current buad rate */ ++ ++ termios->c_cflag |= CS8; /* character size 8 bits */ ++ ++ /* baud rate 115200 */ ++ tty_termios_encode_baud_rate(termios, 115200, 115200); ++} ++ ++static void _hso_serial_set_termios(struct tty_struct *tty, ++ struct ktermios *old) ++{ ++ struct hso_serial *serial = tty->driver_data; ++ ++ if (!serial) { ++ printk(KERN_ERR "%s: no tty structures", __func__); ++ return; ++ } ++ ++ D4("port %d", serial->minor); ++ ++ /* ++ * Fix up unsupported bits ++ */ ++ tty->termios.c_iflag &= ~IXON; /* disable enable XON/XOFF flow control */ ++ ++ tty->termios.c_cflag &= ++ ~(CSIZE /* no size */ ++ | PARENB /* disable parity bit */ ++ | CBAUD /* clear current baud rate */ ++ | CBAUDEX); /* clear current buad rate */ ++ ++ tty->termios.c_cflag |= CS8; /* character size 8 bits */ ++ ++ /* baud rate 115200 */ ++ tty_encode_baud_rate(tty, 115200, 115200); ++} ++ ++static void hso_resubmit_rx_bulk_urb(struct hso_serial *serial, struct urb *urb) ++{ ++ int result; ++ /* We are done with this URB, resubmit it. Prep the USB to wait for ++ * another frame */ ++ usb_fill_bulk_urb(urb, serial->parent->usb, ++ usb_rcvbulkpipe(serial->parent->usb, ++ serial->in_endp-> ++ bEndpointAddress & 0x7F), ++ urb->transfer_buffer, serial->rx_data_length, ++ hso_std_serial_read_bulk_callback, serial); ++ /* Give this to the USB subsystem so it can tell us when more data ++ * arrives. */ ++ result = usb_submit_urb(urb, GFP_ATOMIC); ++ if (result) { ++ dev_err(&urb->dev->dev, "%s failed submit serial rx_urb %d\n", ++ __func__, result); ++ } ++} ++ ++ ++ ++ ++static void put_rxbuf_data_and_resubmit_bulk_urb(struct hso_serial *serial) ++{ ++ int count; ++ struct urb *curr_urb; ++ ++ while (serial->rx_urb_filled[serial->curr_rx_urb_idx]) { ++ curr_urb = serial->rx_urb[serial->curr_rx_urb_idx]; ++ count = put_rxbuf_data(curr_urb, serial); ++ if (count == -1) ++ return; ++ if (count == 0) { ++ serial->curr_rx_urb_idx++; ++ if (serial->curr_rx_urb_idx >= serial->num_rx_urbs) ++ serial->curr_rx_urb_idx = 0; ++ hso_resubmit_rx_bulk_urb(serial, curr_urb); ++ } ++ } ++} ++ ++static void put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial *serial) ++{ ++ int count = 0; ++ struct urb *urb; ++ ++ urb = serial->rx_urb[0]; ++ if (atomic_read(&serial->port.count) > 0) { ++ count = put_rxbuf_data(urb, serial); ++ if (count == -1) ++ return; ++ } ++ /* Re issue a read as long as we receive data. */ ++ ++ if (count == 0 && ((urb->actual_length != 0) || ++ (serial->rx_state == RX_PENDING))) { ++ serial->rx_state = RX_SENT; ++ hso_mux_serial_read(serial); ++ } else ++ serial->rx_state = RX_IDLE; ++} ++ ++ ++/* read callback for Diag and CS port */ ++static void hso_std_serial_read_bulk_callback(struct urb *urb) ++{ ++ struct hso_serial *serial = urb->context; ++ int status = urb->status; ++ ++ D4("\n--- Got serial_read_bulk callback %02x ---", status); ++ ++ /* sanity check */ ++ if (!serial) { ++ D1("serial == NULL"); ++ return; ++ } ++ if (status) { ++ handle_usb_error(status, __func__, serial->parent); ++ return; ++ } ++ ++ D1("Actual length = %d\n", urb->actual_length); ++ DUMP1(urb->transfer_buffer, urb->actual_length); ++ ++ /* Anyone listening? */ ++ if (atomic_read(&serial->port.count) == 0) ++ return; ++ ++ if (serial->parent->port_spec & HSO_INFO_CRC_BUG) ++ fix_crc_bug(urb, serial->in_endp->wMaxPacketSize); ++ /* Valid data, handle RX data */ ++ spin_lock(&serial->serial_lock); ++ serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 1; ++ put_rxbuf_data_and_resubmit_bulk_urb(serial); ++ spin_unlock(&serial->serial_lock); ++} ++ ++/* ++ * This needs to be a tasklet otherwise we will ++ * end up recursively calling this function. ++ */ ++static void hso_unthrottle_tasklet(struct hso_serial *serial) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&serial->serial_lock, flags); ++ if ((serial->parent->port_spec & HSO_INTF_MUX)) ++ put_rxbuf_data_and_resubmit_ctrl_urb(serial); ++ else ++ put_rxbuf_data_and_resubmit_bulk_urb(serial); ++ spin_unlock_irqrestore(&serial->serial_lock, flags); ++} ++ ++static void hso_unthrottle(struct tty_struct *tty) ++{ ++ struct hso_serial *serial = tty->driver_data; ++ ++ tasklet_hi_schedule(&serial->unthrottle_tasklet); ++} ++ ++/* open the requested serial port */ ++static int hso_serial_open(struct tty_struct *tty, struct file *filp) ++{ ++ struct hso_serial *serial = get_serial_by_index(tty->index); ++ int result; ++ ++ /* sanity check */ ++ if (serial == NULL || serial->magic != HSO_SERIAL_MAGIC) { ++ WARN_ON(1); ++ tty->driver_data = NULL; ++ D1("Failed to open port"); ++ return -ENODEV; ++ } ++ ++ mutex_lock(&serial->parent->mutex); ++ result = usb_autopm_get_interface(serial->parent->interface); ++ if (result < 0) ++ goto err_out; ++ ++ D1("Opening %d", serial->minor); ++ kref_get(&serial->parent->ref); ++ ++ /* setup */ ++ tty->driver_data = serial; ++ tty_port_tty_set(&serial->port, tty); ++ ++ /* check for port already opened, if not set the termios */ ++ if (atomic_inc_return(&serial->port.count) == 1) { ++ serial->rx_state = RX_IDLE; ++ /* Force default termio settings */ ++ _hso_serial_set_termios(tty, NULL); ++ tasklet_init(&serial->unthrottle_tasklet, ++ (void (*)(unsigned long))hso_unthrottle_tasklet, ++ (unsigned long)serial); ++ result = hso_start_serial_device(serial->parent, GFP_KERNEL); ++ if (result) { ++ hso_stop_serial_device(serial->parent); ++ atomic_dec(&serial->port.count); ++ kref_put(&serial->parent->ref, hso_serial_ref_free); ++ } ++ } else { ++ D1("Port was already open"); ++ } ++ ++ usb_autopm_put_interface(serial->parent->interface); ++ ++ /* done */ ++ if (result) ++ hso_serial_tiocmset(tty, TIOCM_RTS | TIOCM_DTR, 0); ++err_out: ++ mutex_unlock(&serial->parent->mutex); ++ return result; ++} ++ ++/* close the requested serial port */ ++static void hso_serial_close(struct tty_struct *tty, struct file *filp) ++{ ++ struct hso_serial *serial = tty->driver_data; ++ u8 usb_gone; ++ ++ D1("Closing serial port"); ++ ++ /* Open failed, no close cleanup required */ ++ if (serial == NULL) ++ return; ++ ++ mutex_lock(&serial->parent->mutex); ++ usb_gone = serial->parent->usb_gone; ++ ++ if (!usb_gone) ++ usb_autopm_get_interface(serial->parent->interface); ++ ++ /* reset the rts and dtr */ ++ /* do the actual close */ ++ atomic_dec(&serial->port.count); ++ ++ if (atomic_read(&serial->port.count) <= 0) { ++ atomic_set(&serial->port.count, 0); ++ tty_port_tty_set(&serial->port, NULL); ++ if (!usb_gone) ++ hso_stop_serial_device(serial->parent); ++ tasklet_kill(&serial->unthrottle_tasklet); ++ } ++ ++ if (!usb_gone) ++ usb_autopm_put_interface(serial->parent->interface); ++ ++ mutex_unlock(&serial->parent->mutex); ++ ++ kref_put(&serial->parent->ref, hso_serial_ref_free); ++} ++ ++/* close the requested serial port */ ++static int hso_serial_write(struct tty_struct *tty, const unsigned char *buf, ++ int count) ++{ ++ struct hso_serial *serial = tty->driver_data; ++ int space, tx_bytes; ++ unsigned long flags; ++ ++ /* sanity check */ ++ if (serial == NULL) { ++ printk(KERN_ERR "%s: serial is NULL\n", __func__); ++ return -ENODEV; ++ } ++ ++ spin_lock_irqsave(&serial->serial_lock, flags); ++ ++ space = serial->tx_data_length - serial->tx_buffer_count; ++ tx_bytes = (count < space) ? count : space; ++ ++ if (!tx_bytes) ++ goto out; ++ ++ memcpy(serial->tx_buffer + serial->tx_buffer_count, buf, tx_bytes); ++ serial->tx_buffer_count += tx_bytes; ++ ++out: ++ spin_unlock_irqrestore(&serial->serial_lock, flags); ++ ++ hso_kick_transmit(serial); ++ /* done */ ++ return tx_bytes; ++} ++ ++/* how much room is there for writing */ ++static int hso_serial_write_room(struct tty_struct *tty) ++{ ++ struct hso_serial *serial = tty->driver_data; ++ int room; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&serial->serial_lock, flags); ++ room = serial->tx_data_length - serial->tx_buffer_count; ++ spin_unlock_irqrestore(&serial->serial_lock, flags); ++ ++ /* return free room */ ++ return room; ++} ++ ++/* setup the term */ ++static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old) ++{ ++ struct hso_serial *serial = tty->driver_data; ++ unsigned long flags; ++ ++ if (old) ++ D5("Termios called with: cflags new[%d] - old[%d]", ++ tty->termios.c_cflag, old->c_cflag); ++ ++ /* the actual setup */ ++ spin_lock_irqsave(&serial->serial_lock, flags); ++ if (atomic_read(&serial->port.count)) ++ _hso_serial_set_termios(tty, old); ++ else ++ tty->termios = *old; ++ spin_unlock_irqrestore(&serial->serial_lock, flags); ++ ++ /* done */ ++} ++ ++/* how many characters in the buffer */ ++static int hso_serial_chars_in_buffer(struct tty_struct *tty) ++{ ++ struct hso_serial *serial = tty->driver_data; ++ int chars; ++ unsigned long flags; ++ ++ /* sanity check */ ++ if (serial == NULL) ++ return 0; ++ ++ spin_lock_irqsave(&serial->serial_lock, flags); ++ chars = serial->tx_buffer_count; ++ spin_unlock_irqrestore(&serial->serial_lock, flags); ++ ++ return chars; ++} ++static int tiocmget_submit_urb(struct hso_serial *serial, ++ struct hso_tiocmget *tiocmget, ++ struct usb_device *usb) ++{ ++ int result; ++ ++ if (serial->parent->usb_gone) ++ return -ENODEV; ++ usb_fill_int_urb(tiocmget->urb, usb, ++ usb_rcvintpipe(usb, ++ tiocmget->endp-> ++ bEndpointAddress & 0x7F), ++ &tiocmget->serial_state_notification, ++ sizeof(struct hso_serial_state_notification), ++ tiocmget_intr_callback, serial, ++ tiocmget->endp->bInterval); ++ result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC); ++ if (result) { ++ dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__, ++ result); ++ } ++ return result; ++ ++} ++ ++static void tiocmget_intr_callback(struct urb *urb) ++{ ++ struct hso_serial *serial = urb->context; ++ struct hso_tiocmget *tiocmget; ++ int status = urb->status; ++ u16 UART_state_bitmap, prev_UART_state_bitmap; ++ struct uart_icount *icount; ++ struct hso_serial_state_notification *serial_state_notification; ++ struct usb_device *usb; ++ int if_num; ++ ++ /* Sanity checks */ ++ if (!serial) ++ return; ++ if (status) { ++ handle_usb_error(status, __func__, serial->parent); ++ return; ++ } ++ ++ /* tiocmget is only supported on HSO_PORT_MODEM */ ++ tiocmget = serial->tiocmget; ++ if (!tiocmget) ++ return; ++ BUG_ON((serial->parent->port_spec & HSO_PORT_MASK) != HSO_PORT_MODEM); ++ ++ usb = serial->parent->usb; ++ if_num = serial->parent->interface->altsetting->desc.bInterfaceNumber; ++ ++ /* wIndex should be the USB interface number of the port to which the ++ * notification applies, which should always be the Modem port. ++ */ ++ serial_state_notification = &tiocmget->serial_state_notification; ++ if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE || ++ serial_state_notification->bNotification != B_NOTIFICATION || ++ le16_to_cpu(serial_state_notification->wValue) != W_VALUE || ++ le16_to_cpu(serial_state_notification->wIndex) != if_num || ++ le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) { ++ dev_warn(&usb->dev, ++ "hso received invalid serial state notification\n"); ++ DUMP(serial_state_notification, ++ sizeof(struct hso_serial_state_notification)); ++ } else { ++ ++ UART_state_bitmap = le16_to_cpu(serial_state_notification-> ++ UART_state_bitmap); ++ prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap; ++ icount = &tiocmget->icount; ++ spin_lock(&serial->serial_lock); ++ if ((UART_state_bitmap & B_OVERRUN) != ++ (prev_UART_state_bitmap & B_OVERRUN)) ++ icount->parity++; ++ if ((UART_state_bitmap & B_PARITY) != ++ (prev_UART_state_bitmap & B_PARITY)) ++ icount->parity++; ++ if ((UART_state_bitmap & B_FRAMING) != ++ (prev_UART_state_bitmap & B_FRAMING)) ++ icount->frame++; ++ if ((UART_state_bitmap & B_RING_SIGNAL) && ++ !(prev_UART_state_bitmap & B_RING_SIGNAL)) ++ icount->rng++; ++ if ((UART_state_bitmap & B_BREAK) != ++ (prev_UART_state_bitmap & B_BREAK)) ++ icount->brk++; ++ if ((UART_state_bitmap & B_TX_CARRIER) != ++ (prev_UART_state_bitmap & B_TX_CARRIER)) ++ icount->dsr++; ++ if ((UART_state_bitmap & B_RX_CARRIER) != ++ (prev_UART_state_bitmap & B_RX_CARRIER)) ++ icount->dcd++; ++ tiocmget->prev_UART_state_bitmap = UART_state_bitmap; ++ spin_unlock(&serial->serial_lock); ++ tiocmget->intr_completed = 1; ++ wake_up_interruptible(&tiocmget->waitq); ++ } ++ memset(serial_state_notification, 0, ++ sizeof(struct hso_serial_state_notification)); ++ tiocmget_submit_urb(serial, ++ tiocmget, ++ serial->parent->usb); ++} ++ ++/* ++ * next few functions largely stolen from drivers/serial/serial_core.c ++ */ ++/* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change ++ * - mask passed in arg for lines of interest ++ * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) ++ * Caller should use TIOCGICOUNT to see which one it was ++ */ ++static int ++hso_wait_modem_status(struct hso_serial *serial, unsigned long arg) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ struct uart_icount cprev, cnow; ++ struct hso_tiocmget *tiocmget; ++ int ret; ++ ++ tiocmget = serial->tiocmget; ++ if (!tiocmget) ++ return -ENOENT; ++ /* ++ * note the counters on entry ++ */ ++ spin_lock_irq(&serial->serial_lock); ++ memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount)); ++ spin_unlock_irq(&serial->serial_lock); ++ add_wait_queue(&tiocmget->waitq, &wait); ++ for (;;) { ++ spin_lock_irq(&serial->serial_lock); ++ memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); ++ spin_unlock_irq(&serial->serial_lock); ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ++ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ++ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd))) { ++ ret = 0; ++ break; ++ } ++ schedule(); ++ /* see if a signal did it */ ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ cprev = cnow; ++ } ++ current->state = TASK_RUNNING; ++ remove_wait_queue(&tiocmget->waitq, &wait); ++ ++ return ret; ++} ++ ++/* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++static int hso_get_count(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ struct uart_icount cnow; ++ struct hso_serial *serial = tty->driver_data; ++ struct hso_tiocmget *tiocmget = serial->tiocmget; ++ ++ memset(icount, 0, sizeof(struct serial_icounter_struct)); ++ ++ if (!tiocmget) ++ return -ENOENT; ++ spin_lock_irq(&serial->serial_lock); ++ memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount)); ++ spin_unlock_irq(&serial->serial_lock); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ ++ return 0; ++} ++ ++ ++static int hso_serial_tiocmget(struct tty_struct *tty) ++{ ++ int retval; ++ struct hso_serial *serial = tty->driver_data; ++ struct hso_tiocmget *tiocmget; ++ u16 UART_state_bitmap; ++ ++ /* sanity check */ ++ if (!serial) { ++ D1("no tty structures"); ++ return -EINVAL; ++ } ++ spin_lock_irq(&serial->serial_lock); ++ retval = ((serial->rts_state) ? TIOCM_RTS : 0) | ++ ((serial->dtr_state) ? TIOCM_DTR : 0); ++ tiocmget = serial->tiocmget; ++ if (tiocmget) { ++ ++ UART_state_bitmap = le16_to_cpu( ++ tiocmget->prev_UART_state_bitmap); ++ if (UART_state_bitmap & B_RING_SIGNAL) ++ retval |= TIOCM_RNG; ++ if (UART_state_bitmap & B_RX_CARRIER) ++ retval |= TIOCM_CD; ++ if (UART_state_bitmap & B_TX_CARRIER) ++ retval |= TIOCM_DSR; ++ } ++ spin_unlock_irq(&serial->serial_lock); ++ return retval; ++} ++ ++static int hso_serial_tiocmset(struct tty_struct *tty, ++ unsigned int set, unsigned int clear) ++{ ++ int val = 0; ++ unsigned long flags; ++ int if_num; ++ struct hso_serial *serial = tty->driver_data; ++ ++ /* sanity check */ ++ if (!serial) { ++ D1("no tty structures"); ++ return -EINVAL; ++ } ++ ++ if ((serial->parent->port_spec & HSO_PORT_MASK) != HSO_PORT_MODEM) ++ return -EINVAL; ++ ++ if_num = serial->parent->interface->altsetting->desc.bInterfaceNumber; ++ ++ spin_lock_irqsave(&serial->serial_lock, flags); ++ if (set & TIOCM_RTS) ++ serial->rts_state = 1; ++ if (set & TIOCM_DTR) ++ serial->dtr_state = 1; ++ ++ if (clear & TIOCM_RTS) ++ serial->rts_state = 0; ++ if (clear & TIOCM_DTR) ++ serial->dtr_state = 0; ++ ++ if (serial->dtr_state) ++ val |= 0x01; ++ if (serial->rts_state) ++ val |= 0x02; ++ ++ spin_unlock_irqrestore(&serial->serial_lock, flags); ++ ++ return usb_control_msg(serial->parent->usb, ++ usb_rcvctrlpipe(serial->parent->usb, 0), 0x22, ++ 0x21, val, if_num, NULL, 0, ++ USB_CTRL_SET_TIMEOUT); ++} ++ ++static int hso_serial_ioctl(struct tty_struct *tty, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct hso_serial *serial = tty->driver_data; ++ int ret = 0; ++ D4("IOCTL cmd: %d, arg: %ld", cmd, arg); ++ ++ if (!serial) ++ return -ENODEV; ++ switch (cmd) { ++ case TIOCMIWAIT: ++ ret = hso_wait_modem_status(serial, arg); ++ break; ++ default: ++ ret = -ENOIOCTLCMD; ++ break; ++ } ++ return ret; ++} ++ ++ ++/* starts a transmit */ ++static void hso_kick_transmit(struct hso_serial *serial) ++{ ++ u8 *temp; ++ unsigned long flags; ++ int res; ++ ++ spin_lock_irqsave(&serial->serial_lock, flags); ++ if (!serial->tx_buffer_count) ++ goto out; ++ ++ if (serial->tx_urb_used) ++ goto out; ++ ++ /* Wakeup USB interface if necessary */ ++ if (hso_get_activity(serial->parent) == -EAGAIN) ++ goto out; ++ ++ /* Switch pointers around to avoid memcpy */ ++ temp = serial->tx_buffer; ++ serial->tx_buffer = serial->tx_data; ++ serial->tx_data = temp; ++ serial->tx_data_count = serial->tx_buffer_count; ++ serial->tx_buffer_count = 0; ++ ++ /* If temp is set, it means we switched buffers */ ++ if (temp && serial->write_data) { ++ res = serial->write_data(serial); ++ if (res >= 0) ++ serial->tx_urb_used = 1; ++ } ++out: ++ spin_unlock_irqrestore(&serial->serial_lock, flags); ++} ++ ++/* make a request (for reading and writing data to muxed serial port) */ ++static int mux_device_request(struct hso_serial *serial, u8 type, u16 port, ++ struct urb *ctrl_urb, ++ struct usb_ctrlrequest *ctrl_req, ++ u8 *ctrl_urb_data, u32 size) ++{ ++ int result; ++ int pipe; ++ ++ /* Sanity check */ ++ if (!serial || !ctrl_urb || !ctrl_req) { ++ printk(KERN_ERR "%s: Wrong arguments\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* initialize */ ++ ctrl_req->wValue = 0; ++ ctrl_req->wIndex = cpu_to_le16(hso_port_to_mux(port)); ++ ctrl_req->wLength = cpu_to_le16(size); ++ ++ if (type == USB_CDC_GET_ENCAPSULATED_RESPONSE) { ++ /* Reading command */ ++ ctrl_req->bRequestType = USB_DIR_IN | ++ USB_TYPE_OPTION_VENDOR | ++ USB_RECIP_INTERFACE; ++ ctrl_req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; ++ pipe = usb_rcvctrlpipe(serial->parent->usb, 0); ++ } else { ++ /* Writing command */ ++ ctrl_req->bRequestType = USB_DIR_OUT | ++ USB_TYPE_OPTION_VENDOR | ++ USB_RECIP_INTERFACE; ++ ctrl_req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; ++ pipe = usb_sndctrlpipe(serial->parent->usb, 0); ++ } ++ /* syslog */ ++ D2("%s command (%02x) len: %d, port: %d", ++ type == USB_CDC_GET_ENCAPSULATED_RESPONSE ? "Read" : "Write", ++ ctrl_req->bRequestType, ctrl_req->wLength, port); ++ ++ /* Load ctrl urb */ ++ ctrl_urb->transfer_flags = 0; ++ usb_fill_control_urb(ctrl_urb, ++ serial->parent->usb, ++ pipe, ++ (u8 *) ctrl_req, ++ ctrl_urb_data, size, ctrl_callback, serial); ++ /* Send it on merry way */ ++ result = usb_submit_urb(ctrl_urb, GFP_ATOMIC); ++ if (result) { ++ dev_err(&ctrl_urb->dev->dev, ++ "%s failed submit ctrl_urb %d type %d\n", __func__, ++ result, type); ++ return result; ++ } ++ ++ /* done */ ++ return size; ++} ++ ++/* called by intr_callback when read occurs */ ++static int hso_mux_serial_read(struct hso_serial *serial) ++{ ++ if (!serial) ++ return -EINVAL; ++ ++ /* clean data */ ++ memset(serial->rx_data[0], 0, CTRL_URB_RX_SIZE); ++ /* make the request */ ++ ++ if (serial->num_rx_urbs != 1) { ++ dev_err(&serial->parent->interface->dev, ++ "ERROR: mux'd reads with multiple buffers " ++ "not possible\n"); ++ return 0; ++ } ++ return mux_device_request(serial, ++ USB_CDC_GET_ENCAPSULATED_RESPONSE, ++ serial->parent->port_spec & HSO_PORT_MASK, ++ serial->rx_urb[0], ++ &serial->ctrl_req_rx, ++ serial->rx_data[0], serial->rx_data_length); ++} ++ ++/* used for muxed serial port callback (muxed serial read) */ ++static void intr_callback(struct urb *urb) ++{ ++ struct hso_shared_int *shared_int = urb->context; ++ struct hso_serial *serial; ++ unsigned char *port_req; ++ int status = urb->status; ++ int i; ++ ++ usb_mark_last_busy(urb->dev); ++ ++ /* sanity check */ ++ if (!shared_int) ++ return; ++ ++ /* status check */ ++ if (status) { ++ handle_usb_error(status, __func__, NULL); ++ return; ++ } ++ D4("\n--- Got intr callback 0x%02X ---", status); ++ ++ /* what request? */ ++ port_req = urb->transfer_buffer; ++ D4(" port_req = 0x%.2X\n", *port_req); ++ /* loop over all muxed ports to find the one sending this */ ++ for (i = 0; i < 8; i++) { ++ /* max 8 channels on MUX */ ++ if (*port_req & (1 << i)) { ++ serial = get_serial_by_shared_int_and_type(shared_int, ++ (1 << i)); ++ if (serial != NULL) { ++ D1("Pending read interrupt on port %d\n", i); ++ spin_lock(&serial->serial_lock); ++ if (serial->rx_state == RX_IDLE && ++ atomic_read(&serial->port.count) > 0) { ++ /* Setup and send a ctrl req read on ++ * port i */ ++ if (!serial->rx_urb_filled[0]) { ++ serial->rx_state = RX_SENT; ++ hso_mux_serial_read(serial); ++ } else ++ serial->rx_state = RX_PENDING; ++ } else { ++ D1("Already a read pending on " ++ "port %d or port not open\n", i); ++ } ++ spin_unlock(&serial->serial_lock); ++ } ++ } ++ } ++ /* Resubmit interrupt urb */ ++ hso_mux_submit_intr_urb(shared_int, urb->dev, GFP_ATOMIC); ++} ++ ++/* called for writing to muxed serial port */ ++static int hso_mux_serial_write_data(struct hso_serial *serial) ++{ ++ if (NULL == serial) ++ return -EINVAL; ++ ++ return mux_device_request(serial, ++ USB_CDC_SEND_ENCAPSULATED_COMMAND, ++ serial->parent->port_spec & HSO_PORT_MASK, ++ serial->tx_urb, ++ &serial->ctrl_req_tx, ++ serial->tx_data, serial->tx_data_count); ++} ++ ++/* write callback for Diag and CS port */ ++static void hso_std_serial_write_bulk_callback(struct urb *urb) ++{ ++ struct hso_serial *serial = urb->context; ++ int status = urb->status; ++ ++ /* sanity check */ ++ if (!serial) { ++ D1("serial == NULL"); ++ return; ++ } ++ ++ spin_lock(&serial->serial_lock); ++ serial->tx_urb_used = 0; ++ spin_unlock(&serial->serial_lock); ++ if (status) { ++ handle_usb_error(status, __func__, serial->parent); ++ return; ++ } ++ hso_put_activity(serial->parent); ++ tty_port_tty_wakeup(&serial->port); ++ hso_kick_transmit(serial); ++ ++ D1(" "); ++} ++ ++/* called for writing diag or CS serial port */ ++static int hso_std_serial_write_data(struct hso_serial *serial) ++{ ++ int count = serial->tx_data_count; ++ int result; ++ ++ usb_fill_bulk_urb(serial->tx_urb, ++ serial->parent->usb, ++ usb_sndbulkpipe(serial->parent->usb, ++ serial->out_endp-> ++ bEndpointAddress & 0x7F), ++ serial->tx_data, serial->tx_data_count, ++ hso_std_serial_write_bulk_callback, serial); ++ ++ result = usb_submit_urb(serial->tx_urb, GFP_ATOMIC); ++ if (result) { ++ dev_warn(&serial->parent->usb->dev, ++ "Failed to submit urb - res %d\n", result); ++ return result; ++ } ++ ++ return count; ++} ++ ++/* callback after read or write on muxed serial port */ ++static void ctrl_callback(struct urb *urb) ++{ ++ struct hso_serial *serial = urb->context; ++ struct usb_ctrlrequest *req; ++ int status = urb->status; ++ ++ /* sanity check */ ++ if (!serial) ++ return; ++ ++ spin_lock(&serial->serial_lock); ++ serial->tx_urb_used = 0; ++ spin_unlock(&serial->serial_lock); ++ if (status) { ++ handle_usb_error(status, __func__, serial->parent); ++ return; ++ } ++ ++ /* what request? */ ++ req = (struct usb_ctrlrequest *)(urb->setup_packet); ++ D4("\n--- Got muxed ctrl callback 0x%02X ---", status); ++ D4("Actual length of urb = %d\n", urb->actual_length); ++ DUMP1(urb->transfer_buffer, urb->actual_length); ++ ++ if (req->bRequestType == ++ (USB_DIR_IN | USB_TYPE_OPTION_VENDOR | USB_RECIP_INTERFACE)) { ++ /* response to a read command */ ++ serial->rx_urb_filled[0] = 1; ++ spin_lock(&serial->serial_lock); ++ put_rxbuf_data_and_resubmit_ctrl_urb(serial); ++ spin_unlock(&serial->serial_lock); ++ } else { ++ hso_put_activity(serial->parent); ++ tty_port_tty_wakeup(&serial->port); ++ /* response to a write command */ ++ hso_kick_transmit(serial); ++ } ++} ++ ++/* handle RX data for serial port */ ++static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial) ++{ ++ struct tty_struct *tty; ++ int count; ++ ++ /* Sanity check */ ++ if (urb == NULL || serial == NULL) { ++ D1("serial = NULL"); ++ return -2; ++ } ++ ++ tty = tty_port_tty_get(&serial->port); ++ ++ if (tty && test_bit(TTY_THROTTLED, &tty->flags)) { ++ tty_kref_put(tty); ++ return -1; ++ } ++ ++ /* Push data to tty */ ++ D1("data to push to tty"); ++ count = tty_buffer_request_room(&serial->port, urb->actual_length); ++ if (count >= urb->actual_length) { ++ tty_insert_flip_string(&serial->port, urb->transfer_buffer, ++ urb->actual_length); ++ tty_flip_buffer_push(&serial->port); ++ } else { ++ dev_warn(&serial->parent->usb->dev, ++ "dropping data, %d bytes lost\n", urb->actual_length); ++ } ++ ++ tty_kref_put(tty); ++ ++ serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0; ++ ++ return 0; ++} ++ ++ ++/* Base driver functions */ ++ ++static void hso_log_port(struct hso_device *hso_dev) ++{ ++ char *port_type; ++ char port_dev[20]; ++ ++ switch (hso_dev->port_spec & HSO_PORT_MASK) { ++ case HSO_PORT_CONTROL: ++ port_type = "Control"; ++ break; ++ case HSO_PORT_APP: ++ port_type = "Application"; ++ break; ++ case HSO_PORT_GPS: ++ port_type = "GPS"; ++ break; ++ case HSO_PORT_GPS_CONTROL: ++ port_type = "GPS control"; ++ break; ++ case HSO_PORT_APP2: ++ port_type = "Application2"; ++ break; ++ case HSO_PORT_PCSC: ++ port_type = "PCSC"; ++ break; ++ case HSO_PORT_DIAG: ++ port_type = "Diagnostic"; ++ break; ++ case HSO_PORT_DIAG2: ++ port_type = "Diagnostic2"; ++ break; ++ case HSO_PORT_MODEM: ++ port_type = "Modem"; ++ break; ++ case HSO_PORT_NETWORK: ++ port_type = "Network"; ++ break; ++ default: ++ port_type = "Unknown"; ++ break; ++ } ++ if ((hso_dev->port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) { ++ sprintf(port_dev, "%s", dev2net(hso_dev)->net->name); ++ } else ++ sprintf(port_dev, "/dev/%s%d", tty_filename, ++ dev2ser(hso_dev)->minor); ++ ++ dev_dbg(&hso_dev->interface->dev, "HSO: Found %s port %s\n", ++ port_type, port_dev); ++} ++ ++static int hso_start_net_device(struct hso_device *hso_dev) ++{ ++ int i, result = 0; ++ struct hso_net *hso_net = dev2net(hso_dev); ++ ++ if (!hso_net) ++ return -ENODEV; ++ ++ /* send URBs for all read buffers */ ++ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { ++ ++ /* Prep a receive URB */ ++ usb_fill_bulk_urb(hso_net->mux_bulk_rx_urb_pool[i], ++ hso_dev->usb, ++ usb_rcvbulkpipe(hso_dev->usb, ++ hso_net->in_endp-> ++ bEndpointAddress & 0x7F), ++ hso_net->mux_bulk_rx_buf_pool[i], ++ MUX_BULK_RX_BUF_SIZE, read_bulk_callback, ++ hso_net); ++ ++ /* Put it out there so the device can send us stuff */ ++ result = usb_submit_urb(hso_net->mux_bulk_rx_urb_pool[i], ++ GFP_NOIO); ++ if (result) ++ dev_warn(&hso_dev->usb->dev, ++ "%s failed mux_bulk_rx_urb[%d] %d\n", __func__, ++ i, result); ++ } ++ ++ return result; ++} ++ ++static int hso_stop_net_device(struct hso_device *hso_dev) ++{ ++ int i; ++ struct hso_net *hso_net = dev2net(hso_dev); ++ ++ if (!hso_net) ++ return -ENODEV; ++ ++ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { ++ if (hso_net->mux_bulk_rx_urb_pool[i]) ++ usb_kill_urb(hso_net->mux_bulk_rx_urb_pool[i]); ++ ++ } ++ if (hso_net->mux_bulk_tx_urb) ++ usb_kill_urb(hso_net->mux_bulk_tx_urb); ++ ++ return 0; ++} ++ ++static int hso_start_serial_device(struct hso_device *hso_dev, gfp_t flags) ++{ ++ int i, result = 0; ++ struct hso_serial *serial = dev2ser(hso_dev); ++ ++ if (!serial) ++ return -ENODEV; ++ ++ /* If it is not the MUX port fill in and submit a bulk urb (already ++ * allocated in hso_serial_start) */ ++ if (!(serial->parent->port_spec & HSO_INTF_MUX)) { ++ for (i = 0; i < serial->num_rx_urbs; i++) { ++ usb_fill_bulk_urb(serial->rx_urb[i], ++ serial->parent->usb, ++ usb_rcvbulkpipe(serial->parent->usb, ++ serial->in_endp-> ++ bEndpointAddress & ++ 0x7F), ++ serial->rx_data[i], ++ serial->rx_data_length, ++ hso_std_serial_read_bulk_callback, ++ serial); ++ result = usb_submit_urb(serial->rx_urb[i], flags); ++ if (result) { ++ dev_warn(&serial->parent->usb->dev, ++ "Failed to submit urb - res %d\n", ++ result); ++ break; ++ } ++ } ++ } else { ++ mutex_lock(&serial->shared_int->shared_int_lock); ++ if (!serial->shared_int->use_count) { ++ result = ++ hso_mux_submit_intr_urb(serial->shared_int, ++ hso_dev->usb, flags); ++ } ++ serial->shared_int->use_count++; ++ mutex_unlock(&serial->shared_int->shared_int_lock); ++ } ++ if (serial->tiocmget) ++ tiocmget_submit_urb(serial, ++ serial->tiocmget, ++ serial->parent->usb); ++ return result; ++} ++ ++static int hso_stop_serial_device(struct hso_device *hso_dev) ++{ ++ int i; ++ struct hso_serial *serial = dev2ser(hso_dev); ++ struct hso_tiocmget *tiocmget; ++ ++ if (!serial) ++ return -ENODEV; ++ ++ for (i = 0; i < serial->num_rx_urbs; i++) { ++ if (serial->rx_urb[i]) { ++ usb_kill_urb(serial->rx_urb[i]); ++ serial->rx_urb_filled[i] = 0; ++ } ++ } ++ serial->curr_rx_urb_idx = 0; ++ ++ if (serial->tx_urb) ++ usb_kill_urb(serial->tx_urb); ++ ++ if (serial->shared_int) { ++ mutex_lock(&serial->shared_int->shared_int_lock); ++ if (serial->shared_int->use_count && ++ (--serial->shared_int->use_count == 0)) { ++ struct urb *urb; ++ ++ urb = serial->shared_int->shared_intr_urb; ++ if (urb) ++ usb_kill_urb(urb); ++ } ++ mutex_unlock(&serial->shared_int->shared_int_lock); ++ } ++ tiocmget = serial->tiocmget; ++ if (tiocmget) { ++ wake_up_interruptible(&tiocmget->waitq); ++ usb_kill_urb(tiocmget->urb); ++ } ++ ++ return 0; ++} ++ ++static void hso_serial_common_free(struct hso_serial *serial) ++{ ++ int i; ++ ++ if (serial->parent->dev) ++ device_remove_file(serial->parent->dev, &dev_attr_hsotype); ++ ++ tty_unregister_device(tty_drv, serial->minor); ++ ++ for (i = 0; i < serial->num_rx_urbs; i++) { ++ /* unlink and free RX URB */ ++ usb_free_urb(serial->rx_urb[i]); ++ /* free the RX buffer */ ++ kfree(serial->rx_data[i]); ++ } ++ ++ /* unlink and free TX URB */ ++ usb_free_urb(serial->tx_urb); ++ kfree(serial->tx_data); ++ tty_port_destroy(&serial->port); ++} ++ ++static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, ++ int rx_size, int tx_size) ++{ ++ struct device *dev; ++ int minor; ++ int i; ++ ++ tty_port_init(&serial->port); ++ ++ minor = get_free_serial_index(); ++ if (minor < 0) ++ goto exit; ++ ++ /* register our minor number */ ++ serial->parent->dev = tty_port_register_device(&serial->port, tty_drv, ++ minor, &serial->parent->interface->dev); ++ dev = serial->parent->dev; ++ dev_set_drvdata(dev, serial->parent); ++ i = device_create_file(dev, &dev_attr_hsotype); ++ ++ /* fill in specific data for later use */ ++ serial->minor = minor; ++ serial->magic = HSO_SERIAL_MAGIC; ++ spin_lock_init(&serial->serial_lock); ++ serial->num_rx_urbs = num_urbs; ++ ++ /* RX, allocate urb and initialize */ ++ ++ /* prepare our RX buffer */ ++ serial->rx_data_length = rx_size; ++ for (i = 0; i < serial->num_rx_urbs; i++) { ++ serial->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); ++ if (!serial->rx_urb[i]) { ++ dev_err(dev, "Could not allocate urb?\n"); ++ goto exit; ++ } ++ serial->rx_urb[i]->transfer_buffer = NULL; ++ serial->rx_urb[i]->transfer_buffer_length = 0; ++ serial->rx_data[i] = kzalloc(serial->rx_data_length, ++ GFP_KERNEL); ++ if (!serial->rx_data[i]) ++ goto exit; ++ } ++ ++ /* TX, allocate urb and initialize */ ++ serial->tx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!serial->tx_urb) { ++ dev_err(dev, "Could not allocate urb?\n"); ++ goto exit; ++ } ++ serial->tx_urb->transfer_buffer = NULL; ++ serial->tx_urb->transfer_buffer_length = 0; ++ /* prepare our TX buffer */ ++ serial->tx_data_count = 0; ++ serial->tx_buffer_count = 0; ++ serial->tx_data_length = tx_size; ++ serial->tx_data = kzalloc(serial->tx_data_length, GFP_KERNEL); ++ if (!serial->tx_data) ++ goto exit; ++ ++ serial->tx_buffer = kzalloc(serial->tx_data_length, GFP_KERNEL); ++ if (!serial->tx_buffer) ++ goto exit; ++ ++ return 0; ++exit: ++ hso_serial_common_free(serial); ++ return -1; ++} ++ ++/* Creates a general hso device */ ++static struct hso_device *hso_create_device(struct usb_interface *intf, ++ int port_spec) ++{ ++ struct hso_device *hso_dev; ++ ++ hso_dev = kzalloc(sizeof(*hso_dev), GFP_ATOMIC); ++ if (!hso_dev) ++ return NULL; ++ ++ hso_dev->port_spec = port_spec; ++ hso_dev->usb = interface_to_usbdev(intf); ++ hso_dev->interface = intf; ++ kref_init(&hso_dev->ref); ++ mutex_init(&hso_dev->mutex); ++ ++ INIT_WORK(&hso_dev->async_get_intf, async_get_intf); ++ INIT_WORK(&hso_dev->async_put_intf, async_put_intf); ++ INIT_WORK(&hso_dev->reset_device, reset_device); ++ ++ return hso_dev; ++} ++ ++/* Removes a network device in the network device table */ ++static int remove_net_device(struct hso_device *hso_dev) ++{ ++ int i; ++ ++ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { ++ if (network_table[i] == hso_dev) { ++ network_table[i] = NULL; ++ break; ++ } ++ } ++ if (i == HSO_MAX_NET_DEVICES) ++ return -1; ++ return 0; ++} ++ ++/* Frees our network device */ ++static void hso_free_net_device(struct hso_device *hso_dev) ++{ ++ int i; ++ struct hso_net *hso_net = dev2net(hso_dev); ++ ++ if (!hso_net) ++ return; ++ ++ remove_net_device(hso_net->parent); ++ ++ if (hso_net->net) ++ unregister_netdev(hso_net->net); ++ ++ /* start freeing */ ++ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { ++ usb_free_urb(hso_net->mux_bulk_rx_urb_pool[i]); ++ kfree(hso_net->mux_bulk_rx_buf_pool[i]); ++ hso_net->mux_bulk_rx_buf_pool[i] = NULL; ++ } ++ usb_free_urb(hso_net->mux_bulk_tx_urb); ++ kfree(hso_net->mux_bulk_tx_buf); ++ hso_net->mux_bulk_tx_buf = NULL; ++ ++ if (hso_net->net) ++ free_netdev(hso_net->net); ++ ++ kfree(hso_dev); ++} ++ ++static const struct net_device_ops hso_netdev_ops = { ++ .ndo_open = hso_net_open, ++ .ndo_stop = hso_net_close, ++ .ndo_start_xmit = hso_net_start_xmit, ++ .ndo_tx_timeout = hso_net_tx_timeout, ++}; ++ ++/* initialize the network interface */ ++static void hso_net_init(struct net_device *net) ++{ ++ struct hso_net *hso_net = netdev_priv(net); ++ ++ D1("sizeof hso_net is %d", (int)sizeof(*hso_net)); ++ ++ /* fill in the other fields */ ++ net->netdev_ops = &hso_netdev_ops; ++ net->watchdog_timeo = HSO_NET_TX_TIMEOUT; ++ net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; ++ net->type = ARPHRD_NONE; ++ net->mtu = DEFAULT_MTU - 14; ++ net->tx_queue_len = 10; ++ net->ethtool_ops = &ops; ++ ++ /* and initialize the semaphore */ ++ spin_lock_init(&hso_net->net_lock); ++} ++ ++/* Adds a network device in the network device table */ ++static int add_net_device(struct hso_device *hso_dev) ++{ ++ int i; ++ ++ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { ++ if (network_table[i] == NULL) { ++ network_table[i] = hso_dev; ++ break; ++ } ++ } ++ if (i == HSO_MAX_NET_DEVICES) ++ return -1; ++ return 0; ++} ++ ++static int hso_rfkill_set_block(void *data, bool blocked) ++{ ++ struct hso_device *hso_dev = data; ++ int enabled = !blocked; ++ int rv; ++ ++ mutex_lock(&hso_dev->mutex); ++ if (hso_dev->usb_gone) ++ rv = 0; ++ else ++ rv = usb_control_msg(hso_dev->usb, usb_rcvctrlpipe(hso_dev->usb, 0), ++ enabled ? 0x82 : 0x81, 0x40, 0, 0, NULL, 0, ++ USB_CTRL_SET_TIMEOUT); ++ mutex_unlock(&hso_dev->mutex); ++ return rv; ++} ++ ++static const struct rfkill_ops hso_rfkill_ops = { ++ .set_block = hso_rfkill_set_block, ++}; ++ ++/* Creates and sets up everything for rfkill */ ++static void hso_create_rfkill(struct hso_device *hso_dev, ++ struct usb_interface *interface) ++{ ++ struct hso_net *hso_net = dev2net(hso_dev); ++ struct device *dev = &hso_net->net->dev; ++ char *rfkn; ++ ++ rfkn = kzalloc(20, GFP_KERNEL); ++ if (!rfkn) ++ dev_err(dev, "%s - Out of memory\n", __func__); ++ ++ snprintf(rfkn, 20, "hso-%d", ++ interface->altsetting->desc.bInterfaceNumber); ++ ++ hso_net->rfkill = rfkill_alloc(rfkn, ++ &interface_to_usbdev(interface)->dev, ++ RFKILL_TYPE_WWAN, ++ &hso_rfkill_ops, hso_dev); ++ if (!hso_net->rfkill) { ++ dev_err(dev, "%s - Out of memory\n", __func__); ++ kfree(rfkn); ++ return; ++ } ++ if (rfkill_register(hso_net->rfkill) < 0) { ++ rfkill_destroy(hso_net->rfkill); ++ kfree(rfkn); ++ hso_net->rfkill = NULL; ++ dev_err(dev, "%s - Failed to register rfkill\n", __func__); ++ return; ++ } ++} ++ ++static struct device_type hso_type = { ++ .name = "wwan", ++}; ++ ++/* Creates our network device */ ++static struct hso_device *hso_create_net_device(struct usb_interface *interface, ++ int port_spec) ++{ ++ int result, i; ++ struct net_device *net; ++ struct hso_net *hso_net; ++ struct hso_device *hso_dev; ++ ++ hso_dev = hso_create_device(interface, port_spec); ++ if (!hso_dev) ++ return NULL; ++ ++ /* allocate our network device, then we can put in our private data */ ++ /* call hso_net_init to do the basic initialization */ ++ net = alloc_netdev(sizeof(struct hso_net), "hso%d", NET_NAME_UNKNOWN, ++ hso_net_init); ++ if (!net) { ++ dev_err(&interface->dev, "Unable to create ethernet device\n"); ++ goto exit; ++ } ++ ++ hso_net = netdev_priv(net); ++ ++ hso_dev->port_data.dev_net = hso_net; ++ hso_net->net = net; ++ hso_net->parent = hso_dev; ++ ++ hso_net->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, ++ USB_DIR_IN); ++ if (!hso_net->in_endp) { ++ dev_err(&interface->dev, "Can't find BULK IN endpoint\n"); ++ goto exit; ++ } ++ hso_net->out_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, ++ USB_DIR_OUT); ++ if (!hso_net->out_endp) { ++ dev_err(&interface->dev, "Can't find BULK OUT endpoint\n"); ++ goto exit; ++ } ++ SET_NETDEV_DEV(net, &interface->dev); ++ SET_NETDEV_DEVTYPE(net, &hso_type); ++ ++ /* registering our net device */ ++ result = register_netdev(net); ++ if (result) { ++ dev_err(&interface->dev, "Failed to register device\n"); ++ goto exit; ++ } ++ ++ /* start allocating */ ++ for (i = 0; i < MUX_BULK_RX_BUF_COUNT; i++) { ++ hso_net->mux_bulk_rx_urb_pool[i] = usb_alloc_urb(0, GFP_KERNEL); ++ if (!hso_net->mux_bulk_rx_urb_pool[i]) { ++ dev_err(&interface->dev, "Could not allocate rx urb\n"); ++ goto exit; ++ } ++ hso_net->mux_bulk_rx_buf_pool[i] = kzalloc(MUX_BULK_RX_BUF_SIZE, ++ GFP_KERNEL); ++ if (!hso_net->mux_bulk_rx_buf_pool[i]) ++ goto exit; ++ } ++ hso_net->mux_bulk_tx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!hso_net->mux_bulk_tx_urb) { ++ dev_err(&interface->dev, "Could not allocate tx urb\n"); ++ goto exit; ++ } ++ hso_net->mux_bulk_tx_buf = kzalloc(MUX_BULK_TX_BUF_SIZE, GFP_KERNEL); ++ if (!hso_net->mux_bulk_tx_buf) ++ goto exit; ++ ++ add_net_device(hso_dev); ++ ++ hso_log_port(hso_dev); ++ ++ hso_create_rfkill(hso_dev, interface); ++ ++ return hso_dev; ++exit: ++ hso_free_net_device(hso_dev); ++ return NULL; ++} ++ ++static void hso_free_tiomget(struct hso_serial *serial) ++{ ++ struct hso_tiocmget *tiocmget; ++ if (!serial) ++ return; ++ tiocmget = serial->tiocmget; ++ if (tiocmget) { ++ usb_free_urb(tiocmget->urb); ++ tiocmget->urb = NULL; ++ serial->tiocmget = NULL; ++ kfree(tiocmget); ++ } ++} ++ ++/* Frees an AT channel ( goes for both mux and non-mux ) */ ++static void hso_free_serial_device(struct hso_device *hso_dev) ++{ ++ struct hso_serial *serial = dev2ser(hso_dev); ++ ++ if (!serial) ++ return; ++ set_serial_by_index(serial->minor, NULL); ++ ++ hso_serial_common_free(serial); ++ ++ if (serial->shared_int) { ++ mutex_lock(&serial->shared_int->shared_int_lock); ++ if (--serial->shared_int->ref_count == 0) ++ hso_free_shared_int(serial->shared_int); ++ else ++ mutex_unlock(&serial->shared_int->shared_int_lock); ++ } ++ hso_free_tiomget(serial); ++ kfree(serial); ++ kfree(hso_dev); ++} ++ ++/* Creates a bulk AT channel */ ++static struct hso_device *hso_create_bulk_serial_device( ++ struct usb_interface *interface, int port) ++{ ++ struct hso_device *hso_dev; ++ struct hso_serial *serial; ++ int num_urbs; ++ struct hso_tiocmget *tiocmget; ++ ++ hso_dev = hso_create_device(interface, port); ++ if (!hso_dev) ++ return NULL; ++ ++ serial = kzalloc(sizeof(*serial), GFP_KERNEL); ++ if (!serial) ++ goto exit; ++ ++ serial->parent = hso_dev; ++ hso_dev->port_data.dev_serial = serial; ++ ++ if ((port & HSO_PORT_MASK) == HSO_PORT_MODEM) { ++ num_urbs = 2; ++ serial->tiocmget = kzalloc(sizeof(struct hso_tiocmget), ++ GFP_KERNEL); ++ /* it isn't going to break our heart if serial->tiocmget ++ * allocation fails don't bother checking this. ++ */ ++ if (serial->tiocmget) { ++ tiocmget = serial->tiocmget; ++ tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (tiocmget->urb) { ++ mutex_init(&tiocmget->mutex); ++ init_waitqueue_head(&tiocmget->waitq); ++ tiocmget->endp = hso_get_ep( ++ interface, ++ USB_ENDPOINT_XFER_INT, ++ USB_DIR_IN); ++ } else ++ hso_free_tiomget(serial); ++ } ++ } ++ else ++ num_urbs = 1; ++ ++ if (hso_serial_common_create(serial, num_urbs, BULK_URB_RX_SIZE, ++ BULK_URB_TX_SIZE)) ++ goto exit; ++ ++ serial->in_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, ++ USB_DIR_IN); ++ if (!serial->in_endp) { ++ dev_err(&interface->dev, "Failed to find BULK IN ep\n"); ++ goto exit2; ++ } ++ ++ if (! ++ (serial->out_endp = ++ hso_get_ep(interface, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT))) { ++ dev_err(&interface->dev, "Failed to find BULK IN ep\n"); ++ goto exit2; ++ } ++ ++ serial->write_data = hso_std_serial_write_data; ++ ++ /* and record this serial */ ++ set_serial_by_index(serial->minor, serial); ++ ++ /* setup the proc dirs and files if needed */ ++ hso_log_port(hso_dev); ++ ++ /* done, return it */ ++ return hso_dev; ++ ++exit2: ++ hso_serial_common_free(serial); ++exit: ++ hso_free_tiomget(serial); ++ kfree(serial); ++ kfree(hso_dev); ++ return NULL; ++} ++ ++/* Creates a multiplexed AT channel */ ++static ++struct hso_device *hso_create_mux_serial_device(struct usb_interface *interface, ++ int port, ++ struct hso_shared_int *mux) ++{ ++ struct hso_device *hso_dev; ++ struct hso_serial *serial; ++ int port_spec; ++ ++ port_spec = HSO_INTF_MUX; ++ port_spec &= ~HSO_PORT_MASK; ++ ++ port_spec |= hso_mux_to_port(port); ++ if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NO_PORT) ++ return NULL; ++ ++ hso_dev = hso_create_device(interface, port_spec); ++ if (!hso_dev) ++ return NULL; ++ ++ serial = kzalloc(sizeof(*serial), GFP_KERNEL); ++ if (!serial) ++ goto exit; ++ ++ hso_dev->port_data.dev_serial = serial; ++ serial->parent = hso_dev; ++ ++ if (hso_serial_common_create ++ (serial, 1, CTRL_URB_RX_SIZE, CTRL_URB_TX_SIZE)) ++ goto exit; ++ ++ serial->tx_data_length--; ++ serial->write_data = hso_mux_serial_write_data; ++ ++ serial->shared_int = mux; ++ mutex_lock(&serial->shared_int->shared_int_lock); ++ serial->shared_int->ref_count++; ++ mutex_unlock(&serial->shared_int->shared_int_lock); ++ ++ /* and record this serial */ ++ set_serial_by_index(serial->minor, serial); ++ ++ /* setup the proc dirs and files if needed */ ++ hso_log_port(hso_dev); ++ ++ /* done, return it */ ++ return hso_dev; ++ ++exit: ++ if (serial) { ++ tty_unregister_device(tty_drv, serial->minor); ++ kfree(serial); ++ } ++ if (hso_dev) ++ kfree(hso_dev); ++ return NULL; ++ ++} ++ ++static void hso_free_shared_int(struct hso_shared_int *mux) ++{ ++ usb_free_urb(mux->shared_intr_urb); ++ kfree(mux->shared_intr_buf); ++ mutex_unlock(&mux->shared_int_lock); ++ kfree(mux); ++} ++ ++static ++struct hso_shared_int *hso_create_shared_int(struct usb_interface *interface) ++{ ++ struct hso_shared_int *mux = kzalloc(sizeof(*mux), GFP_KERNEL); ++ ++ if (!mux) ++ return NULL; ++ ++ mux->intr_endp = hso_get_ep(interface, USB_ENDPOINT_XFER_INT, ++ USB_DIR_IN); ++ if (!mux->intr_endp) { ++ dev_err(&interface->dev, "Can't find INT IN endpoint\n"); ++ goto exit; ++ } ++ ++ mux->shared_intr_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!mux->shared_intr_urb) { ++ dev_err(&interface->dev, "Could not allocate intr urb?\n"); ++ goto exit; ++ } ++ mux->shared_intr_buf = ++ kzalloc(le16_to_cpu(mux->intr_endp->wMaxPacketSize), ++ GFP_KERNEL); ++ if (!mux->shared_intr_buf) ++ goto exit; ++ ++ mutex_init(&mux->shared_int_lock); ++ ++ return mux; ++ ++exit: ++ kfree(mux->shared_intr_buf); ++ usb_free_urb(mux->shared_intr_urb); ++ kfree(mux); ++ return NULL; ++} ++ ++/* Gets the port spec for a certain interface */ ++static int hso_get_config_data(struct usb_interface *interface) ++{ ++ struct usb_device *usbdev = interface_to_usbdev(interface); ++ u8 *config_data = kmalloc(17, GFP_KERNEL); ++ u32 if_num = interface->altsetting->desc.bInterfaceNumber; ++ s32 result; ++ ++ if (!config_data) ++ return -ENOMEM; ++ if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), ++ 0x86, 0xC0, 0, 0, config_data, 17, ++ USB_CTRL_SET_TIMEOUT) != 0x11) { ++ kfree(config_data); ++ return -EIO; ++ } ++ ++ switch (config_data[if_num]) { ++ case 0x0: ++ result = 0; ++ break; ++ case 0x1: ++ result = HSO_PORT_DIAG; ++ break; ++ case 0x2: ++ result = HSO_PORT_GPS; ++ break; ++ case 0x3: ++ result = HSO_PORT_GPS_CONTROL; ++ break; ++ case 0x4: ++ result = HSO_PORT_APP; ++ break; ++ case 0x5: ++ result = HSO_PORT_APP2; ++ break; ++ case 0x6: ++ result = HSO_PORT_CONTROL; ++ break; ++ case 0x7: ++ result = HSO_PORT_NETWORK; ++ break; ++ case 0x8: ++ result = HSO_PORT_MODEM; ++ break; ++ case 0x9: ++ result = HSO_PORT_MSD; ++ break; ++ case 0xa: ++ result = HSO_PORT_PCSC; ++ break; ++ case 0xb: ++ result = HSO_PORT_VOICE; ++ break; ++ default: ++ result = 0; ++ } ++ ++ if (result) ++ result |= HSO_INTF_BULK; ++ ++ if (config_data[16] & 0x1) ++ result |= HSO_INFO_CRC_BUG; ++ ++ kfree(config_data); ++ return result; ++} ++ ++/* called once for each interface upon device insertion */ ++static int hso_probe(struct usb_interface *interface, ++ const struct usb_device_id *id) ++{ ++ int mux, i, if_num, port_spec; ++ unsigned char port_mask; ++ struct hso_device *hso_dev = NULL; ++ struct hso_shared_int *shared_int; ++ struct hso_device *tmp_dev = NULL; ++ ++ if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) { ++ dev_err(&interface->dev, "Not our interface\n"); ++ return -ENODEV; ++ } ++ ++ if_num = interface->altsetting->desc.bInterfaceNumber; ++ ++ /* Get the interface/port specification from either driver_info or from ++ * the device itself */ ++ if (id->driver_info) ++ port_spec = ((u32 *)(id->driver_info))[if_num]; ++ else ++ port_spec = hso_get_config_data(interface); ++ ++ /* Check if we need to switch to alt interfaces prior to port ++ * configuration */ ++ if (interface->num_altsetting > 1) ++ usb_set_interface(interface_to_usbdev(interface), if_num, 1); ++ interface->needs_remote_wakeup = 1; ++ ++ /* Allocate new hso device(s) */ ++ switch (port_spec & HSO_INTF_MASK) { ++ case HSO_INTF_MUX: ++ if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) { ++ /* Create the network device */ ++ if (!disable_net) { ++ hso_dev = hso_create_net_device(interface, ++ port_spec); ++ if (!hso_dev) ++ goto exit; ++ tmp_dev = hso_dev; ++ } ++ } ++ ++ if (hso_get_mux_ports(interface, &port_mask)) ++ /* TODO: de-allocate everything */ ++ goto exit; ++ ++ shared_int = hso_create_shared_int(interface); ++ if (!shared_int) ++ goto exit; ++ ++ for (i = 1, mux = 0; i < 0x100; i = i << 1, mux++) { ++ if (port_mask & i) { ++ hso_dev = hso_create_mux_serial_device( ++ interface, i, shared_int); ++ if (!hso_dev) ++ goto exit; ++ } ++ } ++ ++ if (tmp_dev) ++ hso_dev = tmp_dev; ++ break; ++ ++ case HSO_INTF_BULK: ++ /* It's a regular bulk interface */ ++ if ((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) { ++ if (!disable_net) ++ hso_dev = ++ hso_create_net_device(interface, port_spec); ++ } else { ++ hso_dev = ++ hso_create_bulk_serial_device(interface, port_spec); ++ } ++ if (!hso_dev) ++ goto exit; ++ break; ++ default: ++ goto exit; ++ } ++ ++ /* save our data pointer in this device */ ++ usb_set_intfdata(interface, hso_dev); ++ ++ /* done */ ++ return 0; ++exit: ++ hso_free_interface(interface); ++ return -ENODEV; ++} ++ ++/* device removed, cleaning up */ ++static void hso_disconnect(struct usb_interface *interface) ++{ ++ hso_free_interface(interface); ++ ++ /* remove reference of our private data */ ++ usb_set_intfdata(interface, NULL); ++} ++ ++static void async_get_intf(struct work_struct *data) ++{ ++ struct hso_device *hso_dev = ++ container_of(data, struct hso_device, async_get_intf); ++ usb_autopm_get_interface(hso_dev->interface); ++} ++ ++static void async_put_intf(struct work_struct *data) ++{ ++ struct hso_device *hso_dev = ++ container_of(data, struct hso_device, async_put_intf); ++ usb_autopm_put_interface(hso_dev->interface); ++} ++ ++static int hso_get_activity(struct hso_device *hso_dev) ++{ ++ if (hso_dev->usb->state == USB_STATE_SUSPENDED) { ++ if (!hso_dev->is_active) { ++ hso_dev->is_active = 1; ++ schedule_work(&hso_dev->async_get_intf); ++ } ++ } ++ ++ if (hso_dev->usb->state != USB_STATE_CONFIGURED) ++ return -EAGAIN; ++ ++ usb_mark_last_busy(hso_dev->usb); ++ ++ return 0; ++} ++ ++static int hso_put_activity(struct hso_device *hso_dev) ++{ ++ if (hso_dev->usb->state != USB_STATE_SUSPENDED) { ++ if (hso_dev->is_active) { ++ hso_dev->is_active = 0; ++ schedule_work(&hso_dev->async_put_intf); ++ return -EAGAIN; ++ } ++ } ++ hso_dev->is_active = 0; ++ return 0; ++} ++ ++/* called by kernel when we need to suspend device */ ++static int hso_suspend(struct usb_interface *iface, pm_message_t message) ++{ ++ int i, result; ++ ++ /* Stop all serial ports */ ++ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { ++ if (serial_table[i] && (serial_table[i]->interface == iface)) { ++ result = hso_stop_serial_device(serial_table[i]); ++ if (result) ++ goto out; ++ } ++ } ++ ++ /* Stop all network ports */ ++ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { ++ if (network_table[i] && ++ (network_table[i]->interface == iface)) { ++ result = hso_stop_net_device(network_table[i]); ++ if (result) ++ goto out; ++ } ++ } ++ ++out: ++ return 0; ++} ++ ++/* called by kernel when we need to resume device */ ++static int hso_resume(struct usb_interface *iface) ++{ ++ int i, result = 0; ++ struct hso_net *hso_net; ++ ++ /* Start all serial ports */ ++ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { ++ if (serial_table[i] && (serial_table[i]->interface == iface)) { ++ if (atomic_read(&dev2ser(serial_table[i])->port.count)) { ++ result = ++ hso_start_serial_device(serial_table[i], GFP_NOIO); ++ hso_kick_transmit(dev2ser(serial_table[i])); ++ if (result) ++ goto out; ++ } ++ } ++ } ++ ++ /* Start all network ports */ ++ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { ++ if (network_table[i] && ++ (network_table[i]->interface == iface)) { ++ hso_net = dev2net(network_table[i]); ++ if (hso_net->flags & IFF_UP) { ++ /* First transmit any lingering data, ++ then restart the device. */ ++ if (hso_net->skb_tx_buf) { ++ dev_dbg(&iface->dev, ++ "Transmitting" ++ " lingering data\n"); ++ hso_net_start_xmit(hso_net->skb_tx_buf, ++ hso_net->net); ++ hso_net->skb_tx_buf = NULL; ++ } ++ result = hso_start_net_device(network_table[i]); ++ if (result) ++ goto out; ++ } ++ } ++ } ++ ++out: ++ return result; ++} ++ ++static void reset_device(struct work_struct *data) ++{ ++ struct hso_device *hso_dev = ++ container_of(data, struct hso_device, reset_device); ++ struct usb_device *usb = hso_dev->usb; ++ int result; ++ ++ if (hso_dev->usb_gone) { ++ D1("No reset during disconnect\n"); ++ } else { ++ result = usb_lock_device_for_reset(usb, hso_dev->interface); ++ if (result < 0) ++ D1("unable to lock device for reset: %d\n", result); ++ else { ++ usb_reset_device(usb); ++ usb_unlock_device(usb); ++ } ++ } ++} ++ ++static void hso_serial_ref_free(struct kref *ref) ++{ ++ struct hso_device *hso_dev = container_of(ref, struct hso_device, ref); ++ ++ hso_free_serial_device(hso_dev); ++} ++ ++static void hso_free_interface(struct usb_interface *interface) ++{ ++ struct hso_serial *hso_dev; ++ int i; ++ ++ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { ++ if (serial_table[i] && ++ (serial_table[i]->interface == interface)) { ++ hso_dev = dev2ser(serial_table[i]); ++ tty_port_tty_hangup(&hso_dev->port, false); ++ mutex_lock(&hso_dev->parent->mutex); ++ hso_dev->parent->usb_gone = 1; ++ mutex_unlock(&hso_dev->parent->mutex); ++ kref_put(&serial_table[i]->ref, hso_serial_ref_free); ++ } ++ } ++ ++ for (i = 0; i < HSO_MAX_NET_DEVICES; i++) { ++ if (network_table[i] && ++ (network_table[i]->interface == interface)) { ++ struct rfkill *rfk = dev2net(network_table[i])->rfkill; ++ /* hso_stop_net_device doesn't stop the net queue since ++ * traffic needs to start it again when suspended */ ++ netif_stop_queue(dev2net(network_table[i])->net); ++ hso_stop_net_device(network_table[i]); ++ cancel_work_sync(&network_table[i]->async_put_intf); ++ cancel_work_sync(&network_table[i]->async_get_intf); ++ if (rfk) { ++ rfkill_unregister(rfk); ++ rfkill_destroy(rfk); ++ } ++ hso_free_net_device(network_table[i]); ++ } ++ } ++} ++ ++/* Helper functions */ ++ ++/* Get the endpoint ! */ ++static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf, ++ int type, int dir) ++{ ++ int i; ++ struct usb_host_interface *iface = intf->cur_altsetting; ++ struct usb_endpoint_descriptor *endp; ++ ++ for (i = 0; i < iface->desc.bNumEndpoints; i++) { ++ endp = &iface->endpoint[i].desc; ++ if (((endp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir) && ++ (usb_endpoint_type(endp) == type)) ++ return endp; ++ } ++ ++ return NULL; ++} ++ ++/* Get the byte that describes which ports are enabled */ ++static int hso_get_mux_ports(struct usb_interface *intf, unsigned char *ports) ++{ ++ int i; ++ struct usb_host_interface *iface = intf->cur_altsetting; ++ ++ if (iface->extralen == 3) { ++ *ports = iface->extra[2]; ++ return 0; ++ } ++ ++ for (i = 0; i < iface->desc.bNumEndpoints; i++) { ++ if (iface->endpoint[i].extralen == 3) { ++ *ports = iface->endpoint[i].extra[2]; ++ return 0; ++ } ++ } ++ ++ return -1; ++} ++ ++/* interrupt urb needs to be submitted, used for serial read of muxed port */ ++static int hso_mux_submit_intr_urb(struct hso_shared_int *shared_int, ++ struct usb_device *usb, gfp_t gfp) ++{ ++ int result; ++ ++ usb_fill_int_urb(shared_int->shared_intr_urb, usb, ++ usb_rcvintpipe(usb, ++ shared_int->intr_endp->bEndpointAddress & 0x7F), ++ shared_int->shared_intr_buf, ++ 1, ++ intr_callback, shared_int, ++ shared_int->intr_endp->bInterval); ++ ++ result = usb_submit_urb(shared_int->shared_intr_urb, gfp); ++ if (result) ++ dev_warn(&usb->dev, "%s failed mux_intr_urb %d\n", __func__, ++ result); ++ ++ return result; ++} ++ ++/* operations setup of the serial interface */ ++static const struct tty_operations hso_serial_ops = { ++ .open = hso_serial_open, ++ .close = hso_serial_close, ++ .write = hso_serial_write, ++ .write_room = hso_serial_write_room, ++ .ioctl = hso_serial_ioctl, ++ .set_termios = hso_serial_set_termios, ++ .chars_in_buffer = hso_serial_chars_in_buffer, ++ .tiocmget = hso_serial_tiocmget, ++ .tiocmset = hso_serial_tiocmset, ++ .get_icount = hso_get_count, ++ .unthrottle = hso_unthrottle ++}; ++ ++static struct usb_driver hso_driver = { ++ .name = driver_name, ++ .probe = hso_probe, ++ .disconnect = hso_disconnect, ++ .id_table = hso_ids, ++ .suspend = hso_suspend, ++ .resume = hso_resume, ++ .reset_resume = hso_resume, ++ .supports_autosuspend = 1, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++static int __init hso_init(void) ++{ ++ int i; ++ int result; ++ ++ /* put it in the log */ ++ printk(KERN_INFO "hso: %s\n", version); ++ ++ /* Initialise the serial table semaphore and table */ ++ spin_lock_init(&serial_table_lock); ++ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) ++ serial_table[i] = NULL; ++ ++ /* allocate our driver using the proper amount of supported minors */ ++ tty_drv = alloc_tty_driver(HSO_SERIAL_TTY_MINORS); ++ if (!tty_drv) ++ return -ENOMEM; ++ ++ /* fill in all needed values */ ++ tty_drv->driver_name = driver_name; ++ tty_drv->name = tty_filename; ++ ++ /* if major number is provided as parameter, use that one */ ++ if (tty_major) ++ tty_drv->major = tty_major; ++ ++ tty_drv->minor_start = 0; ++ tty_drv->type = TTY_DRIVER_TYPE_SERIAL; ++ tty_drv->subtype = SERIAL_TYPE_NORMAL; ++ tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; ++ tty_drv->init_termios = tty_std_termios; ++ hso_init_termios(&tty_drv->init_termios); ++ tty_set_operations(tty_drv, &hso_serial_ops); ++ ++ /* register the tty driver */ ++ result = tty_register_driver(tty_drv); ++ if (result) { ++ printk(KERN_ERR "%s - tty_register_driver failed(%d)\n", ++ __func__, result); ++ goto err_free_tty; ++ } ++ ++ /* register this module as an usb driver */ ++ result = usb_register(&hso_driver); ++ if (result) { ++ printk(KERN_ERR "Could not register hso driver? error: %d\n", ++ result); ++ goto err_unreg_tty; ++ } ++ ++ /* done */ ++ return 0; ++err_unreg_tty: ++ tty_unregister_driver(tty_drv); ++err_free_tty: ++ put_tty_driver(tty_drv); ++ return result; ++} ++ ++static void __exit hso_exit(void) ++{ ++ printk(KERN_INFO "hso: unloaded\n"); ++ ++ tty_unregister_driver(tty_drv); ++ put_tty_driver(tty_drv); ++ /* deregister the usb driver */ ++ usb_deregister(&hso_driver); ++} ++ ++/* Module definitions */ ++module_init(hso_init); ++module_exit(hso_exit); ++ ++MODULE_AUTHOR(MOD_AUTHOR); ++MODULE_DESCRIPTION(MOD_DESCRIPTION); ++MODULE_LICENSE(MOD_LICENSE); ++ ++/* change the debug level (eg: insmod hso.ko debug=0x04) */ ++MODULE_PARM_DESC(debug, "Level of debug [0x01 | 0x02 | 0x04 | 0x08 | 0x10]"); ++module_param(debug, int, S_IRUGO | S_IWUSR); ++ ++/* set the major tty number (eg: insmod hso.ko tty_major=245) */ ++MODULE_PARM_DESC(tty_major, "Set the major tty number"); ++module_param(tty_major, int, S_IRUGO | S_IWUSR); ++ ++/* disable network interface (eg: insmod hso.ko disable_net=1) */ ++MODULE_PARM_DESC(disable_net, "Disable the network interface"); ++module_param(disable_net, int, S_IRUGO | S_IWUSR); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/huawei_cdc_ncm.c backports-3.18.1-1/drivers/net/usb/huawei_cdc_ncm.c +--- backports-3.18.1-1.org/drivers/net/usb/huawei_cdc_ncm.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/huawei_cdc_ncm.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,221 @@ ++/* huawei_cdc_ncm.c - handles Huawei devices using the CDC NCM protocol as ++ * transport layer. ++ * Copyright (C) 2013 Enrico Mioso ++ * ++ * ++ * ABSTRACT: ++ * This driver handles devices resembling the CDC NCM standard, but ++ * encapsulating another protocol inside it. An example are some Huawei 3G ++ * devices, exposing an embedded AT channel where you can set up the NCM ++ * connection. ++ * This code has been heavily inspired by the cdc_mbim.c driver, which is ++ * Copyright (c) 2012 Smith Micro Software, Inc. ++ * Copyright (c) 2012 Bjørn Mork ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Driver data */ ++struct huawei_cdc_ncm_state { ++ struct cdc_ncm_ctx *ctx; ++ atomic_t pmcount; ++ struct usb_driver *subdriver; ++ struct usb_interface *control; ++ struct usb_interface *data; ++}; ++ ++static int huawei_cdc_ncm_manage_power(struct usbnet *usbnet_dev, int on) ++{ ++ struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; ++ int rv; ++ ++ if ((on && atomic_add_return(1, &drvstate->pmcount) == 1) || ++ (!on && atomic_dec_and_test(&drvstate->pmcount))) { ++ rv = usb_autopm_get_interface(usbnet_dev->intf); ++ usbnet_dev->intf->needs_remote_wakeup = on; ++ if (!rv) ++ usb_autopm_put_interface(usbnet_dev->intf); ++ } ++ return 0; ++} ++ ++static int huawei_cdc_ncm_wdm_manage_power(struct usb_interface *intf, ++ int status) ++{ ++ struct usbnet *usbnet_dev = usb_get_intfdata(intf); ++ ++ /* can be called while disconnecting */ ++ if (!usbnet_dev) ++ return 0; ++ ++ return huawei_cdc_ncm_manage_power(usbnet_dev, status); ++} ++ ++ ++static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev, ++ struct usb_interface *intf) ++{ ++ struct cdc_ncm_ctx *ctx; ++ struct usb_driver *subdriver = ERR_PTR(-ENODEV); ++ int ret = -ENODEV; ++ struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; ++ ++ /* altsetting should always be 1 for NCM devices - so we hard-coded ++ * it here ++ */ ++ ret = cdc_ncm_bind_common(usbnet_dev, intf, 1); ++ if (ret) ++ goto err; ++ ++ ctx = drvstate->ctx; ++ ++ if (usbnet_dev->status) ++ /* The wMaxCommand buffer must be big enough to hold ++ * any message from the modem. Experience has shown ++ * that some replies are more than 256 bytes long ++ */ ++ subdriver = usb_cdc_wdm_register(ctx->control, ++ &usbnet_dev->status->desc, ++ 1024, /* wMaxCommand */ ++ huawei_cdc_ncm_wdm_manage_power); ++ if (IS_ERR(subdriver)) { ++ ret = PTR_ERR(subdriver); ++ cdc_ncm_unbind(usbnet_dev, intf); ++ goto err; ++ } ++ ++ /* Prevent usbnet from using the status descriptor */ ++ usbnet_dev->status = NULL; ++ ++ drvstate->subdriver = subdriver; ++ ++err: ++ return ret; ++} ++ ++static void huawei_cdc_ncm_unbind(struct usbnet *usbnet_dev, ++ struct usb_interface *intf) ++{ ++ struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; ++ struct cdc_ncm_ctx *ctx = drvstate->ctx; ++ ++ if (drvstate->subdriver && drvstate->subdriver->disconnect) ++ drvstate->subdriver->disconnect(ctx->control); ++ drvstate->subdriver = NULL; ++ ++ cdc_ncm_unbind(usbnet_dev, intf); ++} ++ ++static int huawei_cdc_ncm_suspend(struct usb_interface *intf, ++ pm_message_t message) ++{ ++ int ret = 0; ++ struct usbnet *usbnet_dev = usb_get_intfdata(intf); ++ struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; ++ struct cdc_ncm_ctx *ctx = drvstate->ctx; ++ ++ if (ctx == NULL) { ++ ret = -ENODEV; ++ goto error; ++ } ++ ++ ret = usbnet_suspend(intf, message); ++ if (ret < 0) ++ goto error; ++ ++ if (intf == ctx->control && ++ drvstate->subdriver && ++ drvstate->subdriver->suspend) ++ ret = drvstate->subdriver->suspend(intf, message); ++ if (ret < 0) ++ usbnet_resume(intf); ++ ++error: ++ return ret; ++} ++ ++static int huawei_cdc_ncm_resume(struct usb_interface *intf) ++{ ++ int ret = 0; ++ struct usbnet *usbnet_dev = usb_get_intfdata(intf); ++ struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; ++ bool callsub; ++ struct cdc_ncm_ctx *ctx = drvstate->ctx; ++ ++ /* should we call subdriver's resume function? */ ++ callsub = ++ (intf == ctx->control && ++ drvstate->subdriver && ++ drvstate->subdriver->resume); ++ ++ if (callsub) ++ ret = drvstate->subdriver->resume(intf); ++ if (ret < 0) ++ goto err; ++ ret = usbnet_resume(intf); ++ if (ret < 0 && callsub) ++ drvstate->subdriver->suspend(intf, PMSG_SUSPEND); ++err: ++ return ret; ++} ++ ++static const struct driver_info huawei_cdc_ncm_info = { ++ .description = "Huawei CDC NCM device", ++ .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN, ++ .bind = huawei_cdc_ncm_bind, ++ .unbind = huawei_cdc_ncm_unbind, ++ .manage_power = huawei_cdc_ncm_manage_power, ++ .rx_fixup = cdc_ncm_rx_fixup, ++ .tx_fixup = cdc_ncm_tx_fixup, ++}; ++ ++static const struct usb_device_id huawei_cdc_ncm_devs[] = { ++ /* Huawei NCM devices disguised as vendor specific */ ++ { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16), ++ .driver_info = (unsigned long)&huawei_cdc_ncm_info, ++ }, ++ { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46), ++ .driver_info = (unsigned long)&huawei_cdc_ncm_info, ++ }, ++ { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76), ++ .driver_info = (unsigned long)&huawei_cdc_ncm_info, ++ }, ++ { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x03, 0x16), ++ .driver_info = (unsigned long)&huawei_cdc_ncm_info, ++ }, ++ ++ /* Terminating entry */ ++ { ++ }, ++}; ++MODULE_DEVICE_TABLE(usb, huawei_cdc_ncm_devs); ++ ++static struct usb_driver huawei_cdc_ncm_driver = { ++ .name = "huawei_cdc_ncm", ++ .id_table = huawei_cdc_ncm_devs, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = huawei_cdc_ncm_suspend, ++ .resume = huawei_cdc_ncm_resume, ++ .reset_resume = huawei_cdc_ncm_resume, ++ .supports_autosuspend = 1, ++ .disable_hub_initiated_lpm = 1, ++}; ++module_usb_driver(huawei_cdc_ncm_driver); ++MODULE_AUTHOR("Enrico Mioso "); ++MODULE_DESCRIPTION("USB CDC NCM host driver with encapsulated protocol support"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/int51x1.c backports-3.18.1-1/drivers/net/usb/int51x1.c +--- backports-3.18.1-1.org/drivers/net/usb/int51x1.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/int51x1.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,199 @@ ++/* ++ * Copyright (c) 2009 Peter Holik ++ * ++ * Intellon usb PLC (Powerline Communications) usb net driver ++ * ++ * http://www.tandel.be/downloads/INT51X1_Datasheet.pdf ++ * ++ * Based on the work of Jan 'RedBully' Seiffert ++ */ ++ ++/* ++ * 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, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define INT51X1_VENDOR_ID 0x09e1 ++#define INT51X1_PRODUCT_ID 0x5121 ++ ++#define INT51X1_HEADER_SIZE 2 /* 2 byte header */ ++ ++#define PACKET_TYPE_PROMISCUOUS (1 << 0) ++#define PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ ++#define PACKET_TYPE_DIRECTED (1 << 2) ++#define PACKET_TYPE_BROADCAST (1 << 3) ++#define PACKET_TYPE_MULTICAST (1 << 4) /* filtered */ ++ ++#define SET_ETHERNET_PACKET_FILTER 0x43 ++ ++static int int51x1_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ int len; ++ ++ if (!(pskb_may_pull(skb, INT51X1_HEADER_SIZE))) { ++ netdev_err(dev->net, "unexpected tiny rx frame\n"); ++ return 0; ++ } ++ ++ len = le16_to_cpu(*(__le16 *)&skb->data[skb->len - 2]); ++ ++ skb_trim(skb, len); ++ ++ return 1; ++} ++ ++static struct sk_buff *int51x1_tx_fixup(struct usbnet *dev, ++ struct sk_buff *skb, gfp_t flags) ++{ ++ int pack_len = skb->len; ++ int pack_with_header_len = pack_len + INT51X1_HEADER_SIZE; ++ int headroom = skb_headroom(skb); ++ int tailroom = skb_tailroom(skb); ++ int need_tail = 0; ++ __le16 *len; ++ ++ /* if packet and our header is smaler than 64 pad to 64 (+ ZLP) */ ++ if ((pack_with_header_len) < dev->maxpacket) ++ need_tail = dev->maxpacket - pack_with_header_len + 1; ++ /* ++ * usbnet would send a ZLP if packetlength mod urbsize == 0 for us, ++ * but we need to know ourself, because this would add to the length ++ * we send down to the device... ++ */ ++ else if (!(pack_with_header_len % dev->maxpacket)) ++ need_tail = 1; ++ ++ if (!skb_cloned(skb) && ++ (headroom + tailroom >= need_tail + INT51X1_HEADER_SIZE)) { ++ if (headroom < INT51X1_HEADER_SIZE || tailroom < need_tail) { ++ skb->data = memmove(skb->head + INT51X1_HEADER_SIZE, ++ skb->data, skb->len); ++ skb_set_tail_pointer(skb, skb->len); ++ } ++ } else { ++ struct sk_buff *skb2; ++ ++ skb2 = skb_copy_expand(skb, ++ INT51X1_HEADER_SIZE, ++ need_tail, ++ flags); ++ dev_kfree_skb_any(skb); ++ if (!skb2) ++ return NULL; ++ skb = skb2; ++ } ++ ++ pack_len += need_tail; ++ pack_len &= 0x07ff; ++ ++ len = (__le16 *) __skb_push(skb, INT51X1_HEADER_SIZE); ++ *len = cpu_to_le16(pack_len); ++ ++ if(need_tail) ++ memset(__skb_put(skb, need_tail), 0, need_tail); ++ ++ return skb; ++} ++ ++static void int51x1_set_multicast(struct net_device *netdev) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ u16 filter = PACKET_TYPE_DIRECTED | PACKET_TYPE_BROADCAST; ++ ++ if (netdev->flags & IFF_PROMISC) { ++ /* do not expect to see traffic of other PLCs */ ++ filter |= PACKET_TYPE_PROMISCUOUS; ++ netdev_info(dev->net, "promiscuous mode enabled\n"); ++ } else if (!netdev_mc_empty(netdev) || ++ (netdev->flags & IFF_ALLMULTI)) { ++ filter |= PACKET_TYPE_ALL_MULTICAST; ++ netdev_dbg(dev->net, "receive all multicast enabled\n"); ++ } else { ++ /* ~PROMISCUOUS, ~MULTICAST */ ++ netdev_dbg(dev->net, "receive own packets only\n"); ++ } ++ ++ usbnet_write_cmd_async(dev, SET_ETHERNET_PACKET_FILTER, ++ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, ++ filter, 0, NULL, 0); ++} ++ ++static const struct net_device_ops int51x1_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = usbnet_change_mtu, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_set_rx_mode = int51x1_set_multicast, ++}; ++ ++static int int51x1_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int status = usbnet_get_ethernet_addr(dev, 3); ++ ++ if (status) ++ return status; ++ ++ dev->net->hard_header_len += INT51X1_HEADER_SIZE; ++ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; ++ dev->net->netdev_ops = &int51x1_netdev_ops; ++ ++ return usbnet_get_endpoints(dev, intf); ++} ++ ++static const struct driver_info int51x1_info = { ++ .description = "Intellon usb powerline adapter", ++ .bind = int51x1_bind, ++ .rx_fixup = int51x1_rx_fixup, ++ .tx_fixup = int51x1_tx_fixup, ++ .in = 1, ++ .out = 2, ++ .flags = FLAG_ETHER, ++}; ++ ++static const struct usb_device_id products[] = { ++ { ++ USB_DEVICE(INT51X1_VENDOR_ID, INT51X1_PRODUCT_ID), ++ .driver_info = (unsigned long) &int51x1_info, ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver int51x1_driver = { ++ .name = "int51x1", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(int51x1_driver); ++ ++MODULE_AUTHOR("Peter Holik"); ++MODULE_DESCRIPTION("Intellon usb powerline adapter"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/ipheth.c backports-3.18.1-1/drivers/net/usb/ipheth.c +--- backports-3.18.1-1.org/drivers/net/usb/ipheth.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/ipheth.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,588 @@ ++/* ++ * ipheth.c - Apple iPhone USB Ethernet driver ++ * ++ * Copyright (c) 2009 Diego Giagio ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of GIAGIO.COM nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * Alternatively, provided that this notice is retained in full, this ++ * software may be distributed under the terms of the GNU General ++ * Public License ("GPL") version 2, in which case the provisions of the ++ * GPL apply INSTEAD OF those given above. ++ * ++ * The provided data structures and external interfaces from this code ++ * are not restricted to be used by modules with a GPL compatible license. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ++ * ++ * Attention: iPhone device must be paired, otherwise it won't respond to our ++ * driver. For more info: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define USB_VENDOR_APPLE 0x05ac ++#define USB_PRODUCT_IPHONE 0x1290 ++#define USB_PRODUCT_IPHONE_3G 0x1292 ++#define USB_PRODUCT_IPHONE_3GS 0x1294 ++#define USB_PRODUCT_IPHONE_4 0x1297 ++#define USB_PRODUCT_IPAD 0x129a ++#define USB_PRODUCT_IPAD_2 0x12a2 ++#define USB_PRODUCT_IPAD_3 0x12a6 ++#define USB_PRODUCT_IPAD_MINI 0x12ab ++#define USB_PRODUCT_IPHONE_4_VZW 0x129c ++#define USB_PRODUCT_IPHONE_4S 0x12a0 ++#define USB_PRODUCT_IPHONE_5 0x12a8 ++ ++#define IPHETH_USBINTF_CLASS 255 ++#define IPHETH_USBINTF_SUBCLASS 253 ++#define IPHETH_USBINTF_PROTO 1 ++ ++#define IPHETH_BUF_SIZE 1516 ++#define IPHETH_IP_ALIGN 2 /* padding at front of URB */ ++#define IPHETH_TX_TIMEOUT (5 * HZ) ++ ++#define IPHETH_INTFNUM 2 ++#define IPHETH_ALT_INTFNUM 1 ++ ++#define IPHETH_CTRL_ENDP 0x00 ++#define IPHETH_CTRL_BUF_SIZE 0x40 ++#define IPHETH_CTRL_TIMEOUT (5 * HZ) ++ ++#define IPHETH_CMD_GET_MACADDR 0x00 ++#define IPHETH_CMD_CARRIER_CHECK 0x45 ++ ++#define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ) ++#define IPHETH_CARRIER_ON 0x04 ++ ++static struct usb_device_id ipheth_table[] = { ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3G, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3GS, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPAD, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPAD_2, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPAD_3, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPAD_MINI, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4_VZW, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4S, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { USB_DEVICE_AND_INTERFACE_INFO( ++ USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_5, ++ IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, ++ IPHETH_USBINTF_PROTO) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(usb, ipheth_table); ++ ++struct ipheth_device { ++ struct usb_device *udev; ++ struct usb_interface *intf; ++ struct net_device *net; ++ struct sk_buff *tx_skb; ++ struct urb *tx_urb; ++ struct urb *rx_urb; ++ unsigned char *tx_buf; ++ unsigned char *rx_buf; ++ unsigned char *ctrl_buf; ++ u8 bulk_in; ++ u8 bulk_out; ++ struct delayed_work carrier_work; ++}; ++ ++static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags); ++ ++static int ipheth_alloc_urbs(struct ipheth_device *iphone) ++{ ++ struct urb *tx_urb = NULL; ++ struct urb *rx_urb = NULL; ++ u8 *tx_buf = NULL; ++ u8 *rx_buf = NULL; ++ ++ tx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (tx_urb == NULL) ++ goto error_nomem; ++ ++ rx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (rx_urb == NULL) ++ goto free_tx_urb; ++ ++ tx_buf = usb_alloc_coherent(iphone->udev, IPHETH_BUF_SIZE, ++ GFP_KERNEL, &tx_urb->transfer_dma); ++ if (tx_buf == NULL) ++ goto free_rx_urb; ++ ++ rx_buf = usb_alloc_coherent(iphone->udev, IPHETH_BUF_SIZE, ++ GFP_KERNEL, &rx_urb->transfer_dma); ++ if (rx_buf == NULL) ++ goto free_tx_buf; ++ ++ ++ iphone->tx_urb = tx_urb; ++ iphone->rx_urb = rx_urb; ++ iphone->tx_buf = tx_buf; ++ iphone->rx_buf = rx_buf; ++ return 0; ++ ++free_tx_buf: ++ usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, tx_buf, ++ tx_urb->transfer_dma); ++free_rx_urb: ++ usb_free_urb(rx_urb); ++free_tx_urb: ++ usb_free_urb(tx_urb); ++error_nomem: ++ return -ENOMEM; ++} ++ ++static void ipheth_free_urbs(struct ipheth_device *iphone) ++{ ++ usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, iphone->rx_buf, ++ iphone->rx_urb->transfer_dma); ++ usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, iphone->tx_buf, ++ iphone->tx_urb->transfer_dma); ++ usb_free_urb(iphone->rx_urb); ++ usb_free_urb(iphone->tx_urb); ++} ++ ++static void ipheth_kill_urbs(struct ipheth_device *dev) ++{ ++ usb_kill_urb(dev->tx_urb); ++ usb_kill_urb(dev->rx_urb); ++} ++ ++static void ipheth_rcvbulk_callback(struct urb *urb) ++{ ++ struct ipheth_device *dev; ++ struct sk_buff *skb; ++ int status; ++ char *buf; ++ int len; ++ ++ dev = urb->context; ++ if (dev == NULL) ++ return; ++ ++ status = urb->status; ++ switch (status) { ++ case -ENOENT: ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ return; ++ case 0: ++ break; ++ default: ++ dev_err(&dev->intf->dev, "%s: urb status: %d\n", ++ __func__, status); ++ return; ++ } ++ ++ if (urb->actual_length <= IPHETH_IP_ALIGN) { ++ dev->net->stats.rx_length_errors++; ++ return; ++ } ++ len = urb->actual_length - IPHETH_IP_ALIGN; ++ buf = urb->transfer_buffer + IPHETH_IP_ALIGN; ++ ++ skb = dev_alloc_skb(len); ++ if (!skb) { ++ dev_err(&dev->intf->dev, "%s: dev_alloc_skb: -ENOMEM\n", ++ __func__); ++ dev->net->stats.rx_dropped++; ++ return; ++ } ++ ++ memcpy(skb_put(skb, len), buf, len); ++ skb->dev = dev->net; ++ skb->protocol = eth_type_trans(skb, dev->net); ++ ++ dev->net->stats.rx_packets++; ++ dev->net->stats.rx_bytes += len; ++ ++ netif_rx(skb); ++ ipheth_rx_submit(dev, GFP_ATOMIC); ++} ++ ++static void ipheth_sndbulk_callback(struct urb *urb) ++{ ++ struct ipheth_device *dev; ++ int status = urb->status; ++ ++ dev = urb->context; ++ if (dev == NULL) ++ return; ++ ++ if (status != 0 && ++ status != -ENOENT && ++ status != -ECONNRESET && ++ status != -ESHUTDOWN) ++ dev_err(&dev->intf->dev, "%s: urb status: %d\n", ++ __func__, status); ++ ++ dev_kfree_skb_irq(dev->tx_skb); ++ netif_wake_queue(dev->net); ++} ++ ++static int ipheth_carrier_set(struct ipheth_device *dev) ++{ ++ struct usb_device *udev = dev->udev; ++ int retval; ++ ++ retval = usb_control_msg(udev, ++ usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP), ++ IPHETH_CMD_CARRIER_CHECK, /* request */ ++ 0xc0, /* request type */ ++ 0x00, /* value */ ++ 0x02, /* index */ ++ dev->ctrl_buf, IPHETH_CTRL_BUF_SIZE, ++ IPHETH_CTRL_TIMEOUT); ++ if (retval < 0) { ++ dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n", ++ __func__, retval); ++ return retval; ++ } ++ ++ if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON) ++ netif_carrier_on(dev->net); ++ else ++ netif_carrier_off(dev->net); ++ ++ return 0; ++} ++ ++static void ipheth_carrier_check_work(struct work_struct *work) ++{ ++ struct ipheth_device *dev = container_of(work, struct ipheth_device, ++ carrier_work.work); ++ ++ ipheth_carrier_set(dev); ++ schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); ++} ++ ++static int ipheth_get_macaddr(struct ipheth_device *dev) ++{ ++ struct usb_device *udev = dev->udev; ++ struct net_device *net = dev->net; ++ int retval; ++ ++ retval = usb_control_msg(udev, ++ usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP), ++ IPHETH_CMD_GET_MACADDR, /* request */ ++ 0xc0, /* request type */ ++ 0x00, /* value */ ++ 0x02, /* index */ ++ dev->ctrl_buf, ++ IPHETH_CTRL_BUF_SIZE, ++ IPHETH_CTRL_TIMEOUT); ++ if (retval < 0) { ++ dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n", ++ __func__, retval); ++ } else if (retval < ETH_ALEN) { ++ dev_err(&dev->intf->dev, ++ "%s: usb_control_msg: short packet: %d bytes\n", ++ __func__, retval); ++ retval = -EINVAL; ++ } else { ++ memcpy(net->dev_addr, dev->ctrl_buf, ETH_ALEN); ++ retval = 0; ++ } ++ ++ return retval; ++} ++ ++static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags) ++{ ++ struct usb_device *udev = dev->udev; ++ int retval; ++ ++ usb_fill_bulk_urb(dev->rx_urb, udev, ++ usb_rcvbulkpipe(udev, dev->bulk_in), ++ dev->rx_buf, IPHETH_BUF_SIZE, ++ ipheth_rcvbulk_callback, ++ dev); ++ dev->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ retval = usb_submit_urb(dev->rx_urb, mem_flags); ++ if (retval) ++ dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n", ++ __func__, retval); ++ return retval; ++} ++ ++static int ipheth_open(struct net_device *net) ++{ ++ struct ipheth_device *dev = netdev_priv(net); ++ struct usb_device *udev = dev->udev; ++ int retval = 0; ++ ++ usb_set_interface(udev, IPHETH_INTFNUM, IPHETH_ALT_INTFNUM); ++ ++ retval = ipheth_carrier_set(dev); ++ if (retval) ++ return retval; ++ ++ retval = ipheth_rx_submit(dev, GFP_KERNEL); ++ if (retval) ++ return retval; ++ ++ schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); ++ netif_start_queue(net); ++ return retval; ++} ++ ++static int ipheth_close(struct net_device *net) ++{ ++ struct ipheth_device *dev = netdev_priv(net); ++ ++ cancel_delayed_work_sync(&dev->carrier_work); ++ netif_stop_queue(net); ++ return 0; ++} ++ ++static int ipheth_tx(struct sk_buff *skb, struct net_device *net) ++{ ++ struct ipheth_device *dev = netdev_priv(net); ++ struct usb_device *udev = dev->udev; ++ int retval; ++ ++ /* Paranoid */ ++ if (skb->len > IPHETH_BUF_SIZE) { ++ WARN(1, "%s: skb too large: %d bytes\n", __func__, skb->len); ++ dev->net->stats.tx_dropped++; ++ dev_kfree_skb_irq(skb); ++ return NETDEV_TX_OK; ++ } ++ ++ memcpy(dev->tx_buf, skb->data, skb->len); ++ if (skb->len < IPHETH_BUF_SIZE) ++ memset(dev->tx_buf + skb->len, 0, IPHETH_BUF_SIZE - skb->len); ++ ++ usb_fill_bulk_urb(dev->tx_urb, udev, ++ usb_sndbulkpipe(udev, dev->bulk_out), ++ dev->tx_buf, IPHETH_BUF_SIZE, ++ ipheth_sndbulk_callback, ++ dev); ++ dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC); ++ if (retval) { ++ dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n", ++ __func__, retval); ++ dev->net->stats.tx_errors++; ++ dev_kfree_skb_irq(skb); ++ } else { ++ dev->tx_skb = skb; ++ ++ dev->net->stats.tx_packets++; ++ dev->net->stats.tx_bytes += skb->len; ++ netif_stop_queue(net); ++ } ++ ++ return NETDEV_TX_OK; ++} ++ ++static void ipheth_tx_timeout(struct net_device *net) ++{ ++ struct ipheth_device *dev = netdev_priv(net); ++ ++ dev_err(&dev->intf->dev, "%s: TX timeout\n", __func__); ++ dev->net->stats.tx_errors++; ++ usb_unlink_urb(dev->tx_urb); ++} ++ ++static u32 ipheth_ethtool_op_get_link(struct net_device *net) ++{ ++ struct ipheth_device *dev = netdev_priv(net); ++ return netif_carrier_ok(dev->net); ++} ++ ++static const struct ethtool_ops ops = { ++ .get_link = ipheth_ethtool_op_get_link ++}; ++ ++static const struct net_device_ops ipheth_netdev_ops = { ++ .ndo_open = ipheth_open, ++ .ndo_stop = ipheth_close, ++ .ndo_start_xmit = ipheth_tx, ++ .ndo_tx_timeout = ipheth_tx_timeout, ++}; ++ ++static int ipheth_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ struct usb_device *udev = interface_to_usbdev(intf); ++ struct usb_host_interface *hintf; ++ struct usb_endpoint_descriptor *endp; ++ struct ipheth_device *dev; ++ struct net_device *netdev; ++ int i; ++ int retval; ++ ++ netdev = alloc_etherdev(sizeof(struct ipheth_device)); ++ if (!netdev) ++ return -ENOMEM; ++ ++ netdev->netdev_ops = &ipheth_netdev_ops; ++ netdev->watchdog_timeo = IPHETH_TX_TIMEOUT; ++ strcpy(netdev->name, "eth%d"); ++ ++ dev = netdev_priv(netdev); ++ dev->udev = udev; ++ dev->net = netdev; ++ dev->intf = intf; ++ ++ /* Set up endpoints */ ++ hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM); ++ if (hintf == NULL) { ++ retval = -ENODEV; ++ dev_err(&intf->dev, "Unable to find alternate settings interface\n"); ++ goto err_endpoints; ++ } ++ ++ for (i = 0; i < hintf->desc.bNumEndpoints; i++) { ++ endp = &hintf->endpoint[i].desc; ++ if (usb_endpoint_is_bulk_in(endp)) ++ dev->bulk_in = endp->bEndpointAddress; ++ else if (usb_endpoint_is_bulk_out(endp)) ++ dev->bulk_out = endp->bEndpointAddress; ++ } ++ if (!(dev->bulk_in && dev->bulk_out)) { ++ retval = -ENODEV; ++ dev_err(&intf->dev, "Unable to find endpoints\n"); ++ goto err_endpoints; ++ } ++ ++ dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL); ++ if (dev->ctrl_buf == NULL) { ++ retval = -ENOMEM; ++ goto err_alloc_ctrl_buf; ++ } ++ ++ retval = ipheth_get_macaddr(dev); ++ if (retval) ++ goto err_get_macaddr; ++ ++ INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work); ++ ++ retval = ipheth_alloc_urbs(dev); ++ if (retval) { ++ dev_err(&intf->dev, "error allocating urbs: %d\n", retval); ++ goto err_alloc_urbs; ++ } ++ ++ usb_set_intfdata(intf, dev); ++ ++ SET_NETDEV_DEV(netdev, &intf->dev); ++ netdev->ethtool_ops = &ops; ++ ++ retval = register_netdev(netdev); ++ if (retval) { ++ dev_err(&intf->dev, "error registering netdev: %d\n", retval); ++ retval = -EIO; ++ goto err_register_netdev; ++ } ++ ++ dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n"); ++ return 0; ++ ++err_register_netdev: ++ ipheth_free_urbs(dev); ++err_alloc_urbs: ++err_get_macaddr: ++err_alloc_ctrl_buf: ++ kfree(dev->ctrl_buf); ++err_endpoints: ++ free_netdev(netdev); ++ return retval; ++} ++ ++static void ipheth_disconnect(struct usb_interface *intf) ++{ ++ struct ipheth_device *dev; ++ ++ dev = usb_get_intfdata(intf); ++ if (dev != NULL) { ++ unregister_netdev(dev->net); ++ ipheth_kill_urbs(dev); ++ ipheth_free_urbs(dev); ++ kfree(dev->ctrl_buf); ++ free_netdev(dev->net); ++ } ++ usb_set_intfdata(intf, NULL); ++ dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n"); ++} ++ ++static struct usb_driver ipheth_driver = { ++ .name = "ipheth", ++ .probe = ipheth_probe, ++ .disconnect = ipheth_disconnect, ++ .id_table = ipheth_table, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(ipheth_driver); ++ ++MODULE_AUTHOR("Diego Giagio "); ++MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver"); ++MODULE_LICENSE("Dual BSD/GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/kalmia.c backports-3.18.1-1/drivers/net/usb/kalmia.c +--- backports-3.18.1-1.org/drivers/net/usb/kalmia.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/kalmia.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,366 @@ ++/* ++ * USB network interface driver for Samsung Kalmia based LTE USB modem like the ++ * Samsung GT-B3730 and GT-B3710. ++ * ++ * Copyright (C) 2011 Marius Bjoernstad Kotsbak ++ * ++ * Sponsored by Quicklink Video Distribution Services Ltd. ++ * ++ * Based on the cdc_eem module. ++ * ++ * 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * The Samsung Kalmia based LTE USB modems have a CDC ACM port for modem control ++ * handled by the "option" module and an ethernet data port handled by this ++ * module. ++ * ++ * The stick must first be switched into modem mode by usb_modeswitch ++ * or similar tool. Then the modem gets sent two initialization packets by ++ * this module, which gives the MAC address of the device. User space can then ++ * connect the modem using AT commands through the ACM port and then use ++ * DHCP on the network interface exposed by this module. Network packets are ++ * sent to and from the modem in a proprietary format discovered after watching ++ * the behavior of the windows driver for the modem. ++ * ++ * More information about the use of the modem is available in usb_modeswitch ++ * forum and the project page: ++ * ++ * http://www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?t=465 ++ * https://github.com/mkotsbak/Samsung-GT-B3730-linux-driver ++ */ ++ ++/* #define DEBUG */ ++/* #define VERBOSE */ ++ ++#define KALMIA_HEADER_LENGTH 6 ++#define KALMIA_ALIGN_SIZE 4 ++#define KALMIA_USB_TIMEOUT 10000 ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int ++kalmia_send_init_packet(struct usbnet *dev, u8 *init_msg, u8 init_msg_len, ++ u8 *buffer, u8 expected_len) ++{ ++ int act_len; ++ int status; ++ ++ netdev_dbg(dev->net, "Sending init packet"); ++ ++ status = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 0x02), ++ init_msg, init_msg_len, &act_len, KALMIA_USB_TIMEOUT); ++ if (status != 0) { ++ netdev_err(dev->net, ++ "Error sending init packet. Status %i, length %i\n", ++ status, act_len); ++ return status; ++ } ++ else if (act_len != init_msg_len) { ++ netdev_err(dev->net, ++ "Did not send all of init packet. Bytes sent: %i", ++ act_len); ++ } ++ else { ++ netdev_dbg(dev->net, "Successfully sent init packet."); ++ } ++ ++ status = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, 0x81), ++ buffer, expected_len, &act_len, KALMIA_USB_TIMEOUT); ++ ++ if (status != 0) ++ netdev_err(dev->net, ++ "Error receiving init result. Status %i, length %i\n", ++ status, act_len); ++ else if (act_len != expected_len) ++ netdev_err(dev->net, "Unexpected init result length: %i\n", ++ act_len); ++ ++ return status; ++} ++ ++static int ++kalmia_init_and_get_ethernet_addr(struct usbnet *dev, u8 *ethernet_addr) ++{ ++ static const char init_msg_1[] = ++ { 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, ++ 0x00, 0x00 }; ++ static const char init_msg_2[] = ++ { 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xf4, ++ 0x00, 0x00 }; ++ static const int buflen = 28; ++ char *usb_buf; ++ int status; ++ ++ usb_buf = kmalloc(buflen, GFP_DMA | GFP_KERNEL); ++ if (!usb_buf) ++ return -ENOMEM; ++ ++ memcpy(usb_buf, init_msg_1, 12); ++ status = kalmia_send_init_packet(dev, usb_buf, sizeof(init_msg_1) ++ / sizeof(init_msg_1[0]), usb_buf, 24); ++ if (status != 0) ++ return status; ++ ++ memcpy(usb_buf, init_msg_2, 12); ++ status = kalmia_send_init_packet(dev, usb_buf, sizeof(init_msg_2) ++ / sizeof(init_msg_2[0]), usb_buf, 28); ++ if (status != 0) ++ return status; ++ ++ memcpy(ethernet_addr, usb_buf + 10, ETH_ALEN); ++ ++ kfree(usb_buf); ++ return status; ++} ++ ++static int ++kalmia_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int status; ++ u8 ethernet_addr[ETH_ALEN]; ++ ++ /* Don't bind to AT command interface */ ++ if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) ++ return -EINVAL; ++ ++ dev->in = usb_rcvbulkpipe(dev->udev, 0x81 & USB_ENDPOINT_NUMBER_MASK); ++ dev->out = usb_sndbulkpipe(dev->udev, 0x02 & USB_ENDPOINT_NUMBER_MASK); ++ dev->status = NULL; ++ ++ dev->net->hard_header_len += KALMIA_HEADER_LENGTH; ++ dev->hard_mtu = 1400; ++ dev->rx_urb_size = dev->hard_mtu * 10; // Found as optimal after testing ++ ++ status = kalmia_init_and_get_ethernet_addr(dev, ethernet_addr); ++ ++ if (status < 0) { ++ usb_set_intfdata(intf, NULL); ++ usb_driver_release_interface(driver_of(intf), intf); ++ return status; ++ } ++ ++ memcpy(dev->net->dev_addr, ethernet_addr, ETH_ALEN); ++ ++ return status; ++} ++ ++static struct sk_buff * ++kalmia_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) ++{ ++ struct sk_buff *skb2 = NULL; ++ u16 content_len; ++ unsigned char *header_start; ++ unsigned char ether_type_1, ether_type_2; ++ u8 remainder, padlen = 0; ++ ++ if (!skb_cloned(skb)) { ++ int headroom = skb_headroom(skb); ++ int tailroom = skb_tailroom(skb); ++ ++ if ((tailroom >= KALMIA_ALIGN_SIZE) && (headroom ++ >= KALMIA_HEADER_LENGTH)) ++ goto done; ++ ++ if ((headroom + tailroom) > (KALMIA_HEADER_LENGTH ++ + KALMIA_ALIGN_SIZE)) { ++ skb->data = memmove(skb->head + KALMIA_HEADER_LENGTH, ++ skb->data, skb->len); ++ skb_set_tail_pointer(skb, skb->len); ++ goto done; ++ } ++ } ++ ++ skb2 = skb_copy_expand(skb, KALMIA_HEADER_LENGTH, ++ KALMIA_ALIGN_SIZE, flags); ++ if (!skb2) ++ return NULL; ++ ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ ++done: ++ header_start = skb_push(skb, KALMIA_HEADER_LENGTH); ++ ether_type_1 = header_start[KALMIA_HEADER_LENGTH + 12]; ++ ether_type_2 = header_start[KALMIA_HEADER_LENGTH + 13]; ++ ++ netdev_dbg(dev->net, "Sending etherType: %02x%02x", ether_type_1, ++ ether_type_2); ++ ++ /* According to empiric data for data packages */ ++ header_start[0] = 0x57; ++ header_start[1] = 0x44; ++ content_len = skb->len - KALMIA_HEADER_LENGTH; ++ ++ put_unaligned_le16(content_len, &header_start[2]); ++ header_start[4] = ether_type_1; ++ header_start[5] = ether_type_2; ++ ++ /* Align to 4 bytes by padding with zeros */ ++ remainder = skb->len % KALMIA_ALIGN_SIZE; ++ if (remainder > 0) { ++ padlen = KALMIA_ALIGN_SIZE - remainder; ++ memset(skb_put(skb, padlen), 0, padlen); ++ } ++ ++ netdev_dbg(dev->net, ++ "Sending package with length %i and padding %i. Header: %6phC.", ++ content_len, padlen, header_start); ++ ++ return skb; ++} ++ ++static int ++kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ /* ++ * Our task here is to strip off framing, leaving skb with one ++ * data frame for the usbnet framework code to process. ++ */ ++ static const u8 HEADER_END_OF_USB_PACKET[] = ++ { 0x57, 0x5a, 0x00, 0x00, 0x08, 0x00 }; ++ static const u8 EXPECTED_UNKNOWN_HEADER_1[] = ++ { 0x57, 0x43, 0x1e, 0x00, 0x15, 0x02 }; ++ static const u8 EXPECTED_UNKNOWN_HEADER_2[] = ++ { 0x57, 0x50, 0x0e, 0x00, 0x00, 0x00 }; ++ int i = 0; ++ ++ /* incomplete header? */ ++ if (skb->len < KALMIA_HEADER_LENGTH) ++ return 0; ++ ++ do { ++ struct sk_buff *skb2 = NULL; ++ u8 *header_start; ++ u16 usb_packet_length, ether_packet_length; ++ int is_last; ++ ++ header_start = skb->data; ++ ++ if (unlikely(header_start[0] != 0x57 || header_start[1] != 0x44)) { ++ if (!memcmp(header_start, EXPECTED_UNKNOWN_HEADER_1, ++ sizeof(EXPECTED_UNKNOWN_HEADER_1)) || !memcmp( ++ header_start, EXPECTED_UNKNOWN_HEADER_2, ++ sizeof(EXPECTED_UNKNOWN_HEADER_2))) { ++ netdev_dbg(dev->net, ++ "Received expected unknown frame header: %6phC. Package length: %i\n", ++ header_start, ++ skb->len - KALMIA_HEADER_LENGTH); ++ } ++ else { ++ netdev_err(dev->net, ++ "Received unknown frame header: %6phC. Package length: %i\n", ++ header_start, ++ skb->len - KALMIA_HEADER_LENGTH); ++ return 0; ++ } ++ } ++ else ++ netdev_dbg(dev->net, ++ "Received header: %6phC. Package length: %i\n", ++ header_start, skb->len - KALMIA_HEADER_LENGTH); ++ ++ /* subtract start header and end header */ ++ usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH); ++ ether_packet_length = get_unaligned_le16(&header_start[2]); ++ skb_pull(skb, KALMIA_HEADER_LENGTH); ++ ++ /* Some small packets misses end marker */ ++ if (usb_packet_length < ether_packet_length) { ++ ether_packet_length = usb_packet_length ++ + KALMIA_HEADER_LENGTH; ++ is_last = true; ++ } ++ else { ++ netdev_dbg(dev->net, "Correct package length #%i", i ++ + 1); ++ ++ is_last = (memcmp(skb->data + ether_packet_length, ++ HEADER_END_OF_USB_PACKET, ++ sizeof(HEADER_END_OF_USB_PACKET)) == 0); ++ if (!is_last) { ++ header_start = skb->data + ether_packet_length; ++ netdev_dbg(dev->net, ++ "End header: %6phC. Package length: %i\n", ++ header_start, ++ skb->len - KALMIA_HEADER_LENGTH); ++ } ++ } ++ ++ if (is_last) { ++ skb2 = skb; ++ } ++ else { ++ skb2 = skb_clone(skb, GFP_ATOMIC); ++ if (unlikely(!skb2)) ++ return 0; ++ } ++ ++ skb_trim(skb2, ether_packet_length); ++ ++ if (is_last) { ++ return 1; ++ } ++ else { ++ usbnet_skb_return(dev, skb2); ++ skb_pull(skb, ether_packet_length); ++ } ++ ++ i++; ++ } ++ while (skb->len); ++ ++ return 1; ++} ++ ++static const struct driver_info kalmia_info = { ++ .description = "Samsung Kalmia LTE USB dongle", ++ .flags = FLAG_WWAN, ++ .bind = kalmia_bind, ++ .rx_fixup = kalmia_rx_fixup, ++ .tx_fixup = kalmia_tx_fixup ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static const struct usb_device_id products[] = { ++ /* The unswitched USB ID, to get the module auto loaded: */ ++ { USB_DEVICE(0x04e8, 0x689a) }, ++ /* The stick swithed into modem (by e.g. usb_modeswitch): */ ++ { USB_DEVICE(0x04e8, 0x6889), ++ .driver_info = (unsigned long) &kalmia_info, }, ++ { /* EMPTY == end of list */} }; ++MODULE_DEVICE_TABLE( usb, products); ++ ++static struct usb_driver kalmia_driver = { ++ .name = "kalmia", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(kalmia_driver); ++ ++MODULE_AUTHOR("Marius Bjoernstad Kotsbak "); ++MODULE_DESCRIPTION("Samsung Kalmia USB network driver"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/kaweth.c backports-3.18.1-1/drivers/net/usb/kaweth.c +--- backports-3.18.1-1.org/drivers/net/usb/kaweth.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/kaweth.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,1331 @@ ++/**************************************************************** ++ * ++ * kaweth.c - driver for KL5KUSB101 based USB->Ethernet ++ * ++ * (c) 2000 Interlan Communications ++ * (c) 2000 Stephane Alnet ++ * (C) 2001 Brad Hards ++ * (C) 2002 Oliver Neukum ++ * ++ * Original author: The Zapman ++ * Inspired by, and much credit goes to Michael Rothwell ++ * for the test equipment, help, and patience ++ * Based off of (and with thanks to) Petko Manolov's pegaus.c driver. ++ * Also many thanks to Joel Silverman and Ed Surprenant at Kawasaki ++ * for providing the firmware and driver resources. ++ * ++ * 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, 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, see . ++ * ++ ****************************************************************/ ++ ++/* TODO: ++ * Develop test procedures for USB net interfaces ++ * Run test procedures ++ * Fix bugs from previous two steps ++ * Snoop other OSs for any tricks we're not doing ++ * Reduce arbitrary timeouts ++ * Smart multicast support ++ * Temporary MAC change support ++ * Tunable SOFs parameter - ioctl()? ++ * Ethernet stats collection ++ * Code formatting improvements ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#undef DEBUG ++ ++#define KAWETH_MTU 1514 ++#define KAWETH_BUF_SIZE 1664 ++#define KAWETH_TX_TIMEOUT (5 * HZ) ++#define KAWETH_SCRATCH_SIZE 32 ++#define KAWETH_FIRMWARE_BUF_SIZE 4096 ++#define KAWETH_CONTROL_TIMEOUT (30000) ++ ++#define KAWETH_STATUS_BROKEN 0x0000001 ++#define KAWETH_STATUS_CLOSING 0x0000002 ++#define KAWETH_STATUS_SUSPENDING 0x0000004 ++ ++#define KAWETH_STATUS_BLOCKED (KAWETH_STATUS_CLOSING | KAWETH_STATUS_SUSPENDING) ++ ++#define KAWETH_PACKET_FILTER_PROMISCUOUS 0x01 ++#define KAWETH_PACKET_FILTER_ALL_MULTICAST 0x02 ++#define KAWETH_PACKET_FILTER_DIRECTED 0x04 ++#define KAWETH_PACKET_FILTER_BROADCAST 0x08 ++#define KAWETH_PACKET_FILTER_MULTICAST 0x10 ++ ++/* Table 7 */ ++#define KAWETH_COMMAND_GET_ETHERNET_DESC 0x00 ++#define KAWETH_COMMAND_MULTICAST_FILTERS 0x01 ++#define KAWETH_COMMAND_SET_PACKET_FILTER 0x02 ++#define KAWETH_COMMAND_STATISTICS 0x03 ++#define KAWETH_COMMAND_SET_TEMP_MAC 0x06 ++#define KAWETH_COMMAND_GET_TEMP_MAC 0x07 ++#define KAWETH_COMMAND_SET_URB_SIZE 0x08 ++#define KAWETH_COMMAND_SET_SOFS_WAIT 0x09 ++#define KAWETH_COMMAND_SCAN 0xFF ++ ++#define KAWETH_SOFS_TO_WAIT 0x05 ++ ++#define INTBUFFERSIZE 4 ++ ++#define STATE_OFFSET 0 ++#define STATE_MASK 0x40 ++#define STATE_SHIFT 5 ++ ++#define IS_BLOCKED(s) (s & KAWETH_STATUS_BLOCKED) ++ ++ ++MODULE_AUTHOR("Michael Zappe , Stephane Alnet , Brad Hards and Oliver Neukum "); ++MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver"); ++MODULE_LICENSE("GPL"); ++MODULE_FIRMWARE("kaweth/new_code.bin"); ++MODULE_FIRMWARE("kaweth/new_code_fix.bin"); ++MODULE_FIRMWARE("kaweth/trigger_code.bin"); ++MODULE_FIRMWARE("kaweth/trigger_code_fix.bin"); ++ ++static const char driver_name[] = "kaweth"; ++ ++static int kaweth_probe( ++ struct usb_interface *intf, ++ const struct usb_device_id *id /* from id_table */ ++ ); ++static void kaweth_disconnect(struct usb_interface *intf); ++static int kaweth_internal_control_msg(struct usb_device *usb_dev, ++ unsigned int pipe, ++ struct usb_ctrlrequest *cmd, void *data, ++ int len, int timeout); ++static int kaweth_suspend(struct usb_interface *intf, pm_message_t message); ++static int kaweth_resume(struct usb_interface *intf); ++ ++/**************************************************************** ++ * usb_device_id ++ ****************************************************************/ ++static struct usb_device_id usb_klsi_table[] = { ++ { USB_DEVICE(0x03e8, 0x0008) }, /* AOX Endpoints USB Ethernet */ ++ { USB_DEVICE(0x04bb, 0x0901) }, /* I-O DATA USB-ET/T */ ++ { USB_DEVICE(0x0506, 0x03e8) }, /* 3Com 3C19250 */ ++ { USB_DEVICE(0x0506, 0x11f8) }, /* 3Com 3C460 */ ++ { USB_DEVICE(0x0557, 0x2002) }, /* ATEN USB Ethernet */ ++ { USB_DEVICE(0x0557, 0x4000) }, /* D-Link DSB-650C */ ++ { USB_DEVICE(0x0565, 0x0002) }, /* Peracom Enet */ ++ { USB_DEVICE(0x0565, 0x0003) }, /* Optus@Home UEP1045A */ ++ { USB_DEVICE(0x0565, 0x0005) }, /* Peracom Enet2 */ ++ { USB_DEVICE(0x05e9, 0x0008) }, /* KLSI KL5KUSB101B */ ++ { USB_DEVICE(0x05e9, 0x0009) }, /* KLSI KL5KUSB101B (Board change) */ ++ { USB_DEVICE(0x066b, 0x2202) }, /* Linksys USB10T */ ++ { USB_DEVICE(0x06e1, 0x0008) }, /* ADS USB-10BT */ ++ { USB_DEVICE(0x06e1, 0x0009) }, /* ADS USB-10BT */ ++ { USB_DEVICE(0x0707, 0x0100) }, /* SMC 2202USB */ ++ { USB_DEVICE(0x07aa, 0x0001) }, /* Correga K.K. */ ++ { USB_DEVICE(0x07b8, 0x4000) }, /* D-Link DU-E10 */ ++ { USB_DEVICE(0x07c9, 0xb010) }, /* Allied Telesyn AT-USB10 USB Ethernet Adapter */ ++ { USB_DEVICE(0x0846, 0x1001) }, /* NetGear EA-101 */ ++ { USB_DEVICE(0x0846, 0x1002) }, /* NetGear EA-101 */ ++ { USB_DEVICE(0x085a, 0x0008) }, /* PortGear Ethernet Adapter */ ++ { USB_DEVICE(0x085a, 0x0009) }, /* PortGear Ethernet Adapter */ ++ { USB_DEVICE(0x087d, 0x5704) }, /* Jaton USB Ethernet Device Adapter */ ++ { USB_DEVICE(0x0951, 0x0008) }, /* Kingston Technology USB Ethernet Adapter */ ++ { USB_DEVICE(0x095a, 0x3003) }, /* Portsmith Express Ethernet Adapter */ ++ { USB_DEVICE(0x10bd, 0x1427) }, /* ASANTE USB To Ethernet Adapter */ ++ { USB_DEVICE(0x1342, 0x0204) }, /* Mobility USB-Ethernet Adapter */ ++ { USB_DEVICE(0x13d2, 0x0400) }, /* Shark Pocket Adapter */ ++ { USB_DEVICE(0x1485, 0x0001) }, /* Silicom U2E */ ++ { USB_DEVICE(0x1485, 0x0002) }, /* Psion Dacom Gold Port Ethernet */ ++ { USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */ ++ { USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */ ++ { USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */ ++ { USB_DEVICE(0x1668, 0x0323) }, /* Actiontec USB Ethernet */ ++ { USB_DEVICE(0x2001, 0x4000) }, /* D-link DSB-650C */ ++ {} /* Null terminator */ ++}; ++ ++MODULE_DEVICE_TABLE (usb, usb_klsi_table); ++ ++/**************************************************************** ++ * kaweth_driver ++ ****************************************************************/ ++static struct usb_driver kaweth_driver = { ++ .name = driver_name, ++ .probe = kaweth_probe, ++ .disconnect = kaweth_disconnect, ++ .suspend = kaweth_suspend, ++ .resume = kaweth_resume, ++ .id_table = usb_klsi_table, ++ .supports_autosuspend = 1, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++typedef __u8 eth_addr_t[6]; ++ ++/**************************************************************** ++ * usb_eth_dev ++ ****************************************************************/ ++struct usb_eth_dev { ++ char *name; ++ __u16 vendor; ++ __u16 device; ++ void *pdata; ++}; ++ ++/**************************************************************** ++ * kaweth_ethernet_configuration ++ * Refer Table 8 ++ ****************************************************************/ ++struct kaweth_ethernet_configuration ++{ ++ __u8 size; ++ __u8 reserved1; ++ __u8 reserved2; ++ eth_addr_t hw_addr; ++ __u32 statistics_mask; ++ __le16 segment_size; ++ __u16 max_multicast_filters; ++ __u8 reserved3; ++} __packed; ++ ++/**************************************************************** ++ * kaweth_device ++ ****************************************************************/ ++struct kaweth_device ++{ ++ spinlock_t device_lock; ++ ++ __u32 status; ++ int end; ++ int suspend_lowmem_rx; ++ int suspend_lowmem_ctrl; ++ int linkstate; ++ int opened; ++ struct delayed_work lowmem_work; ++ ++ struct usb_device *dev; ++ struct usb_interface *intf; ++ struct net_device *net; ++ wait_queue_head_t term_wait; ++ ++ struct urb *rx_urb; ++ struct urb *tx_urb; ++ struct urb *irq_urb; ++ ++ dma_addr_t intbufferhandle; ++ __u8 *intbuffer; ++ dma_addr_t rxbufferhandle; ++ __u8 *rx_buf; ++ ++ ++ struct sk_buff *tx_skb; ++ ++ __u8 *firmware_buf; ++ __u8 scratch[KAWETH_SCRATCH_SIZE]; ++ __u16 packet_filter_bitmap; ++ ++ struct kaweth_ethernet_configuration configuration; ++ ++ struct net_device_stats stats; ++}; ++ ++/**************************************************************** ++ * kaweth_control ++ ****************************************************************/ ++static int kaweth_control(struct kaweth_device *kaweth, ++ unsigned int pipe, ++ __u8 request, ++ __u8 requesttype, ++ __u16 value, ++ __u16 index, ++ void *data, ++ __u16 size, ++ int timeout) ++{ ++ struct usb_ctrlrequest *dr; ++ int retval; ++ ++ netdev_dbg(kaweth->net, "kaweth_control()\n"); ++ ++ if(in_interrupt()) { ++ netdev_dbg(kaweth->net, "in_interrupt()\n"); ++ return -EBUSY; ++ } ++ ++ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); ++ if (!dr) ++ return -ENOMEM; ++ ++ dr->bRequestType = requesttype; ++ dr->bRequest = request; ++ dr->wValue = cpu_to_le16(value); ++ dr->wIndex = cpu_to_le16(index); ++ dr->wLength = cpu_to_le16(size); ++ ++ retval = kaweth_internal_control_msg(kaweth->dev, ++ pipe, ++ dr, ++ data, ++ size, ++ timeout); ++ ++ kfree(dr); ++ return retval; ++} ++ ++/**************************************************************** ++ * kaweth_read_configuration ++ ****************************************************************/ ++static int kaweth_read_configuration(struct kaweth_device *kaweth) ++{ ++ int retval; ++ ++ netdev_dbg(kaweth->net, "Reading kaweth configuration\n"); ++ ++ retval = kaweth_control(kaweth, ++ usb_rcvctrlpipe(kaweth->dev, 0), ++ KAWETH_COMMAND_GET_ETHERNET_DESC, ++ USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, ++ 0, ++ 0, ++ (void *)&kaweth->configuration, ++ sizeof(kaweth->configuration), ++ KAWETH_CONTROL_TIMEOUT); ++ ++ return retval; ++} ++ ++/**************************************************************** ++ * kaweth_set_urb_size ++ ****************************************************************/ ++static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size) ++{ ++ int retval; ++ ++ netdev_dbg(kaweth->net, "Setting URB size to %d\n", (unsigned)urb_size); ++ ++ retval = kaweth_control(kaweth, ++ usb_sndctrlpipe(kaweth->dev, 0), ++ KAWETH_COMMAND_SET_URB_SIZE, ++ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, ++ urb_size, ++ 0, ++ (void *)&kaweth->scratch, ++ 0, ++ KAWETH_CONTROL_TIMEOUT); ++ ++ return retval; ++} ++ ++/**************************************************************** ++ * kaweth_set_sofs_wait ++ ****************************************************************/ ++static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait) ++{ ++ int retval; ++ ++ netdev_dbg(kaweth->net, "Set SOFS wait to %d\n", (unsigned)sofs_wait); ++ ++ retval = kaweth_control(kaweth, ++ usb_sndctrlpipe(kaweth->dev, 0), ++ KAWETH_COMMAND_SET_SOFS_WAIT, ++ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, ++ sofs_wait, ++ 0, ++ (void *)&kaweth->scratch, ++ 0, ++ KAWETH_CONTROL_TIMEOUT); ++ ++ return retval; ++} ++ ++/**************************************************************** ++ * kaweth_set_receive_filter ++ ****************************************************************/ ++static int kaweth_set_receive_filter(struct kaweth_device *kaweth, ++ __u16 receive_filter) ++{ ++ int retval; ++ ++ netdev_dbg(kaweth->net, "Set receive filter to %d\n", ++ (unsigned)receive_filter); ++ ++ retval = kaweth_control(kaweth, ++ usb_sndctrlpipe(kaweth->dev, 0), ++ KAWETH_COMMAND_SET_PACKET_FILTER, ++ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, ++ receive_filter, ++ 0, ++ (void *)&kaweth->scratch, ++ 0, ++ KAWETH_CONTROL_TIMEOUT); ++ ++ return retval; ++} ++ ++/**************************************************************** ++ * kaweth_download_firmware ++ ****************************************************************/ ++static int kaweth_download_firmware(struct kaweth_device *kaweth, ++ const char *fwname, ++ __u8 interrupt, ++ __u8 type) ++{ ++ const struct firmware *fw; ++ int data_len; ++ int ret; ++ ++ ret = request_firmware(&fw, fwname, &kaweth->dev->dev); ++ if (ret) { ++ dev_err(&kaweth->intf->dev, "Firmware request failed\n"); ++ return ret; ++ } ++ ++ if (fw->size > KAWETH_FIRMWARE_BUF_SIZE) { ++ dev_err(&kaweth->intf->dev, "Firmware too big: %zu\n", ++ fw->size); ++ release_firmware(fw); ++ return -ENOSPC; ++ } ++ data_len = fw->size; ++ memcpy(kaweth->firmware_buf, fw->data, fw->size); ++ ++ release_firmware(fw); ++ ++ kaweth->firmware_buf[2] = (data_len & 0xFF) - 7; ++ kaweth->firmware_buf[3] = data_len >> 8; ++ kaweth->firmware_buf[4] = type; ++ kaweth->firmware_buf[5] = interrupt; ++ ++ netdev_dbg(kaweth->net, "High: %i, Low:%i\n", kaweth->firmware_buf[3], ++ kaweth->firmware_buf[2]); ++ ++ netdev_dbg(kaweth->net, ++ "Downloading firmware at %p to kaweth device at %p\n", ++ kaweth->firmware_buf, kaweth); ++ netdev_dbg(kaweth->net, "Firmware length: %d\n", data_len); ++ ++ return kaweth_control(kaweth, ++ usb_sndctrlpipe(kaweth->dev, 0), ++ KAWETH_COMMAND_SCAN, ++ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, ++ 0, ++ 0, ++ (void *)kaweth->firmware_buf, ++ data_len, ++ KAWETH_CONTROL_TIMEOUT); ++} ++ ++/**************************************************************** ++ * kaweth_trigger_firmware ++ ****************************************************************/ ++static int kaweth_trigger_firmware(struct kaweth_device *kaweth, ++ __u8 interrupt) ++{ ++ kaweth->firmware_buf[0] = 0xB6; ++ kaweth->firmware_buf[1] = 0xC3; ++ kaweth->firmware_buf[2] = 0x01; ++ kaweth->firmware_buf[3] = 0x00; ++ kaweth->firmware_buf[4] = 0x06; ++ kaweth->firmware_buf[5] = interrupt; ++ kaweth->firmware_buf[6] = 0x00; ++ kaweth->firmware_buf[7] = 0x00; ++ ++ netdev_dbg(kaweth->net, "Triggering firmware\n"); ++ ++ return kaweth_control(kaweth, ++ usb_sndctrlpipe(kaweth->dev, 0), ++ KAWETH_COMMAND_SCAN, ++ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, ++ 0, ++ 0, ++ (void *)kaweth->firmware_buf, ++ 8, ++ KAWETH_CONTROL_TIMEOUT); ++} ++ ++/**************************************************************** ++ * kaweth_reset ++ ****************************************************************/ ++static int kaweth_reset(struct kaweth_device *kaweth) ++{ ++ int result; ++ ++ netdev_dbg(kaweth->net, "kaweth_reset(%p)\n", kaweth); ++ result = usb_reset_configuration(kaweth->dev); ++ mdelay(10); ++ ++ netdev_dbg(kaweth->net, "kaweth_reset() returns %d.\n", result); ++ ++ return result; ++} ++ ++static void kaweth_usb_receive(struct urb *); ++static int kaweth_resubmit_rx_urb(struct kaweth_device *, gfp_t); ++ ++/**************************************************************** ++ int_callback ++*****************************************************************/ ++ ++static void kaweth_resubmit_int_urb(struct kaweth_device *kaweth, gfp_t mf) ++{ ++ int status; ++ ++ status = usb_submit_urb (kaweth->irq_urb, mf); ++ if (unlikely(status == -ENOMEM)) { ++ kaweth->suspend_lowmem_ctrl = 1; ++ schedule_delayed_work(&kaweth->lowmem_work, HZ/4); ++ } else { ++ kaweth->suspend_lowmem_ctrl = 0; ++ } ++ ++ if (status) ++ dev_err(&kaweth->intf->dev, ++ "can't resubmit intr, %s-%s, status %d\n", ++ kaweth->dev->bus->bus_name, ++ kaweth->dev->devpath, status); ++} ++ ++static void int_callback(struct urb *u) ++{ ++ struct kaweth_device *kaweth = u->context; ++ int act_state; ++ int status = u->status; ++ ++ switch (status) { ++ case 0: /* success */ ++ break; ++ case -ECONNRESET: /* unlink */ ++ case -ENOENT: ++ case -ESHUTDOWN: ++ return; ++ /* -EPIPE: should clear the halt */ ++ default: /* error */ ++ goto resubmit; ++ } ++ ++ /* we check the link state to report changes */ ++ if (kaweth->linkstate != (act_state = ( kaweth->intbuffer[STATE_OFFSET] | STATE_MASK) >> STATE_SHIFT)) { ++ if (act_state) ++ netif_carrier_on(kaweth->net); ++ else ++ netif_carrier_off(kaweth->net); ++ ++ kaweth->linkstate = act_state; ++ } ++resubmit: ++ kaweth_resubmit_int_urb(kaweth, GFP_ATOMIC); ++} ++ ++static void kaweth_resubmit_tl(struct work_struct *work) ++{ ++ struct kaweth_device *kaweth = ++ container_of(work, struct kaweth_device, lowmem_work.work); ++ ++ if (IS_BLOCKED(kaweth->status)) ++ return; ++ ++ if (kaweth->suspend_lowmem_rx) ++ kaweth_resubmit_rx_urb(kaweth, GFP_NOIO); ++ ++ if (kaweth->suspend_lowmem_ctrl) ++ kaweth_resubmit_int_urb(kaweth, GFP_NOIO); ++} ++ ++ ++/**************************************************************** ++ * kaweth_resubmit_rx_urb ++ ****************************************************************/ ++static int kaweth_resubmit_rx_urb(struct kaweth_device *kaweth, ++ gfp_t mem_flags) ++{ ++ int result; ++ ++ usb_fill_bulk_urb(kaweth->rx_urb, ++ kaweth->dev, ++ usb_rcvbulkpipe(kaweth->dev, 1), ++ kaweth->rx_buf, ++ KAWETH_BUF_SIZE, ++ kaweth_usb_receive, ++ kaweth); ++ kaweth->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ kaweth->rx_urb->transfer_dma = kaweth->rxbufferhandle; ++ ++ if((result = usb_submit_urb(kaweth->rx_urb, mem_flags))) { ++ if (result == -ENOMEM) { ++ kaweth->suspend_lowmem_rx = 1; ++ schedule_delayed_work(&kaweth->lowmem_work, HZ/4); ++ } ++ dev_err(&kaweth->intf->dev, "resubmitting rx_urb %d failed\n", ++ result); ++ } else { ++ kaweth->suspend_lowmem_rx = 0; ++ } ++ ++ return result; ++} ++ ++static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth); ++ ++/**************************************************************** ++ * kaweth_usb_receive ++ ****************************************************************/ ++static void kaweth_usb_receive(struct urb *urb) ++{ ++ struct device *dev = &urb->dev->dev; ++ struct kaweth_device *kaweth = urb->context; ++ struct net_device *net = kaweth->net; ++ int status = urb->status; ++ ++ int count = urb->actual_length; ++ int count2 = urb->transfer_buffer_length; ++ ++ __u16 pkt_len = le16_to_cpup((__le16 *)kaweth->rx_buf); ++ ++ struct sk_buff *skb; ++ ++ if (unlikely(status == -EPIPE)) { ++ kaweth->stats.rx_errors++; ++ kaweth->end = 1; ++ wake_up(&kaweth->term_wait); ++ dev_dbg(dev, "Status was -EPIPE.\n"); ++ return; ++ } ++ if (unlikely(status == -ECONNRESET || status == -ESHUTDOWN)) { ++ /* we are killed - set a flag and wake the disconnect handler */ ++ kaweth->end = 1; ++ wake_up(&kaweth->term_wait); ++ dev_dbg(dev, "Status was -ECONNRESET or -ESHUTDOWN.\n"); ++ return; ++ } ++ if (unlikely(status == -EPROTO || status == -ETIME || ++ status == -EILSEQ)) { ++ kaweth->stats.rx_errors++; ++ dev_dbg(dev, "Status was -EPROTO, -ETIME, or -EILSEQ.\n"); ++ return; ++ } ++ if (unlikely(status == -EOVERFLOW)) { ++ kaweth->stats.rx_errors++; ++ dev_dbg(dev, "Status was -EOVERFLOW.\n"); ++ } ++ spin_lock(&kaweth->device_lock); ++ if (IS_BLOCKED(kaweth->status)) { ++ spin_unlock(&kaweth->device_lock); ++ return; ++ } ++ spin_unlock(&kaweth->device_lock); ++ ++ if(status && status != -EREMOTEIO && count != 1) { ++ dev_err(&kaweth->intf->dev, ++ "%s RX status: %d count: %d packet_len: %d\n", ++ net->name, status, count, (int)pkt_len); ++ kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); ++ return; ++ } ++ ++ if(kaweth->net && (count > 2)) { ++ if(pkt_len > (count - 2)) { ++ dev_err(&kaweth->intf->dev, ++ "Packet length too long for USB frame (pkt_len: %x, count: %x)\n", ++ pkt_len, count); ++ dev_err(&kaweth->intf->dev, "Packet len & 2047: %x\n", ++ pkt_len & 2047); ++ dev_err(&kaweth->intf->dev, "Count 2: %x\n", count2); ++ kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); ++ return; ++ } ++ ++ if(!(skb = dev_alloc_skb(pkt_len+2))) { ++ kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); ++ return; ++ } ++ ++ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ ++ ++ skb_copy_to_linear_data(skb, kaweth->rx_buf + 2, pkt_len); ++ ++ skb_put(skb, pkt_len); ++ ++ skb->protocol = eth_type_trans(skb, net); ++ ++ netif_rx(skb); ++ ++ kaweth->stats.rx_packets++; ++ kaweth->stats.rx_bytes += pkt_len; ++ } ++ ++ kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); ++} ++ ++/**************************************************************** ++ * kaweth_open ++ ****************************************************************/ ++static int kaweth_open(struct net_device *net) ++{ ++ struct kaweth_device *kaweth = netdev_priv(net); ++ int res; ++ ++ netdev_dbg(kaweth->net, "Opening network device.\n"); ++ ++ res = usb_autopm_get_interface(kaweth->intf); ++ if (res) { ++ dev_err(&kaweth->intf->dev, "Interface cannot be resumed.\n"); ++ return -EIO; ++ } ++ res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL); ++ if (res) ++ goto err_out; ++ ++ usb_fill_int_urb( ++ kaweth->irq_urb, ++ kaweth->dev, ++ usb_rcvintpipe(kaweth->dev, 3), ++ kaweth->intbuffer, ++ INTBUFFERSIZE, ++ int_callback, ++ kaweth, ++ 250); /* overriding the descriptor */ ++ kaweth->irq_urb->transfer_dma = kaweth->intbufferhandle; ++ kaweth->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL); ++ if (res) { ++ usb_kill_urb(kaweth->rx_urb); ++ goto err_out; ++ } ++ kaweth->opened = 1; ++ ++ netif_start_queue(net); ++ ++ kaweth_async_set_rx_mode(kaweth); ++ return 0; ++ ++err_out: ++ usb_autopm_put_interface(kaweth->intf); ++ return -EIO; ++} ++ ++/**************************************************************** ++ * kaweth_kill_urbs ++ ****************************************************************/ ++static void kaweth_kill_urbs(struct kaweth_device *kaweth) ++{ ++ usb_kill_urb(kaweth->irq_urb); ++ usb_kill_urb(kaweth->rx_urb); ++ usb_kill_urb(kaweth->tx_urb); ++ ++ cancel_delayed_work_sync(&kaweth->lowmem_work); ++ ++ /* a scheduled work may have resubmitted, ++ we hit them again */ ++ usb_kill_urb(kaweth->irq_urb); ++ usb_kill_urb(kaweth->rx_urb); ++} ++ ++/**************************************************************** ++ * kaweth_close ++ ****************************************************************/ ++static int kaweth_close(struct net_device *net) ++{ ++ struct kaweth_device *kaweth = netdev_priv(net); ++ ++ netif_stop_queue(net); ++ kaweth->opened = 0; ++ ++ kaweth->status |= KAWETH_STATUS_CLOSING; ++ ++ kaweth_kill_urbs(kaweth); ++ ++ kaweth->status &= ~KAWETH_STATUS_CLOSING; ++ ++ usb_autopm_put_interface(kaweth->intf); ++ ++ return 0; ++} ++ ++static u32 kaweth_get_link(struct net_device *dev) ++{ ++ struct kaweth_device *kaweth = netdev_priv(dev); ++ ++ return kaweth->linkstate; ++} ++ ++static const struct ethtool_ops ops = { ++ .get_link = kaweth_get_link ++}; ++ ++/**************************************************************** ++ * kaweth_usb_transmit_complete ++ ****************************************************************/ ++static void kaweth_usb_transmit_complete(struct urb *urb) ++{ ++ struct kaweth_device *kaweth = urb->context; ++ struct sk_buff *skb = kaweth->tx_skb; ++ int status = urb->status; ++ ++ if (unlikely(status != 0)) ++ if (status != -ENOENT) ++ dev_dbg(&urb->dev->dev, "%s: TX status %d.\n", ++ kaweth->net->name, status); ++ ++ netif_wake_queue(kaweth->net); ++ dev_kfree_skb_irq(skb); ++} ++ ++/**************************************************************** ++ * kaweth_start_xmit ++ ****************************************************************/ ++static netdev_tx_t kaweth_start_xmit(struct sk_buff *skb, ++ struct net_device *net) ++{ ++ struct kaweth_device *kaweth = netdev_priv(net); ++ __le16 *private_header; ++ ++ int res; ++ ++ spin_lock_irq(&kaweth->device_lock); ++ ++ kaweth_async_set_rx_mode(kaweth); ++ netif_stop_queue(net); ++ if (IS_BLOCKED(kaweth->status)) { ++ goto skip; ++ } ++ ++ /* We now decide whether we can put our special header into the sk_buff */ ++ if (skb_cloned(skb) || skb_headroom(skb) < 2) { ++ /* no such luck - we make our own */ ++ struct sk_buff *copied_skb; ++ copied_skb = skb_copy_expand(skb, 2, 0, GFP_ATOMIC); ++ dev_kfree_skb_irq(skb); ++ skb = copied_skb; ++ if (!copied_skb) { ++ kaweth->stats.tx_errors++; ++ netif_start_queue(net); ++ spin_unlock_irq(&kaweth->device_lock); ++ return NETDEV_TX_OK; ++ } ++ } ++ ++ private_header = (__le16 *)__skb_push(skb, 2); ++ *private_header = cpu_to_le16(skb->len-2); ++ kaweth->tx_skb = skb; ++ ++ usb_fill_bulk_urb(kaweth->tx_urb, ++ kaweth->dev, ++ usb_sndbulkpipe(kaweth->dev, 2), ++ private_header, ++ skb->len, ++ kaweth_usb_transmit_complete, ++ kaweth); ++ kaweth->end = 0; ++ ++ if((res = usb_submit_urb(kaweth->tx_urb, GFP_ATOMIC))) ++ { ++ dev_warn(&net->dev, "kaweth failed tx_urb %d\n", res); ++skip: ++ kaweth->stats.tx_errors++; ++ ++ netif_start_queue(net); ++ dev_kfree_skb_irq(skb); ++ } ++ else ++ { ++ kaweth->stats.tx_packets++; ++ kaweth->stats.tx_bytes += skb->len; ++ } ++ ++ spin_unlock_irq(&kaweth->device_lock); ++ ++ return NETDEV_TX_OK; ++} ++ ++/**************************************************************** ++ * kaweth_set_rx_mode ++ ****************************************************************/ ++static void kaweth_set_rx_mode(struct net_device *net) ++{ ++ struct kaweth_device *kaweth = netdev_priv(net); ++ ++ __u16 packet_filter_bitmap = KAWETH_PACKET_FILTER_DIRECTED | ++ KAWETH_PACKET_FILTER_BROADCAST | ++ KAWETH_PACKET_FILTER_MULTICAST; ++ ++ netdev_dbg(net, "Setting Rx mode to %d\n", packet_filter_bitmap); ++ ++ netif_stop_queue(net); ++ ++ if (net->flags & IFF_PROMISC) { ++ packet_filter_bitmap |= KAWETH_PACKET_FILTER_PROMISCUOUS; ++ } ++ else if (!netdev_mc_empty(net) || (net->flags & IFF_ALLMULTI)) { ++ packet_filter_bitmap |= KAWETH_PACKET_FILTER_ALL_MULTICAST; ++ } ++ ++ kaweth->packet_filter_bitmap = packet_filter_bitmap; ++ netif_wake_queue(net); ++} ++ ++/**************************************************************** ++ * kaweth_async_set_rx_mode ++ ****************************************************************/ ++static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth) ++{ ++ int result; ++ __u16 packet_filter_bitmap = kaweth->packet_filter_bitmap; ++ ++ kaweth->packet_filter_bitmap = 0; ++ if (packet_filter_bitmap == 0) ++ return; ++ ++ if (in_interrupt()) ++ return; ++ ++ result = kaweth_control(kaweth, ++ usb_sndctrlpipe(kaweth->dev, 0), ++ KAWETH_COMMAND_SET_PACKET_FILTER, ++ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, ++ packet_filter_bitmap, ++ 0, ++ (void *)&kaweth->scratch, ++ 0, ++ KAWETH_CONTROL_TIMEOUT); ++ ++ if(result < 0) { ++ dev_err(&kaweth->intf->dev, "Failed to set Rx mode: %d\n", ++ result); ++ } ++ else { ++ netdev_dbg(kaweth->net, "Set Rx mode to %d\n", ++ packet_filter_bitmap); ++ } ++} ++ ++/**************************************************************** ++ * kaweth_netdev_stats ++ ****************************************************************/ ++static struct net_device_stats *kaweth_netdev_stats(struct net_device *dev) ++{ ++ struct kaweth_device *kaweth = netdev_priv(dev); ++ return &kaweth->stats; ++} ++ ++/**************************************************************** ++ * kaweth_tx_timeout ++ ****************************************************************/ ++static void kaweth_tx_timeout(struct net_device *net) ++{ ++ struct kaweth_device *kaweth = netdev_priv(net); ++ ++ dev_warn(&net->dev, "%s: Tx timed out. Resetting.\n", net->name); ++ kaweth->stats.tx_errors++; ++ net->trans_start = jiffies; ++ ++ usb_unlink_urb(kaweth->tx_urb); ++} ++ ++/**************************************************************** ++ * kaweth_suspend ++ ****************************************************************/ ++static int kaweth_suspend(struct usb_interface *intf, pm_message_t message) ++{ ++ struct kaweth_device *kaweth = usb_get_intfdata(intf); ++ unsigned long flags; ++ ++ dev_dbg(&intf->dev, "Suspending device\n"); ++ spin_lock_irqsave(&kaweth->device_lock, flags); ++ kaweth->status |= KAWETH_STATUS_SUSPENDING; ++ spin_unlock_irqrestore(&kaweth->device_lock, flags); ++ ++ kaweth_kill_urbs(kaweth); ++ return 0; ++} ++ ++/**************************************************************** ++ * kaweth_resume ++ ****************************************************************/ ++static int kaweth_resume(struct usb_interface *intf) ++{ ++ struct kaweth_device *kaweth = usb_get_intfdata(intf); ++ unsigned long flags; ++ ++ dev_dbg(&intf->dev, "Resuming device\n"); ++ spin_lock_irqsave(&kaweth->device_lock, flags); ++ kaweth->status &= ~KAWETH_STATUS_SUSPENDING; ++ spin_unlock_irqrestore(&kaweth->device_lock, flags); ++ ++ if (!kaweth->opened) ++ return 0; ++ kaweth_resubmit_rx_urb(kaweth, GFP_NOIO); ++ kaweth_resubmit_int_urb(kaweth, GFP_NOIO); ++ ++ return 0; ++} ++ ++/**************************************************************** ++ * kaweth_probe ++ ****************************************************************/ ++ ++ ++static const struct net_device_ops kaweth_netdev_ops = { ++ .ndo_open = kaweth_open, ++ .ndo_stop = kaweth_close, ++ .ndo_start_xmit = kaweth_start_xmit, ++ .ndo_tx_timeout = kaweth_tx_timeout, ++ .ndo_set_rx_mode = kaweth_set_rx_mode, ++ .ndo_get_stats = kaweth_netdev_stats, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++}; ++ ++static int kaweth_probe( ++ struct usb_interface *intf, ++ const struct usb_device_id *id /* from id_table */ ++ ) ++{ ++ struct device *dev = &intf->dev; ++ struct usb_device *udev = interface_to_usbdev(intf); ++ struct kaweth_device *kaweth; ++ struct net_device *netdev; ++ const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; ++ int result = 0; ++ ++ dev_dbg(dev, ++ "Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x\n", ++ udev->devnum, le16_to_cpu(udev->descriptor.idVendor), ++ le16_to_cpu(udev->descriptor.idProduct), ++ le16_to_cpu(udev->descriptor.bcdDevice)); ++ ++ dev_dbg(dev, "Device at %p\n", udev); ++ ++ dev_dbg(dev, "Descriptor length: %x type: %x\n", ++ (int)udev->descriptor.bLength, ++ (int)udev->descriptor.bDescriptorType); ++ ++ netdev = alloc_etherdev(sizeof(*kaweth)); ++ if (!netdev) ++ return -ENOMEM; ++ ++ kaweth = netdev_priv(netdev); ++ kaweth->dev = udev; ++ kaweth->net = netdev; ++ ++ spin_lock_init(&kaweth->device_lock); ++ init_waitqueue_head(&kaweth->term_wait); ++ ++ dev_dbg(dev, "Resetting.\n"); ++ ++ kaweth_reset(kaweth); ++ ++ /* ++ * If high byte of bcdDevice is nonzero, firmware is already ++ * downloaded. Don't try to do it again, or we'll hang the device. ++ */ ++ ++ if (le16_to_cpu(udev->descriptor.bcdDevice) >> 8) { ++ dev_info(dev, "Firmware present in device.\n"); ++ } else { ++ /* Download the firmware */ ++ dev_info(dev, "Downloading firmware...\n"); ++ kaweth->firmware_buf = (__u8 *)__get_free_page(GFP_KERNEL); ++ if ((result = kaweth_download_firmware(kaweth, ++ "kaweth/new_code.bin", ++ 100, ++ 2)) < 0) { ++ dev_err(dev, "Error downloading firmware (%d)\n", ++ result); ++ goto err_fw; ++ } ++ ++ if ((result = kaweth_download_firmware(kaweth, ++ "kaweth/new_code_fix.bin", ++ 100, ++ 3)) < 0) { ++ dev_err(dev, "Error downloading firmware fix (%d)\n", ++ result); ++ goto err_fw; ++ } ++ ++ if ((result = kaweth_download_firmware(kaweth, ++ "kaweth/trigger_code.bin", ++ 126, ++ 2)) < 0) { ++ dev_err(dev, "Error downloading trigger code (%d)\n", ++ result); ++ goto err_fw; ++ ++ } ++ ++ if ((result = kaweth_download_firmware(kaweth, ++ "kaweth/trigger_code_fix.bin", ++ 126, ++ 3)) < 0) { ++ dev_err(dev, "Error downloading trigger code fix (%d)\n", result); ++ goto err_fw; ++ } ++ ++ ++ if ((result = kaweth_trigger_firmware(kaweth, 126)) < 0) { ++ dev_err(dev, "Error triggering firmware (%d)\n", result); ++ goto err_fw; ++ } ++ ++ /* Device will now disappear for a moment... */ ++ dev_info(dev, "Firmware loaded. I'll be back...\n"); ++err_fw: ++ free_page((unsigned long)kaweth->firmware_buf); ++ free_netdev(netdev); ++ return -EIO; ++ } ++ ++ result = kaweth_read_configuration(kaweth); ++ ++ if(result < 0) { ++ dev_err(dev, "Error reading configuration (%d), no net device created\n", result); ++ goto err_free_netdev; ++ } ++ ++ dev_info(dev, "Statistics collection: %x\n", kaweth->configuration.statistics_mask); ++ dev_info(dev, "Multicast filter limit: %x\n", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1)); ++ dev_info(dev, "MTU: %d\n", le16_to_cpu(kaweth->configuration.segment_size)); ++ dev_info(dev, "Read MAC address %pM\n", kaweth->configuration.hw_addr); ++ ++ if(!memcmp(&kaweth->configuration.hw_addr, ++ &bcast_addr, ++ sizeof(bcast_addr))) { ++ dev_err(dev, "Firmware not functioning properly, no net device created\n"); ++ goto err_free_netdev; ++ } ++ ++ if(kaweth_set_urb_size(kaweth, KAWETH_BUF_SIZE) < 0) { ++ dev_dbg(dev, "Error setting URB size\n"); ++ goto err_free_netdev; ++ } ++ ++ if(kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT) < 0) { ++ dev_err(dev, "Error setting SOFS wait\n"); ++ goto err_free_netdev; ++ } ++ ++ result = kaweth_set_receive_filter(kaweth, ++ KAWETH_PACKET_FILTER_DIRECTED | ++ KAWETH_PACKET_FILTER_BROADCAST | ++ KAWETH_PACKET_FILTER_MULTICAST); ++ ++ if(result < 0) { ++ dev_err(dev, "Error setting receive filter\n"); ++ goto err_free_netdev; ++ } ++ ++ dev_dbg(dev, "Initializing net device.\n"); ++ ++ kaweth->intf = intf; ++ ++ kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!kaweth->tx_urb) ++ goto err_free_netdev; ++ kaweth->rx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!kaweth->rx_urb) ++ goto err_only_tx; ++ kaweth->irq_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!kaweth->irq_urb) ++ goto err_tx_and_rx; ++ ++ kaweth->intbuffer = usb_alloc_coherent( kaweth->dev, ++ INTBUFFERSIZE, ++ GFP_KERNEL, ++ &kaweth->intbufferhandle); ++ if (!kaweth->intbuffer) ++ goto err_tx_and_rx_and_irq; ++ kaweth->rx_buf = usb_alloc_coherent( kaweth->dev, ++ KAWETH_BUF_SIZE, ++ GFP_KERNEL, ++ &kaweth->rxbufferhandle); ++ if (!kaweth->rx_buf) ++ goto err_all_but_rxbuf; ++ ++ memcpy(netdev->broadcast, &bcast_addr, sizeof(bcast_addr)); ++ memcpy(netdev->dev_addr, &kaweth->configuration.hw_addr, ++ sizeof(kaweth->configuration.hw_addr)); ++ ++ netdev->netdev_ops = &kaweth_netdev_ops; ++ netdev->watchdog_timeo = KAWETH_TX_TIMEOUT; ++ netdev->mtu = le16_to_cpu(kaweth->configuration.segment_size); ++ netdev->ethtool_ops = &ops; ++ ++ /* kaweth is zeroed as part of alloc_netdev */ ++ INIT_DELAYED_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl); ++ usb_set_intfdata(intf, kaweth); ++ ++#if 0 ++// dma_supported() is deeply broken on almost all architectures ++ if (dma_supported (dev, 0xffffffffffffffffULL)) ++ kaweth->net->features |= NETIF_F_HIGHDMA; ++#endif ++ ++ SET_NETDEV_DEV(netdev, dev); ++ if (register_netdev(netdev) != 0) { ++ dev_err(dev, "Error registering netdev.\n"); ++ goto err_intfdata; ++ } ++ ++ dev_info(dev, "kaweth interface created at %s\n", ++ kaweth->net->name); ++ ++ dev_dbg(dev, "Kaweth probe returning.\n"); ++ ++ return 0; ++ ++err_intfdata: ++ usb_set_intfdata(intf, NULL); ++ usb_free_coherent(kaweth->dev, KAWETH_BUF_SIZE, (void *)kaweth->rx_buf, kaweth->rxbufferhandle); ++err_all_but_rxbuf: ++ usb_free_coherent(kaweth->dev, INTBUFFERSIZE, (void *)kaweth->intbuffer, kaweth->intbufferhandle); ++err_tx_and_rx_and_irq: ++ usb_free_urb(kaweth->irq_urb); ++err_tx_and_rx: ++ usb_free_urb(kaweth->rx_urb); ++err_only_tx: ++ usb_free_urb(kaweth->tx_urb); ++err_free_netdev: ++ free_netdev(netdev); ++ ++ return -EIO; ++} ++ ++/**************************************************************** ++ * kaweth_disconnect ++ ****************************************************************/ ++static void kaweth_disconnect(struct usb_interface *intf) ++{ ++ struct kaweth_device *kaweth = usb_get_intfdata(intf); ++ struct net_device *netdev; ++ ++ dev_info(&intf->dev, "Unregistering\n"); ++ ++ usb_set_intfdata(intf, NULL); ++ if (!kaweth) { ++ dev_warn(&intf->dev, "unregistering non-existent device\n"); ++ return; ++ } ++ netdev = kaweth->net; ++ ++ netdev_dbg(kaweth->net, "Unregistering net device\n"); ++ unregister_netdev(netdev); ++ ++ usb_free_urb(kaweth->rx_urb); ++ usb_free_urb(kaweth->tx_urb); ++ usb_free_urb(kaweth->irq_urb); ++ ++ usb_free_coherent(kaweth->dev, KAWETH_BUF_SIZE, (void *)kaweth->rx_buf, kaweth->rxbufferhandle); ++ usb_free_coherent(kaweth->dev, INTBUFFERSIZE, (void *)kaweth->intbuffer, kaweth->intbufferhandle); ++ ++ free_netdev(netdev); ++} ++ ++ ++// FIXME this completion stuff is a modified clone of ++// an OLD version of some stuff in usb.c ... ++struct usb_api_data { ++ wait_queue_head_t wqh; ++ int done; ++}; ++ ++/*-------------------------------------------------------------------* ++ * completion handler for compatibility wrappers (sync control/bulk) * ++ *-------------------------------------------------------------------*/ ++static void usb_api_blocking_completion(struct urb *urb) ++{ ++ struct usb_api_data *awd = (struct usb_api_data *)urb->context; ++ ++ awd->done=1; ++ wake_up(&awd->wqh); ++} ++ ++/*-------------------------------------------------------------------* ++ * COMPATIBILITY STUFF * ++ *-------------------------------------------------------------------*/ ++ ++// Starts urb and waits for completion or timeout ++static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) ++{ ++ struct usb_api_data awd; ++ int status; ++ ++ init_waitqueue_head(&awd.wqh); ++ awd.done = 0; ++ ++ urb->context = &awd; ++ status = usb_submit_urb(urb, GFP_NOIO); ++ if (status) { ++ // something went wrong ++ usb_free_urb(urb); ++ return status; ++ } ++ ++ if (!wait_event_timeout(awd.wqh, awd.done, timeout)) { ++ // timeout ++ dev_warn(&urb->dev->dev, "usb_control/bulk_msg: timeout\n"); ++ usb_kill_urb(urb); // remove urb safely ++ status = -ETIMEDOUT; ++ } ++ else { ++ status = urb->status; ++ } ++ ++ if (actual_length) { ++ *actual_length = urb->actual_length; ++ } ++ ++ usb_free_urb(urb); ++ return status; ++} ++ ++/*-------------------------------------------------------------------*/ ++// returns status (negative) or length (positive) ++static int kaweth_internal_control_msg(struct usb_device *usb_dev, ++ unsigned int pipe, ++ struct usb_ctrlrequest *cmd, void *data, ++ int len, int timeout) ++{ ++ struct urb *urb; ++ int retv; ++ int length = 0; /* shut up GCC */ ++ ++ urb = usb_alloc_urb(0, GFP_ATOMIC); ++ if (!urb) ++ return -ENOMEM; ++ ++ usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char*)cmd, data, ++ len, usb_api_blocking_completion, NULL); ++ ++ retv = usb_start_wait_urb(urb, timeout, &length); ++ if (retv < 0) { ++ return retv; ++ } ++ else { ++ return length; ++ } ++} ++ ++module_usb_driver(kaweth_driver); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/Kconfig backports-3.18.1-1/drivers/net/usb/Kconfig +--- backports-3.18.1-1.org/drivers/net/usb/Kconfig 2014-12-21 22:37:15.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/Kconfig 2015-01-03 15:19:02.310281530 +0100 +@@ -13,7 +13,6 @@ + if USB_NET_DRIVERS + + config USB_CATC +- depends on n + tristate "USB CATC NetMate-based Ethernet device support" + depends on m + depends on CRC32 +@@ -34,7 +33,6 @@ + module will be called catc. + + config USB_KAWETH +- depends on n + tristate "USB KLSI KL5USB101-based ethernet device support" + depends on m + ---help--- +@@ -75,7 +73,6 @@ + module will be called kaweth. + + config USB_PEGASUS +- depends on n + tristate "USB Pegasus/Pegasus-II based ethernet device support" + depends on m + select BACKPORT_MII +@@ -92,7 +89,6 @@ + module will be called pegasus. + + config USB_RTL8150 +- depends on n + tristate "USB RTL8150 based ethernet device support" + depends on m + select BACKPORT_MII +@@ -105,7 +101,6 @@ + module will be called rtl8150. + + config USB_RTL8152 +- depends on n + tristate "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters" + depends on m + select BACKPORT_MII +@@ -153,7 +148,6 @@ + module will be called usbnet. + + config USB_NET_AX8817X +- depends on n + tristate "ASIX AX88xxx Based USB 2.0 Ethernet Adapters" + depends on m + depends on USB_USBNET +@@ -183,7 +177,6 @@ + what other networking devices you have in use. + + config USB_NET_AX88179_178A +- depends on n + tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet" + depends on m + depends on USB_USBNET +@@ -232,7 +225,6 @@ + name is used instead. + + config USB_NET_CDC_EEM +- depends on n + tristate "CDC EEM support" + depends on m + depends on USB_USBNET +@@ -268,7 +260,6 @@ + * Ericsson F5521gw Mobile Broadband Module + + config USB_NET_HUAWEI_CDC_NCM +- depends on n + tristate "Huawei NCM embedded AT channel support" + depends on m + depends on USB_USBNET +@@ -304,7 +295,6 @@ + module will be called cdc_mbim. + + config USB_NET_DM9601 +- depends on n + tristate "Davicom DM96xx based USB 10/100 ethernet devices" + depends on m + depends on USB_USBNET +@@ -314,7 +304,6 @@ + based USB 10/100 Ethernet adapters. + + config USB_NET_SR9700 +- depends on n + tristate "CoreChip-sz SR9700 based USB 1.1 10/100 ethernet devices" + depends on m + depends on USB_USBNET +@@ -324,7 +313,6 @@ + 10/100 Ethernet adapters. + + config USB_NET_SR9800 +- depends on n + tristate "CoreChip-sz SR9800 based USB 2.0 10/100 ethernet devices" + depends on m + depends on USB_USBNET +@@ -341,7 +329,6 @@ + module will be called sr9800. + + config USB_NET_SMSC75XX +- depends on n + tristate "SMSC LAN75XX based USB 2.0 gigabit ethernet devices" + depends on m + depends on USB_USBNET +@@ -353,7 +340,6 @@ + Gigabit Ethernet adapters. + + config USB_NET_SMSC95XX +- depends on n + tristate "SMSC LAN95XX based USB 2.0 10/100 ethernet devices" + depends on m + depends on USB_USBNET +@@ -365,7 +351,6 @@ + 10/100 Ethernet adapters. + + config USB_NET_GL620A +- depends on n + tristate "GeneSys GL620USB-A based cables" + depends on m + depends on USB_USBNET +@@ -376,7 +361,6 @@ + Note that the half-duplex "GL620USB" is not supported. + + config USB_NET_NET1080 +- depends on n + tristate "NetChip 1080 based cables (Laplink, ...)" + depends on m + default y +@@ -387,7 +371,6 @@ + optionally with LEDs that indicate traffic + + config USB_NET_PLUSB +- depends on n + tristate "Prolific PL-2301/2302/25A1 based cables" + depends on m + # if the handshake/init/reset problems, from original 'plusb', +@@ -398,7 +381,6 @@ + with one of these chips. + + config USB_NET_MCS7830 +- depends on n + tristate "MosChip MCS7830 based Ethernet adapters" + depends on m + depends on USB_USBNET +@@ -424,7 +406,6 @@ + (and for) Microsoft; it isn't an "Open" ecosystem or market. + + config USB_NET_CDC_SUBSET +- depends on n + tristate "Simple USB Network Links (CDC Ethernet subset)" + depends on m + depends on USB_USBNET +@@ -496,7 +477,6 @@ + with one of these chips. + + config USB_NET_ZAURUS +- depends on n + tristate "Sharp Zaurus (stock ROMs) and compatible" + depends on m + depends on USB_USBNET +@@ -516,7 +496,6 @@ + some cases CDC MDLM) protocol, not "g_ether". + + config USB_NET_CX82310_ETH +- depends on n + tristate "Conexant CX82310 USB ethernet port" + depends on m + depends on USB_USBNET +@@ -526,7 +505,6 @@ + it will not work with ADSL modems (use cxacru driver instead). + + config USB_NET_KALMIA +- depends on n + tristate "Samsung Kalmia based LTE USB modem" + depends on m + depends on USB_USBNET +@@ -561,7 +539,6 @@ + module will be called qmi_wwan. + + config USB_HSO +- depends on n + tristate "Option USB High Speed Mobile Devices" + depends on m + depends on USB && RFKILL && TTY +@@ -574,7 +551,6 @@ + module will be called hso. + + config USB_NET_INT51X1 +- depends on n + tristate "Intellon PLC based usb adapter" + depends on m + depends on USB_USBNET +@@ -584,7 +560,6 @@ + INT51x1/INT5200 chip, like the "devolo dLan duo". + + config USB_CDC_PHONET +- depends on n + tristate "CDC Phonet support" + depends on m + depends on PHONET +@@ -594,7 +569,6 @@ + "PC suite" USB profile. + + config USB_IPHETH +- depends on n + tristate "Apple iPhone USB Ethernet driver" + depends on m + default n +@@ -618,11 +592,10 @@ + module will be called sierra_net. + + config USB_VL600 +- depends on n + tristate "LG VL600 modem dongle" + depends on m + depends on USB_NET_CDCETHER && TTY +- select USB_ACM ++# depends on USB_ACM + help + Select this if you want to use an LG Electronics 4G/LTE usb modem + called VL600. This driver only handles the ethernet +diff -Naur backports-3.18.1-1.org/drivers/net/usb/lg-vl600.c backports-3.18.1-1/drivers/net/usb/lg-vl600.c +--- backports-3.18.1-1.org/drivers/net/usb/lg-vl600.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/lg-vl600.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,353 @@ ++/* ++ * Ethernet interface part of the LG VL600 LTE modem (4G dongle) ++ * ++ * Copyright (C) 2011 Intel Corporation ++ * Author: Andrzej Zaborowski ++ * ++ * 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, see . ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * The device has a CDC ACM port for modem control (it claims to be ++ * CDC ACM anyway) and a CDC Ethernet port for actual network data. ++ * It will however ignore data on both ports that is not encapsulated ++ * in a specific way, any data returned is also encapsulated the same ++ * way. The headers don't seem to follow any popular standard. ++ * ++ * This driver adds and strips these headers from the ethernet frames ++ * sent/received from the CDC Ethernet port. The proprietary header ++ * replaces the standard ethernet header in a packet so only actual ++ * ethernet frames are allowed. The headers allow some form of ++ * multiplexing by using non standard values of the .h_proto field. ++ * Windows/Mac drivers do send a couple of such frames to the device ++ * during initialisation, with protocol set to 0x0906 or 0x0b06 and (what ++ * seems to be) a flag in the .dummy_flags. This doesn't seem necessary ++ * for modem operation but can possibly be used for GPS or other funcitons. ++ */ ++ ++struct vl600_frame_hdr { ++ __le32 len; ++ __le32 serial; ++ __le32 pkt_cnt; ++ __le32 dummy_flags; ++ __le32 dummy; ++ __le32 magic; ++} __attribute__((packed)); ++ ++struct vl600_pkt_hdr { ++ __le32 dummy[2]; ++ __le32 len; ++ __be16 h_proto; ++} __attribute__((packed)); ++ ++struct vl600_state { ++ struct sk_buff *current_rx_buf; ++}; ++ ++static int vl600_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ int ret; ++ struct vl600_state *s = kzalloc(sizeof(struct vl600_state), GFP_KERNEL); ++ ++ if (!s) ++ return -ENOMEM; ++ ++ ret = usbnet_cdc_bind(dev, intf); ++ if (ret) { ++ kfree(s); ++ return ret; ++ } ++ ++ dev->driver_priv = s; ++ ++ /* ARP packets don't go through, but they're also of no use. The ++ * subnet has only two hosts anyway: us and the gateway / DHCP ++ * server (probably simulated by modem firmware or network operator) ++ * whose address changes everytime we connect to the intarwebz and ++ * who doesn't bother answering ARP requests either. So hardware ++ * addresses have no meaning, the destination and the source of every ++ * packet depend only on whether it is on the IN or OUT endpoint. */ ++ dev->net->flags |= IFF_NOARP; ++ /* IPv6 NDP relies on multicast. Enable it by default. */ ++ dev->net->flags |= IFF_MULTICAST; ++ ++ return ret; ++} ++ ++static void vl600_unbind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ struct vl600_state *s = dev->driver_priv; ++ ++ if (s->current_rx_buf) ++ dev_kfree_skb(s->current_rx_buf); ++ ++ kfree(s); ++ ++ return usbnet_cdc_unbind(dev, intf); ++} ++ ++static int vl600_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ struct vl600_frame_hdr *frame; ++ struct vl600_pkt_hdr *packet; ++ struct ethhdr *ethhdr; ++ int packet_len, count; ++ struct sk_buff *buf = skb; ++ struct sk_buff *clone; ++ struct vl600_state *s = dev->driver_priv; ++ ++ /* Frame lengths are generally 4B multiplies but every couple of ++ * hours there's an odd number of bytes sized yet correct frame, ++ * so don't require this. */ ++ ++ /* Allow a packet (or multiple packets batched together) to be ++ * split across many frames. We don't allow a new batch to ++ * begin in the same frame another one is ending however, and no ++ * leading or trailing pad bytes. */ ++ if (s->current_rx_buf) { ++ frame = (struct vl600_frame_hdr *) s->current_rx_buf->data; ++ if (skb->len + s->current_rx_buf->len > ++ le32_to_cpup(&frame->len)) { ++ netif_err(dev, ifup, dev->net, "Fragment too long\n"); ++ dev->net->stats.rx_length_errors++; ++ goto error; ++ } ++ ++ buf = s->current_rx_buf; ++ memcpy(skb_put(buf, skb->len), skb->data, skb->len); ++ } else if (skb->len < 4) { ++ netif_err(dev, ifup, dev->net, "Frame too short\n"); ++ dev->net->stats.rx_length_errors++; ++ goto error; ++ } ++ ++ frame = (struct vl600_frame_hdr *) buf->data; ++ /* Yes, check that frame->magic == 0x53544448 (or 0x44544d48), ++ * otherwise we may run out of memory w/a bad packet */ ++ if (ntohl(frame->magic) != 0x53544448 && ++ ntohl(frame->magic) != 0x44544d48) ++ goto error; ++ ++ if (buf->len < sizeof(*frame) || ++ buf->len != le32_to_cpup(&frame->len)) { ++ /* Save this fragment for later assembly */ ++ if (s->current_rx_buf) ++ return 0; ++ ++ s->current_rx_buf = skb_copy_expand(skb, 0, ++ le32_to_cpup(&frame->len), GFP_ATOMIC); ++ if (!s->current_rx_buf) { ++ netif_err(dev, ifup, dev->net, "Reserving %i bytes " ++ "for packet assembly failed.\n", ++ le32_to_cpup(&frame->len)); ++ dev->net->stats.rx_errors++; ++ } ++ ++ return 0; ++ } ++ ++ count = le32_to_cpup(&frame->pkt_cnt); ++ ++ skb_pull(buf, sizeof(*frame)); ++ ++ while (count--) { ++ if (buf->len < sizeof(*packet)) { ++ netif_err(dev, ifup, dev->net, "Packet too short\n"); ++ goto error; ++ } ++ ++ packet = (struct vl600_pkt_hdr *) buf->data; ++ packet_len = sizeof(*packet) + le32_to_cpup(&packet->len); ++ if (packet_len > buf->len) { ++ netif_err(dev, ifup, dev->net, ++ "Bad packet length stored in header\n"); ++ goto error; ++ } ++ ++ /* Packet header is same size as the ethernet header ++ * (sizeof(*packet) == sizeof(*ethhdr)), additionally ++ * the h_proto field is in the same place so we just leave it ++ * alone and fill in the remaining fields. ++ */ ++ ethhdr = (struct ethhdr *) skb->data; ++ if (be16_to_cpup(ðhdr->h_proto) == ETH_P_ARP && ++ buf->len > 0x26) { ++ /* Copy the addresses from packet contents */ ++ memcpy(ethhdr->h_source, ++ &buf->data[sizeof(*ethhdr) + 0x8], ++ ETH_ALEN); ++ memcpy(ethhdr->h_dest, ++ &buf->data[sizeof(*ethhdr) + 0x12], ++ ETH_ALEN); ++ } else { ++ memset(ethhdr->h_source, 0, ETH_ALEN); ++ memcpy(ethhdr->h_dest, dev->net->dev_addr, ETH_ALEN); ++ ++ /* Inbound IPv6 packets have an IPv4 ethertype (0x800) ++ * for some reason. Peek at the L3 header to check ++ * for IPv6 packets, and set the ethertype to IPv6 ++ * (0x86dd) so Linux can understand it. ++ */ ++ if ((buf->data[sizeof(*ethhdr)] & 0xf0) == 0x60) ++ ethhdr->h_proto = htons(ETH_P_IPV6); ++ } ++ ++ if (count) { ++ /* Not the last packet in this batch */ ++ clone = skb_clone(buf, GFP_ATOMIC); ++ if (!clone) ++ goto error; ++ ++ skb_trim(clone, packet_len); ++ usbnet_skb_return(dev, clone); ++ ++ skb_pull(buf, (packet_len + 3) & ~3); ++ } else { ++ skb_trim(buf, packet_len); ++ ++ if (s->current_rx_buf) { ++ usbnet_skb_return(dev, buf); ++ s->current_rx_buf = NULL; ++ return 0; ++ } ++ ++ return 1; ++ } ++ } ++ ++error: ++ if (s->current_rx_buf) { ++ dev_kfree_skb_any(s->current_rx_buf); ++ s->current_rx_buf = NULL; ++ } ++ dev->net->stats.rx_errors++; ++ return 0; ++} ++ ++static struct sk_buff *vl600_tx_fixup(struct usbnet *dev, ++ struct sk_buff *skb, gfp_t flags) ++{ ++ struct sk_buff *ret; ++ struct vl600_frame_hdr *frame; ++ struct vl600_pkt_hdr *packet; ++ static uint32_t serial = 1; ++ int orig_len = skb->len - sizeof(struct ethhdr); ++ int full_len = (skb->len + sizeof(struct vl600_frame_hdr) + 3) & ~3; ++ ++ frame = (struct vl600_frame_hdr *) skb->data; ++ if (skb->len > sizeof(*frame) && skb->len == le32_to_cpup(&frame->len)) ++ return skb; /* Already encapsulated? */ ++ ++ if (skb->len < sizeof(struct ethhdr)) ++ /* Drop, device can only deal with ethernet packets */ ++ return NULL; ++ ++ if (!skb_cloned(skb)) { ++ int headroom = skb_headroom(skb); ++ int tailroom = skb_tailroom(skb); ++ ++ if (tailroom >= full_len - skb->len - sizeof(*frame) && ++ headroom >= sizeof(*frame)) ++ /* There's enough head and tail room */ ++ goto encapsulate; ++ ++ if (headroom + tailroom + skb->len >= full_len) { ++ /* There's enough total room, just readjust */ ++ skb->data = memmove(skb->head + sizeof(*frame), ++ skb->data, skb->len); ++ skb_set_tail_pointer(skb, skb->len); ++ goto encapsulate; ++ } ++ } ++ ++ /* Alloc a new skb with the required size */ ++ ret = skb_copy_expand(skb, sizeof(struct vl600_frame_hdr), full_len - ++ skb->len - sizeof(struct vl600_frame_hdr), flags); ++ dev_kfree_skb_any(skb); ++ if (!ret) ++ return ret; ++ skb = ret; ++ ++encapsulate: ++ /* Packet header is same size as ethernet packet header ++ * (sizeof(*packet) == sizeof(struct ethhdr)), additionally the ++ * h_proto field is in the same place so we just leave it alone and ++ * overwrite the remaining fields. ++ */ ++ packet = (struct vl600_pkt_hdr *) skb->data; ++ /* The VL600 wants IPv6 packets to have an IPv4 ethertype ++ * Since this modem only supports IPv4 and IPv6, just set all ++ * frames to 0x0800 (ETH_P_IP) ++ */ ++ packet->h_proto = htons(ETH_P_IP); ++ memset(&packet->dummy, 0, sizeof(packet->dummy)); ++ packet->len = cpu_to_le32(orig_len); ++ ++ frame = (struct vl600_frame_hdr *) skb_push(skb, sizeof(*frame)); ++ memset(frame, 0, sizeof(*frame)); ++ frame->len = cpu_to_le32(full_len); ++ frame->serial = cpu_to_le32(serial++); ++ frame->pkt_cnt = cpu_to_le32(1); ++ ++ if (skb->len < full_len) /* Pad */ ++ skb_put(skb, full_len - skb->len); ++ ++ return skb; ++} ++ ++static const struct driver_info vl600_info = { ++ .description = "LG VL600 modem", ++ .flags = FLAG_RX_ASSEMBLE | FLAG_WWAN, ++ .bind = vl600_bind, ++ .unbind = vl600_unbind, ++ .status = usbnet_cdc_status, ++ .rx_fixup = vl600_rx_fixup, ++ .tx_fixup = vl600_tx_fixup, ++}; ++ ++static const struct usb_device_id products[] = { ++ { ++ USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM, ++ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), ++ .driver_info = (unsigned long) &vl600_info, ++ }, ++ {}, /* End */ ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver lg_vl600_driver = { ++ .name = "lg-vl600", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(lg_vl600_driver); ++ ++MODULE_AUTHOR("Anrzej Zaborowski"); ++MODULE_DESCRIPTION("LG-VL600 modem's ethernet link"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/Makefile backports-3.18.1-1/drivers/net/usb/Makefile +--- backports-3.18.1-1.org/drivers/net/usb/Makefile 2014-12-21 22:37:15.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/Makefile 2015-01-03 13:49:51.269970813 +0100 +@@ -1,39 +1,35 @@ +-# +-# Makefile for USB Network drivers +-# +-# +-#obj-$(CPTCFG_USB_CATC) += catc.o +-#obj-$(CPTCFG_USB_KAWETH) += kaweth.o +-#obj-$(CPTCFG_USB_PEGASUS) += pegasus.o +-#obj-$(CPTCFG_USB_RTL8150) += rtl8150.o +-#obj-$(CPTCFG_USB_RTL8152) += r8152.o +-#obj-$(CPTCFG_USB_HSO) += hso.o +-#obj-$(CPTCFG_USB_NET_AX8817X) += asix.o +-#obj-$(CPTCFG_USB_NET_AX88179_178A) += ax88179_178a.o ++obj-$(CPTCFG_USB_CATC) += catc.o ++obj-$(CPTCFG_USB_KAWETH) += kaweth.o ++obj-$(CPTCFG_USB_PEGASUS) += pegasus.o ++obj-$(CPTCFG_USB_RTL8150) += rtl8150.o ++obj-$(CPTCFG_USB_RTL8152) += r8152.o ++obj-$(CPTCFG_USB_HSO) += hso.o ++obj-$(CPTCFG_USB_NET_AX8817X) += asix.o ++asix-y := asix_devices.o asix_common.o ax88172a.o ++obj-$(CPTCFG_USB_NET_AX88179_178A) += ax88179_178a.o + obj-$(CPTCFG_USB_NET_CDCETHER) += cdc_ether.o +-#obj-$(CPTCFG_USB_NET_CDC_EEM) += cdc_eem.o +-#obj-$(CPTCFG_USB_NET_DM9601) += dm9601.o +-#obj-$(CPTCFG_USB_NET_SR9700) += sr9700.o +-#obj-$(CPTCFG_USB_NET_SR9800) += sr9800.o +-#obj-$(CPTCFG_USB_NET_SMSC75XX) += smsc75xx.o +-#obj-$(CPTCFG_USB_NET_SMSC95XX) += smsc95xx.o +-#obj-$(CPTCFG_USB_NET_GL620A) += gl620a.o +-#obj-$(CPTCFG_USB_NET_NET1080) += net1080.o +-#obj-$(CPTCFG_USB_NET_PLUSB) += plusb.o ++obj-$(CPTCFG_USB_NET_CDC_EEM) += cdc_eem.o ++obj-$(CPTCFG_USB_NET_DM9601) += dm9601.o ++obj-$(CPTCFG_USB_NET_SR9700) += sr9700.o ++obj-$(CPTCFG_USB_NET_SR9800) += sr9800.o ++obj-$(CPTCFG_USB_NET_SMSC75XX) += smsc75xx.o ++obj-$(CPTCFG_USB_NET_SMSC95XX) += smsc95xx.o ++obj-$(CPTCFG_USB_NET_GL620A) += gl620a.o ++obj-$(CPTCFG_USB_NET_NET1080) += net1080.o ++obj-$(CPTCFG_USB_NET_PLUSB) += plusb.o + obj-$(CPTCFG_USB_NET_RNDIS_HOST) += rndis_host.o +-#obj-$(CPTCFG_USB_NET_CDC_SUBSET) += cdc_subset.o +-#obj-$(CPTCFG_USB_NET_ZAURUS) += zaurus.o +-#obj-$(CPTCFG_USB_NET_MCS7830) += mcs7830.o ++obj-$(CPTCFG_USB_NET_CDC_SUBSET) += cdc_subset.o ++obj-$(CPTCFG_USB_NET_ZAURUS) += zaurus.o ++obj-$(CPTCFG_USB_NET_MCS7830) += mcs7830.o + obj-$(CPTCFG_USB_USBNET) += usbnet.o +-#obj-$(CPTCFG_USB_NET_INT51X1) += int51x1.o +-#obj-$(CPTCFG_USB_CDC_PHONET) += cdc-phonet.o +-#obj-$(CPTCFG_USB_NET_KALMIA) += kalmia.o +-#obj-$(CPTCFG_USB_IPHETH) += ipheth.o ++obj-$(CPTCFG_USB_NET_INT51X1) += int51x1.o ++obj-$(CPTCFG_USB_CDC_PHONET) += cdc-phonet.o ++obj-$(CPTCFG_USB_NET_KALMIA) += kalmia.o ++obj-$(CPTCFG_USB_IPHETH) += ipheth.o + obj-$(CPTCFG_USB_SIERRA_NET) += sierra_net.o +-#obj-$(CPTCFG_USB_NET_CX82310_ETH) += cx82310_eth.o ++obj-$(CPTCFG_USB_NET_CX82310_ETH) += cx82310_eth.o + obj-$(CPTCFG_USB_NET_CDC_NCM) += cdc_ncm.o +-#obj-$(CPTCFG_USB_NET_HUAWEI_CDC_NCM) += huawei_cdc_ncm.o +-#obj-$(CPTCFG_USB_VL600) += lg-vl600.o ++obj-$(CPTCFG_USB_NET_HUAWEI_CDC_NCM) += huawei_cdc_ncm.o ++obj-$(CPTCFG_USB_VL600) += lg-vl600.o + obj-$(CPTCFG_USB_NET_QMI_WWAN) += qmi_wwan.o + obj-$(CPTCFG_USB_NET_CDC_MBIM) += cdc_mbim.o +- +diff -Naur backports-3.18.1-1.org/drivers/net/usb/mcs7830.c backports-3.18.1-1/drivers/net/usb/mcs7830.c +--- backports-3.18.1-1.org/drivers/net/usb/mcs7830.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/mcs7830.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,643 @@ ++/* ++ * MOSCHIP MCS7830 based (7730/7830/7832) USB 2.0 Ethernet Devices ++ * ++ * based on usbnet.c, asix.c and the vendor provided mcs7830 driver ++ * ++ * Copyright (C) 2010 Andreas Mohr ++ * Copyright (C) 2006 Arnd Bergmann ++ * Copyright (C) 2003-2005 David Hollis ++ * Copyright (C) 2005 Phil Chang ++ * Copyright (c) 2002-2003 TiVo Inc. ++ * ++ * Definitions gathered from MOSCHIP, Data Sheet_7830DA.pdf (thanks!). ++ * ++ * 2010-12-19: add 7832 USB PID ("functionality same as MCS7830"), ++ * per active notification by manufacturer ++ * ++ * TODO: ++ * - support HIF_REG_CONFIG_SLEEPMODE/HIF_REG_CONFIG_TXENABLE (via autopm?) ++ * - implement ethtool_ops get_pauseparam/set_pauseparam ++ * via HIF_REG_PAUSE_THRESHOLD (>= revision C only!) ++ * - implement get_eeprom/[set_eeprom] ++ * - switch PHY on/off on ifup/ifdown (perhaps in usbnet.c, via MII) ++ * - mcs7830_get_regs() handling is weird: for rev 2 we return 32 regs, ++ * can access only ~ 24, remaining user buffer is uninitialized garbage ++ * - anything else? ++ * ++ * ++ * 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, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* requests */ ++#define MCS7830_RD_BMREQ (USB_DIR_IN | USB_TYPE_VENDOR | \ ++ USB_RECIP_DEVICE) ++#define MCS7830_WR_BMREQ (USB_DIR_OUT | USB_TYPE_VENDOR | \ ++ USB_RECIP_DEVICE) ++#define MCS7830_RD_BREQ 0x0E ++#define MCS7830_WR_BREQ 0x0D ++ ++#define MCS7830_CTRL_TIMEOUT 1000 ++#define MCS7830_MAX_MCAST 64 ++ ++#define MCS7830_VENDOR_ID 0x9710 ++#define MCS7832_PRODUCT_ID 0x7832 ++#define MCS7830_PRODUCT_ID 0x7830 ++#define MCS7730_PRODUCT_ID 0x7730 ++ ++#define SITECOM_VENDOR_ID 0x0DF6 ++#define LN_030_PRODUCT_ID 0x0021 ++ ++#define MCS7830_MII_ADVERTISE (ADVERTISE_PAUSE_CAP | ADVERTISE_100FULL | \ ++ ADVERTISE_100HALF | ADVERTISE_10FULL | \ ++ ADVERTISE_10HALF | ADVERTISE_CSMA) ++ ++/* HIF_REG_XX corresponding index value */ ++enum { ++ HIF_REG_MULTICAST_HASH = 0x00, ++ HIF_REG_PACKET_GAP1 = 0x08, ++ HIF_REG_PACKET_GAP2 = 0x09, ++ HIF_REG_PHY_DATA = 0x0a, ++ HIF_REG_PHY_CMD1 = 0x0c, ++ HIF_REG_PHY_CMD1_READ = 0x40, ++ HIF_REG_PHY_CMD1_WRITE = 0x20, ++ HIF_REG_PHY_CMD1_PHYADDR = 0x01, ++ HIF_REG_PHY_CMD2 = 0x0d, ++ HIF_REG_PHY_CMD2_PEND_FLAG_BIT = 0x80, ++ HIF_REG_PHY_CMD2_READY_FLAG_BIT = 0x40, ++ HIF_REG_CONFIG = 0x0e, ++ /* hmm, spec sez: "R/W", "Except bit 3" (likely TXENABLE). */ ++ HIF_REG_CONFIG_CFG = 0x80, ++ HIF_REG_CONFIG_SPEED100 = 0x40, ++ HIF_REG_CONFIG_FULLDUPLEX_ENABLE = 0x20, ++ HIF_REG_CONFIG_RXENABLE = 0x10, ++ HIF_REG_CONFIG_TXENABLE = 0x08, ++ HIF_REG_CONFIG_SLEEPMODE = 0x04, ++ HIF_REG_CONFIG_ALLMULTICAST = 0x02, ++ HIF_REG_CONFIG_PROMISCUOUS = 0x01, ++ HIF_REG_ETHERNET_ADDR = 0x0f, ++ HIF_REG_FRAME_DROP_COUNTER = 0x15, /* 0..ff; reset: 0 */ ++ HIF_REG_PAUSE_THRESHOLD = 0x16, ++ HIF_REG_PAUSE_THRESHOLD_DEFAULT = 0, ++}; ++ ++/* Trailing status byte in Ethernet Rx frame */ ++enum { ++ MCS7830_RX_SHORT_FRAME = 0x01, /* < 64 bytes */ ++ MCS7830_RX_LENGTH_ERROR = 0x02, /* framelen != Ethernet length field */ ++ MCS7830_RX_ALIGNMENT_ERROR = 0x04, /* non-even number of nibbles */ ++ MCS7830_RX_CRC_ERROR = 0x08, ++ MCS7830_RX_LARGE_FRAME = 0x10, /* > 1518 bytes */ ++ MCS7830_RX_FRAME_CORRECT = 0x20, /* frame is correct */ ++ /* [7:6] reserved */ ++}; ++ ++struct mcs7830_data { ++ u8 multi_filter[8]; ++ u8 config; ++}; ++ ++static const char driver_name[] = "MOSCHIP usb-ethernet driver"; ++ ++static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data) ++{ ++ return usbnet_read_cmd(dev, MCS7830_RD_BREQ, MCS7830_RD_BMREQ, ++ 0x0000, index, data, size); ++} ++ ++static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, const void *data) ++{ ++ return usbnet_write_cmd(dev, MCS7830_WR_BREQ, MCS7830_WR_BMREQ, ++ 0x0000, index, data, size); ++} ++ ++static void mcs7830_set_reg_async(struct usbnet *dev, u16 index, u16 size, void *data) ++{ ++ usbnet_write_cmd_async(dev, MCS7830_WR_BREQ, MCS7830_WR_BMREQ, ++ 0x0000, index, data, size); ++} ++ ++static int mcs7830_hif_get_mac_address(struct usbnet *dev, unsigned char *addr) ++{ ++ int ret = mcs7830_get_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr); ++ if (ret < 0) ++ return ret; ++ return 0; ++} ++ ++static int mcs7830_hif_set_mac_address(struct usbnet *dev, unsigned char *addr) ++{ ++ int ret = mcs7830_set_reg(dev, HIF_REG_ETHERNET_ADDR, ETH_ALEN, addr); ++ ++ if (ret < 0) ++ return ret; ++ return 0; ++} ++ ++static int mcs7830_set_mac_address(struct net_device *netdev, void *p) ++{ ++ int ret; ++ struct usbnet *dev = netdev_priv(netdev); ++ struct sockaddr *addr = p; ++ ++ if (netif_running(netdev)) ++ return -EBUSY; ++ ++ if (!is_valid_ether_addr(addr->sa_data)) ++ return -EADDRNOTAVAIL; ++ ++ ret = mcs7830_hif_set_mac_address(dev, addr->sa_data); ++ ++ if (ret < 0) ++ return ret; ++ ++ /* it worked --> adopt it on netdev side */ ++ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); ++ ++ return 0; ++} ++ ++static int mcs7830_read_phy(struct usbnet *dev, u8 index) ++{ ++ int ret; ++ int i; ++ __le16 val; ++ ++ u8 cmd[2] = { ++ HIF_REG_PHY_CMD1_READ | HIF_REG_PHY_CMD1_PHYADDR, ++ HIF_REG_PHY_CMD2_PEND_FLAG_BIT | index, ++ }; ++ ++ mutex_lock(&dev->phy_mutex); ++ /* write the MII command */ ++ ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); ++ if (ret < 0) ++ goto out; ++ ++ /* wait for the data to become valid, should be within < 1ms */ ++ for (i = 0; i < 10; i++) { ++ ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); ++ if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT)) ++ break; ++ ret = -EIO; ++ msleep(1); ++ } ++ if (ret < 0) ++ goto out; ++ ++ /* read actual register contents */ ++ ret = mcs7830_get_reg(dev, HIF_REG_PHY_DATA, 2, &val); ++ if (ret < 0) ++ goto out; ++ ret = le16_to_cpu(val); ++ dev_dbg(&dev->udev->dev, "read PHY reg %02x: %04x (%d tries)\n", ++ index, val, i); ++out: ++ mutex_unlock(&dev->phy_mutex); ++ return ret; ++} ++ ++static int mcs7830_write_phy(struct usbnet *dev, u8 index, u16 val) ++{ ++ int ret; ++ int i; ++ __le16 le_val; ++ ++ u8 cmd[2] = { ++ HIF_REG_PHY_CMD1_WRITE | HIF_REG_PHY_CMD1_PHYADDR, ++ HIF_REG_PHY_CMD2_PEND_FLAG_BIT | (index & 0x1F), ++ }; ++ ++ mutex_lock(&dev->phy_mutex); ++ ++ /* write the new register contents */ ++ le_val = cpu_to_le16(val); ++ ret = mcs7830_set_reg(dev, HIF_REG_PHY_DATA, 2, &le_val); ++ if (ret < 0) ++ goto out; ++ ++ /* write the MII command */ ++ ret = mcs7830_set_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); ++ if (ret < 0) ++ goto out; ++ ++ /* wait for the command to be accepted by the PHY */ ++ for (i = 0; i < 10; i++) { ++ ret = mcs7830_get_reg(dev, HIF_REG_PHY_CMD1, 2, cmd); ++ if ((ret < 0) || (cmd[1] & HIF_REG_PHY_CMD2_READY_FLAG_BIT)) ++ break; ++ ret = -EIO; ++ msleep(1); ++ } ++ if (ret < 0) ++ goto out; ++ ++ ret = 0; ++ dev_dbg(&dev->udev->dev, "write PHY reg %02x: %04x (%d tries)\n", ++ index, val, i); ++out: ++ mutex_unlock(&dev->phy_mutex); ++ return ret; ++} ++ ++/* ++ * This algorithm comes from the original mcs7830 version 1.4 driver, ++ * not sure if it is needed. ++ */ ++static int mcs7830_set_autoneg(struct usbnet *dev, int ptrUserPhyMode) ++{ ++ int ret; ++ /* Enable all media types */ ++ ret = mcs7830_write_phy(dev, MII_ADVERTISE, MCS7830_MII_ADVERTISE); ++ ++ /* First reset BMCR */ ++ if (!ret) ++ ret = mcs7830_write_phy(dev, MII_BMCR, 0x0000); ++ /* Enable Auto Neg */ ++ if (!ret) ++ ret = mcs7830_write_phy(dev, MII_BMCR, BMCR_ANENABLE); ++ /* Restart Auto Neg (Keep the Enable Auto Neg Bit Set) */ ++ if (!ret) ++ ret = mcs7830_write_phy(dev, MII_BMCR, ++ BMCR_ANENABLE | BMCR_ANRESTART ); ++ return ret; ++} ++ ++ ++/* ++ * if we can read register 22, the chip revision is C or higher ++ */ ++static int mcs7830_get_rev(struct usbnet *dev) ++{ ++ u8 dummy[2]; ++ int ret; ++ ret = mcs7830_get_reg(dev, HIF_REG_FRAME_DROP_COUNTER, 2, dummy); ++ if (ret > 0) ++ return 2; /* Rev C or later */ ++ return 1; /* earlier revision */ ++} ++ ++/* ++ * On rev. C we need to set the pause threshold ++ */ ++static void mcs7830_rev_C_fixup(struct usbnet *dev) ++{ ++ u8 pause_threshold = HIF_REG_PAUSE_THRESHOLD_DEFAULT; ++ int retry; ++ ++ for (retry = 0; retry < 2; retry++) { ++ if (mcs7830_get_rev(dev) == 2) { ++ dev_info(&dev->udev->dev, "applying rev.C fixup\n"); ++ mcs7830_set_reg(dev, HIF_REG_PAUSE_THRESHOLD, ++ 1, &pause_threshold); ++ } ++ msleep(1); ++ } ++} ++ ++static int mcs7830_mdio_read(struct net_device *netdev, int phy_id, ++ int location) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ return mcs7830_read_phy(dev, location); ++} ++ ++static void mcs7830_mdio_write(struct net_device *netdev, int phy_id, ++ int location, int val) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ mcs7830_write_phy(dev, location, val); ++} ++ ++static int mcs7830_ioctl(struct net_device *net, struct ifreq *rq, int cmd) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); ++} ++ ++static inline struct mcs7830_data *mcs7830_get_data(struct usbnet *dev) ++{ ++ return (struct mcs7830_data *)&dev->data; ++} ++ ++static void mcs7830_hif_update_multicast_hash(struct usbnet *dev) ++{ ++ struct mcs7830_data *data = mcs7830_get_data(dev); ++ mcs7830_set_reg_async(dev, HIF_REG_MULTICAST_HASH, ++ sizeof data->multi_filter, ++ data->multi_filter); ++} ++ ++static void mcs7830_hif_update_config(struct usbnet *dev) ++{ ++ /* implementation specific to data->config ++ (argument needs to be heap-based anyway - USB DMA!) */ ++ struct mcs7830_data *data = mcs7830_get_data(dev); ++ mcs7830_set_reg_async(dev, HIF_REG_CONFIG, 1, &data->config); ++} ++ ++static void mcs7830_data_set_multicast(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct mcs7830_data *data = mcs7830_get_data(dev); ++ ++ memset(data->multi_filter, 0, sizeof data->multi_filter); ++ ++ data->config = HIF_REG_CONFIG_TXENABLE; ++ ++ /* this should not be needed, but it doesn't work otherwise */ ++ data->config |= HIF_REG_CONFIG_ALLMULTICAST; ++ ++ if (net->flags & IFF_PROMISC) { ++ data->config |= HIF_REG_CONFIG_PROMISCUOUS; ++ } else if (net->flags & IFF_ALLMULTI || ++ netdev_mc_count(net) > MCS7830_MAX_MCAST) { ++ data->config |= HIF_REG_CONFIG_ALLMULTICAST; ++ } else if (netdev_mc_empty(net)) { ++ /* just broadcast and directed */ ++ } else { ++ /* We use the 20 byte dev->data ++ * for our 8 byte filter buffer ++ * to avoid allocating memory that ++ * is tricky to free later */ ++ struct netdev_hw_addr *ha; ++ u32 crc_bits; ++ ++ /* Build the multicast hash filter. */ ++ netdev_for_each_mc_addr(ha, net) { ++ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; ++ data->multi_filter[crc_bits >> 3] |= 1 << (crc_bits & 7); ++ } ++ } ++} ++ ++static int mcs7830_apply_base_config(struct usbnet *dev) ++{ ++ int ret; ++ ++ /* re-configure known MAC (suspend case etc.) */ ++ ret = mcs7830_hif_set_mac_address(dev, dev->net->dev_addr); ++ if (ret) { ++ dev_info(&dev->udev->dev, "Cannot set MAC address\n"); ++ goto out; ++ } ++ ++ /* Set up PHY */ ++ ret = mcs7830_set_autoneg(dev, 0); ++ if (ret) { ++ dev_info(&dev->udev->dev, "Cannot set autoneg\n"); ++ goto out; ++ } ++ ++ mcs7830_hif_update_multicast_hash(dev); ++ mcs7830_hif_update_config(dev); ++ ++ mcs7830_rev_C_fixup(dev); ++ ret = 0; ++out: ++ return ret; ++} ++ ++/* credits go to asix_set_multicast */ ++static void mcs7830_set_multicast(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ ++ mcs7830_data_set_multicast(net); ++ ++ mcs7830_hif_update_multicast_hash(dev); ++ mcs7830_hif_update_config(dev); ++} ++ ++static int mcs7830_get_regs_len(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ ++ switch (mcs7830_get_rev(dev)) { ++ case 1: ++ return 21; ++ case 2: ++ return 32; ++ } ++ return 0; ++} ++ ++static void mcs7830_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *drvinfo) ++{ ++ usbnet_get_drvinfo(net, drvinfo); ++ drvinfo->regdump_len = mcs7830_get_regs_len(net); ++} ++ ++static void mcs7830_get_regs(struct net_device *net, struct ethtool_regs *regs, void *data) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ ++ regs->version = mcs7830_get_rev(dev); ++ mcs7830_get_reg(dev, 0, regs->len, data); ++} ++ ++static const struct ethtool_ops mcs7830_ethtool_ops = { ++ .get_drvinfo = mcs7830_get_drvinfo, ++ .get_regs_len = mcs7830_get_regs_len, ++ .get_regs = mcs7830_get_regs, ++ ++ /* common usbnet calls */ ++ .get_link = usbnet_get_link, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_settings = usbnet_get_settings, ++ .set_settings = usbnet_set_settings, ++ .nway_reset = usbnet_nway_reset, ++}; ++ ++static const struct net_device_ops mcs7830_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = usbnet_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_do_ioctl = mcs7830_ioctl, ++ .ndo_set_rx_mode = mcs7830_set_multicast, ++ .ndo_set_mac_address = mcs7830_set_mac_address, ++}; ++ ++static int mcs7830_bind(struct usbnet *dev, struct usb_interface *udev) ++{ ++ struct net_device *net = dev->net; ++ int ret; ++ int retry; ++ ++ /* Initial startup: Gather MAC address setting from EEPROM */ ++ ret = -EINVAL; ++ for (retry = 0; retry < 5 && ret; retry++) ++ ret = mcs7830_hif_get_mac_address(dev, net->dev_addr); ++ if (ret) { ++ dev_warn(&dev->udev->dev, "Cannot read MAC address\n"); ++ goto out; ++ } ++ ++ mcs7830_data_set_multicast(net); ++ ++ ret = mcs7830_apply_base_config(dev); ++ if (ret) ++ goto out; ++ ++ net->ethtool_ops = &mcs7830_ethtool_ops; ++ net->netdev_ops = &mcs7830_netdev_ops; ++ ++ /* reserve space for the status byte on rx */ ++ dev->rx_urb_size = ETH_FRAME_LEN + 1; ++ ++ dev->mii.mdio_read = mcs7830_mdio_read; ++ dev->mii.mdio_write = mcs7830_mdio_write; ++ dev->mii.dev = net; ++ dev->mii.phy_id_mask = 0x3f; ++ dev->mii.reg_num_mask = 0x1f; ++ dev->mii.phy_id = *((u8 *) net->dev_addr + 1); ++ ++ ret = usbnet_get_endpoints(dev, udev); ++out: ++ return ret; ++} ++ ++/* The chip always appends a status byte that we need to strip */ ++static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ u8 status; ++ ++ /* This check is no longer done by usbnet */ ++ if (skb->len < dev->net->hard_header_len) { ++ dev_err(&dev->udev->dev, "unexpected tiny rx frame\n"); ++ return 0; ++ } ++ ++ skb_trim(skb, skb->len - 1); ++ status = skb->data[skb->len]; ++ ++ if (status != MCS7830_RX_FRAME_CORRECT) { ++ dev_dbg(&dev->udev->dev, "rx fixup status %x\n", status); ++ ++ /* hmm, perhaps usbnet.c already sees a globally visible ++ frame error and increments rx_errors on its own already? */ ++ dev->net->stats.rx_errors++; ++ ++ if (status & (MCS7830_RX_SHORT_FRAME ++ |MCS7830_RX_LENGTH_ERROR ++ |MCS7830_RX_LARGE_FRAME)) ++ dev->net->stats.rx_length_errors++; ++ if (status & MCS7830_RX_ALIGNMENT_ERROR) ++ dev->net->stats.rx_frame_errors++; ++ if (status & MCS7830_RX_CRC_ERROR) ++ dev->net->stats.rx_crc_errors++; ++ } ++ ++ return skb->len > 0; ++} ++ ++static void mcs7830_status(struct usbnet *dev, struct urb *urb) ++{ ++ u8 *buf = urb->transfer_buffer; ++ bool link, link_changed; ++ ++ if (urb->actual_length < 16) ++ return; ++ ++ link = !(buf[1] == 0x20); ++ link_changed = netif_carrier_ok(dev->net) != link; ++ if (link_changed) { ++ usbnet_link_change(dev, link, 0); ++ netdev_dbg(dev->net, "Link Status is: %d\n", link); ++ } ++} ++ ++static const struct driver_info moschip_info = { ++ .description = "MOSCHIP 7830/7832/7730 usb-NET adapter", ++ .bind = mcs7830_bind, ++ .rx_fixup = mcs7830_rx_fixup, ++ .flags = FLAG_ETHER | FLAG_LINK_INTR, ++ .status = mcs7830_status, ++ .in = 1, ++ .out = 2, ++}; ++ ++static const struct driver_info sitecom_info = { ++ .description = "Sitecom LN-30 usb-NET adapter", ++ .bind = mcs7830_bind, ++ .rx_fixup = mcs7830_rx_fixup, ++ .flags = FLAG_ETHER | FLAG_LINK_INTR, ++ .status = mcs7830_status, ++ .in = 1, ++ .out = 2, ++}; ++ ++static const struct usb_device_id products[] = { ++ { ++ USB_DEVICE(MCS7830_VENDOR_ID, MCS7832_PRODUCT_ID), ++ .driver_info = (unsigned long) &moschip_info, ++ }, ++ { ++ USB_DEVICE(MCS7830_VENDOR_ID, MCS7830_PRODUCT_ID), ++ .driver_info = (unsigned long) &moschip_info, ++ }, ++ { ++ USB_DEVICE(MCS7830_VENDOR_ID, MCS7730_PRODUCT_ID), ++ .driver_info = (unsigned long) &moschip_info, ++ }, ++ { ++ USB_DEVICE(SITECOM_VENDOR_ID, LN_030_PRODUCT_ID), ++ .driver_info = (unsigned long) &sitecom_info, ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static int mcs7830_reset_resume (struct usb_interface *intf) ++{ ++ /* YES, this function is successful enough that ethtool -d ++ does show same output pre-/post-suspend */ ++ ++ struct usbnet *dev = usb_get_intfdata(intf); ++ ++ mcs7830_apply_base_config(dev); ++ ++ usbnet_resume(intf); ++ ++ return 0; ++} ++ ++static struct usb_driver mcs7830_driver = { ++ .name = driver_name, ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .reset_resume = mcs7830_reset_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(mcs7830_driver); ++ ++MODULE_DESCRIPTION("USB to network adapter MCS7830)"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/net1080.c backports-3.18.1-1/drivers/net/usb/net1080.c +--- backports-3.18.1-1.org/drivers/net/usb/net1080.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/net1080.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,544 @@ ++/* ++ * Net1080 based USB host-to-host cables ++ * Copyright (C) 2000-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, see . ++ */ ++ ++// #define DEBUG // error path messages, extra info ++// #define VERBOSE // more; success messages ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++ ++/* ++ * Netchip 1080 driver ... http://www.netchip.com ++ * (Sept 2004: End-of-life announcement has been sent.) ++ * Used in (some) LapLink cables ++ */ ++ ++#define frame_errors data[1] ++ ++/* ++ * NetChip framing of ethernet packets, supporting additional error ++ * checks for links that may drop bulk packets from inside messages. ++ * Odd USB length == always short read for last usb packet. ++ * - nc_header ++ * - Ethernet header (14 bytes) ++ * - payload ++ * - (optional padding byte, if needed so length becomes odd) ++ * - nc_trailer ++ * ++ * This framing is to be avoided for non-NetChip devices. ++ */ ++ ++struct nc_header { // packed: ++ __le16 hdr_len; // sizeof nc_header (LE, all) ++ __le16 packet_len; // payload size (including ethhdr) ++ __le16 packet_id; // detects dropped packets ++#define MIN_HEADER 6 ++ ++ // all else is optional, and must start with: ++ // __le16 vendorId; // from usb-if ++ // __le16 productId; ++} __packed; ++ ++#define PAD_BYTE ((unsigned char)0xAC) ++ ++struct nc_trailer { ++ __le16 packet_id; ++} __packed; ++ ++// packets may use FLAG_FRAMING_NC and optional pad ++#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \ ++ + sizeof (struct ethhdr) \ ++ + (mtu) \ ++ + 1 \ ++ + sizeof (struct nc_trailer)) ++ ++#define MIN_FRAMED FRAMED_SIZE(0) ++ ++/* packets _could_ be up to 64KB... */ ++#define NC_MAX_PACKET 32767 ++ ++ ++/* ++ * Zero means no timeout; else, how long a 64 byte bulk packet may be queued ++ * before the hardware drops it. If that's done, the driver will need to ++ * frame network packets to guard against the dropped USB packets. The win32 ++ * driver sets this for both sides of the link. ++ */ ++#define NC_READ_TTL_MS ((u8)255) // ms ++ ++/* ++ * We ignore most registers and EEPROM contents. ++ */ ++#define REG_USBCTL ((u8)0x04) ++#define REG_TTL ((u8)0x10) ++#define REG_STATUS ((u8)0x11) ++ ++/* ++ * Vendor specific requests to read/write data ++ */ ++#define REQUEST_REGISTER ((u8)0x10) ++#define REQUEST_EEPROM ((u8)0x11) ++ ++static int ++nc_vendor_read(struct usbnet *dev, u8 req, u8 regnum, u16 *retval_ptr) ++{ ++ int status = usbnet_read_cmd(dev, req, ++ USB_DIR_IN | USB_TYPE_VENDOR | ++ USB_RECIP_DEVICE, ++ 0, regnum, retval_ptr, ++ sizeof *retval_ptr); ++ if (status > 0) ++ status = 0; ++ if (!status) ++ le16_to_cpus(retval_ptr); ++ return status; ++} ++ ++static inline int ++nc_register_read(struct usbnet *dev, u8 regnum, u16 *retval_ptr) ++{ ++ return nc_vendor_read(dev, REQUEST_REGISTER, regnum, retval_ptr); ++} ++ ++// no retval ... can become async, usable in_interrupt() ++static void ++nc_vendor_write(struct usbnet *dev, u8 req, u8 regnum, u16 value) ++{ ++ usbnet_write_cmd(dev, req, ++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ value, regnum, NULL, 0); ++} ++ ++static inline void ++nc_register_write(struct usbnet *dev, u8 regnum, u16 value) ++{ ++ nc_vendor_write(dev, REQUEST_REGISTER, regnum, value); ++} ++ ++ ++#if 0 ++static void nc_dump_registers(struct usbnet *dev) ++{ ++ u8 reg; ++ u16 *vp = kmalloc(sizeof (u16)); ++ ++ if (!vp) ++ return; ++ ++ netdev_dbg(dev->net, "registers:\n"); ++ for (reg = 0; reg < 0x20; reg++) { ++ int retval; ++ ++ // reading some registers is trouble ++ if (reg >= 0x08 && reg <= 0xf) ++ continue; ++ if (reg >= 0x12 && reg <= 0x1e) ++ continue; ++ ++ retval = nc_register_read(dev, reg, vp); ++ if (retval < 0) ++ netdev_dbg(dev->net, "reg [0x%x] ==> error %d\n", ++ reg, retval); ++ else ++ netdev_dbg(dev->net, "reg [0x%x] = 0x%x\n", reg, *vp); ++ } ++ kfree(vp); ++} ++#endif ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * Control register ++ */ ++ ++#define USBCTL_WRITABLE_MASK 0x1f0f ++// bits 15-13 reserved, r/o ++#define USBCTL_ENABLE_LANG (1 << 12) ++#define USBCTL_ENABLE_MFGR (1 << 11) ++#define USBCTL_ENABLE_PROD (1 << 10) ++#define USBCTL_ENABLE_SERIAL (1 << 9) ++#define USBCTL_ENABLE_DEFAULTS (1 << 8) ++// bits 7-4 reserved, r/o ++#define USBCTL_FLUSH_OTHER (1 << 3) ++#define USBCTL_FLUSH_THIS (1 << 2) ++#define USBCTL_DISCONN_OTHER (1 << 1) ++#define USBCTL_DISCONN_THIS (1 << 0) ++ ++static inline void nc_dump_usbctl(struct usbnet *dev, u16 usbctl) ++{ ++ netif_dbg(dev, link, dev->net, ++ "net1080 %s-%s usbctl 0x%x:%s%s%s%s%s; this%s%s; other%s%s; r/o 0x%x\n", ++ dev->udev->bus->bus_name, dev->udev->devpath, ++ usbctl, ++ (usbctl & USBCTL_ENABLE_LANG) ? " lang" : "", ++ (usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "", ++ (usbctl & USBCTL_ENABLE_PROD) ? " prod" : "", ++ (usbctl & USBCTL_ENABLE_SERIAL) ? " serial" : "", ++ (usbctl & USBCTL_ENABLE_DEFAULTS) ? " defaults" : "", ++ ++ (usbctl & USBCTL_FLUSH_THIS) ? " FLUSH" : "", ++ (usbctl & USBCTL_DISCONN_THIS) ? " DIS" : "", ++ ++ (usbctl & USBCTL_FLUSH_OTHER) ? " FLUSH" : "", ++ (usbctl & USBCTL_DISCONN_OTHER) ? " DIS" : "", ++ ++ usbctl & ~USBCTL_WRITABLE_MASK); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * Status register ++ */ ++ ++#define STATUS_PORT_A (1 << 15) ++ ++#define STATUS_CONN_OTHER (1 << 14) ++#define STATUS_SUSPEND_OTHER (1 << 13) ++#define STATUS_MAILBOX_OTHER (1 << 12) ++#define STATUS_PACKETS_OTHER(n) (((n) >> 8) & 0x03) ++ ++#define STATUS_CONN_THIS (1 << 6) ++#define STATUS_SUSPEND_THIS (1 << 5) ++#define STATUS_MAILBOX_THIS (1 << 4) ++#define STATUS_PACKETS_THIS(n) (((n) >> 0) & 0x03) ++ ++#define STATUS_UNSPEC_MASK 0x0c8c ++#define STATUS_NOISE_MASK ((u16)~(0x0303|STATUS_UNSPEC_MASK)) ++ ++ ++static inline void nc_dump_status(struct usbnet *dev, u16 status) ++{ ++ netif_dbg(dev, link, dev->net, ++ "net1080 %s-%s status 0x%x: this (%c) PKT=%d%s%s%s; other PKT=%d%s%s%s; unspec 0x%x\n", ++ dev->udev->bus->bus_name, dev->udev->devpath, ++ status, ++ ++ // XXX the packet counts don't seem right ++ // (1 at reset, not 0); maybe UNSPEC too ++ ++ (status & STATUS_PORT_A) ? 'A' : 'B', ++ STATUS_PACKETS_THIS(status), ++ (status & STATUS_CONN_THIS) ? " CON" : "", ++ (status & STATUS_SUSPEND_THIS) ? " SUS" : "", ++ (status & STATUS_MAILBOX_THIS) ? " MBOX" : "", ++ ++ STATUS_PACKETS_OTHER(status), ++ (status & STATUS_CONN_OTHER) ? " CON" : "", ++ (status & STATUS_SUSPEND_OTHER) ? " SUS" : "", ++ (status & STATUS_MAILBOX_OTHER) ? " MBOX" : "", ++ ++ status & STATUS_UNSPEC_MASK); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * TTL register ++ */ ++ ++#define TTL_THIS(ttl) (0x00ff & ttl) ++#define TTL_OTHER(ttl) (0x00ff & (ttl >> 8)) ++#define MK_TTL(this,other) ((u16)(((other)<<8)|(0x00ff&(this)))) ++ ++static inline void nc_dump_ttl(struct usbnet *dev, u16 ttl) ++{ ++ netif_dbg(dev, link, dev->net, "net1080 %s-%s ttl 0x%x this = %d, other = %d\n", ++ dev->udev->bus->bus_name, dev->udev->devpath, ++ ttl, TTL_THIS(ttl), TTL_OTHER(ttl)); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int net1080_reset(struct usbnet *dev) ++{ ++ u16 usbctl, status, ttl; ++ u16 vp; ++ int retval; ++ ++ // nc_dump_registers(dev); ++ ++ if ((retval = nc_register_read(dev, REG_STATUS, &vp)) < 0) { ++ netdev_dbg(dev->net, "can't read %s-%s status: %d\n", ++ dev->udev->bus->bus_name, dev->udev->devpath, retval); ++ goto done; ++ } ++ status = vp; ++ nc_dump_status(dev, status); ++ ++ if ((retval = nc_register_read(dev, REG_USBCTL, &vp)) < 0) { ++ netdev_dbg(dev->net, "can't read USBCTL, %d\n", retval); ++ goto done; ++ } ++ usbctl = vp; ++ nc_dump_usbctl(dev, usbctl); ++ ++ nc_register_write(dev, REG_USBCTL, ++ USBCTL_FLUSH_THIS | USBCTL_FLUSH_OTHER); ++ ++ if ((retval = nc_register_read(dev, REG_TTL, &vp)) < 0) { ++ netdev_dbg(dev->net, "can't read TTL, %d\n", retval); ++ goto done; ++ } ++ ttl = vp; ++ // nc_dump_ttl(dev, ttl); ++ ++ nc_register_write(dev, REG_TTL, ++ MK_TTL(NC_READ_TTL_MS, TTL_OTHER(ttl)) ); ++ netdev_dbg(dev->net, "assigned TTL, %d ms\n", NC_READ_TTL_MS); ++ ++ netif_info(dev, link, dev->net, "port %c, peer %sconnected\n", ++ (status & STATUS_PORT_A) ? 'A' : 'B', ++ (status & STATUS_CONN_OTHER) ? "" : "dis"); ++ retval = 0; ++ ++done: ++ return retval; ++} ++ ++static int net1080_check_connect(struct usbnet *dev) ++{ ++ int retval; ++ u16 status; ++ u16 vp; ++ ++ retval = nc_register_read(dev, REG_STATUS, &vp); ++ status = vp; ++ if (retval != 0) { ++ netdev_dbg(dev->net, "net1080_check_conn read - %d\n", retval); ++ return retval; ++ } ++ if ((status & STATUS_CONN_OTHER) != STATUS_CONN_OTHER) ++ return -ENOLINK; ++ return 0; ++} ++ ++static void nc_ensure_sync(struct usbnet *dev) ++{ ++ if (++dev->frame_errors <= 5) ++ return; ++ ++ if (usbnet_write_cmd_async(dev, REQUEST_REGISTER, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_DEVICE, ++ USBCTL_FLUSH_THIS | ++ USBCTL_FLUSH_OTHER, ++ REG_USBCTL, NULL, 0)) ++ return; ++ ++ netif_dbg(dev, rx_err, dev->net, ++ "flush net1080; too many framing errors\n"); ++ dev->frame_errors = 0; ++} ++ ++static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ struct nc_header *header; ++ struct nc_trailer *trailer; ++ u16 hdr_len, packet_len; ++ ++ /* This check is no longer done by usbnet */ ++ if (skb->len < dev->net->hard_header_len) ++ return 0; ++ ++ if (!(skb->len & 0x01)) { ++ netdev_dbg(dev->net, "rx framesize %d range %d..%d mtu %d\n", ++ skb->len, dev->net->hard_header_len, dev->hard_mtu, ++ dev->net->mtu); ++ dev->net->stats.rx_frame_errors++; ++ nc_ensure_sync(dev); ++ return 0; ++ } ++ ++ header = (struct nc_header *) skb->data; ++ hdr_len = le16_to_cpup(&header->hdr_len); ++ packet_len = le16_to_cpup(&header->packet_len); ++ if (FRAMED_SIZE(packet_len) > NC_MAX_PACKET) { ++ dev->net->stats.rx_frame_errors++; ++ netdev_dbg(dev->net, "packet too big, %d\n", packet_len); ++ nc_ensure_sync(dev); ++ return 0; ++ } else if (hdr_len < MIN_HEADER) { ++ dev->net->stats.rx_frame_errors++; ++ netdev_dbg(dev->net, "header too short, %d\n", hdr_len); ++ nc_ensure_sync(dev); ++ return 0; ++ } else if (hdr_len > MIN_HEADER) { ++ // out of band data for us? ++ netdev_dbg(dev->net, "header OOB, %d bytes\n", hdr_len - MIN_HEADER); ++ nc_ensure_sync(dev); ++ // switch (vendor/product ids) { ... } ++ } ++ skb_pull(skb, hdr_len); ++ ++ trailer = (struct nc_trailer *) ++ (skb->data + skb->len - sizeof *trailer); ++ skb_trim(skb, skb->len - sizeof *trailer); ++ ++ if ((packet_len & 0x01) == 0) { ++ if (skb->data [packet_len] != PAD_BYTE) { ++ dev->net->stats.rx_frame_errors++; ++ netdev_dbg(dev->net, "bad pad\n"); ++ return 0; ++ } ++ skb_trim(skb, skb->len - 1); ++ } ++ if (skb->len != packet_len) { ++ dev->net->stats.rx_frame_errors++; ++ netdev_dbg(dev->net, "bad packet len %d (expected %d)\n", ++ skb->len, packet_len); ++ nc_ensure_sync(dev); ++ return 0; ++ } ++ if (header->packet_id != get_unaligned(&trailer->packet_id)) { ++ dev->net->stats.rx_fifo_errors++; ++ netdev_dbg(dev->net, "(2+ dropped) rx packet_id mismatch 0x%x 0x%x\n", ++ le16_to_cpu(header->packet_id), ++ le16_to_cpu(trailer->packet_id)); ++ return 0; ++ } ++#if 0 ++ netdev_dbg(dev->net, "frame hdr_len, ++ header->packet_len, header->packet_id); ++#endif ++ dev->frame_errors = 0; ++ return 1; ++} ++ ++static struct sk_buff * ++net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) ++{ ++ struct sk_buff *skb2; ++ struct nc_header *header = NULL; ++ struct nc_trailer *trailer = NULL; ++ int padlen = sizeof (struct nc_trailer); ++ int len = skb->len; ++ ++ if (!((len + padlen + sizeof (struct nc_header)) & 0x01)) ++ padlen++; ++ if (!skb_cloned(skb)) { ++ int headroom = skb_headroom(skb); ++ int tailroom = skb_tailroom(skb); ++ ++ if (padlen <= tailroom && ++ sizeof(struct nc_header) <= headroom) ++ /* There's enough head and tail room */ ++ goto encapsulate; ++ ++ if ((sizeof (struct nc_header) + padlen) < ++ (headroom + tailroom)) { ++ /* There's enough total room, so just readjust */ ++ skb->data = memmove(skb->head ++ + sizeof (struct nc_header), ++ skb->data, skb->len); ++ skb_set_tail_pointer(skb, len); ++ goto encapsulate; ++ } ++ } ++ ++ /* Create a new skb to use with the correct size */ ++ skb2 = skb_copy_expand(skb, ++ sizeof (struct nc_header), ++ padlen, ++ flags); ++ dev_kfree_skb_any(skb); ++ if (!skb2) ++ return skb2; ++ skb = skb2; ++ ++encapsulate: ++ /* header first */ ++ header = (struct nc_header *) skb_push(skb, sizeof *header); ++ header->hdr_len = cpu_to_le16(sizeof (*header)); ++ header->packet_len = cpu_to_le16(len); ++ header->packet_id = cpu_to_le16((u16)dev->xid++); ++ ++ /* maybe pad; then trailer */ ++ if (!((skb->len + sizeof *trailer) & 0x01)) ++ *skb_put(skb, 1) = PAD_BYTE; ++ trailer = (struct nc_trailer *) skb_put(skb, sizeof *trailer); ++ put_unaligned(header->packet_id, &trailer->packet_id); ++#if 0 ++ netdev_dbg(dev->net, "frame >tx h %d p %d id %d\n", ++ header->hdr_len, header->packet_len, ++ header->packet_id); ++#endif ++ return skb; ++} ++ ++static int net1080_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ unsigned extra = sizeof (struct nc_header) ++ + 1 ++ + sizeof (struct nc_trailer); ++ ++ dev->net->hard_header_len += extra; ++ dev->rx_urb_size = dev->net->hard_header_len + dev->net->mtu; ++ dev->hard_mtu = NC_MAX_PACKET; ++ return usbnet_get_endpoints (dev, intf); ++} ++ ++static const struct driver_info net1080_info = { ++ .description = "NetChip TurboCONNECT", ++ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_NC, ++ .bind = net1080_bind, ++ .reset = net1080_reset, ++ .check_connect = net1080_check_connect, ++ .rx_fixup = net1080_rx_fixup, ++ .tx_fixup = net1080_tx_fixup, ++}; ++ ++static const struct usb_device_id products [] = { ++{ ++ USB_DEVICE(0x0525, 0x1080), // NetChip ref design ++ .driver_info = (unsigned long) &net1080_info, ++}, { ++ USB_DEVICE(0x06D0, 0x0622), // Laplink Gold ++ .driver_info = (unsigned long) &net1080_info, ++}, ++ { }, // END ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver net1080_driver = { ++ .name = "net1080", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(net1080_driver); ++ ++MODULE_AUTHOR("David Brownell"); ++MODULE_DESCRIPTION("NetChip 1080 based USB Host-to-Host Links"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/pegasus.c backports-3.18.1-1/drivers/net/usb/pegasus.c +--- backports-3.18.1-1.org/drivers/net/usb/pegasus.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/pegasus.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,1335 @@ ++/* ++ * Copyright (c) 1999-2013 Petko Manolov (petkan@nucleusys.com) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * ChangeLog: ++ * .... Most of the time spent on reading sources & docs. ++ * v0.2.x First official release for the Linux kernel. ++ * v0.3.0 Beutified and structured, some bugs fixed. ++ * v0.3.x URBifying bulk requests and bugfixing. First relatively ++ * stable release. Still can touch device's registers only ++ * from top-halves. ++ * v0.4.0 Control messages remained unurbified are now URBs. ++ * Now we can touch the HW at any time. ++ * v0.4.9 Control urbs again use process context to wait. Argh... ++ * Some long standing bugs (enable_net_traffic) fixed. ++ * Also nasty trick about resubmiting control urb from ++ * interrupt context used. Please let me know how it ++ * behaves. Pegasus II support added since this version. ++ * TODO: suppressing HCD warnings spewage on disconnect. ++ * v0.4.13 Ethernet address is now set at probe(), not at open() ++ * time as this seems to break dhcpd. ++ * v0.5.0 branch to 2.5.x kernels ++ * v0.5.1 ethtool support added ++ * v0.5.5 rx socket buffers are in a pool and the their allocation ++ * is out of the interrupt routine. ++ * ... ++ * v0.9.3 simplified [get|set]_register(s), async update registers ++ * logic revisited, receive skb_pool removed. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "pegasus.h" ++ ++/* ++ * Version Information ++ */ ++#define DRIVER_VERSION "v0.9.3 (2013/04/25)" ++#define DRIVER_AUTHOR "Petko Manolov " ++#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" ++ ++static const char driver_name[] = "pegasus"; ++ ++#undef PEGASUS_WRITE_EEPROM ++#define BMSR_MEDIA (BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | \ ++ BMSR_100FULL | BMSR_ANEGCAPABLE) ++ ++static bool loopback; ++static bool mii_mode; ++static char *devid; ++ ++static struct usb_eth_dev usb_dev_id[] = { ++#define PEGASUS_DEV(pn, vid, pid, flags) \ ++ {.name = pn, .vendor = vid, .device = pid, .private = flags}, ++#define PEGASUS_DEV_CLASS(pn, vid, pid, dclass, flags) \ ++ PEGASUS_DEV(pn, vid, pid, flags) ++#include "pegasus.h" ++#undef PEGASUS_DEV ++#undef PEGASUS_DEV_CLASS ++ {NULL, 0, 0, 0}, ++ {NULL, 0, 0, 0} ++}; ++ ++static struct usb_device_id pegasus_ids[] = { ++#define PEGASUS_DEV(pn, vid, pid, flags) \ ++ {.match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = vid, .idProduct = pid}, ++/* ++ * The Belkin F8T012xx1 bluetooth adaptor has the same vendor and product ++ * IDs as the Belkin F5D5050, so we need to teach the pegasus driver to ++ * ignore adaptors belonging to the "Wireless" class 0xE0. For this one ++ * case anyway, seeing as the pegasus is for "Wired" adaptors. ++ */ ++#define PEGASUS_DEV_CLASS(pn, vid, pid, dclass, flags) \ ++ {.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_CLASS), \ ++ .idVendor = vid, .idProduct = pid, .bDeviceClass = dclass}, ++#include "pegasus.h" ++#undef PEGASUS_DEV ++#undef PEGASUS_DEV_CLASS ++ {}, ++ {} ++}; ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL"); ++module_param(loopback, bool, 0); ++module_param(mii_mode, bool, 0); ++module_param(devid, charp, 0); ++MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)"); ++MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0"); ++MODULE_PARM_DESC(devid, "The format is: 'DEV_name:VendorID:DeviceID:Flags'"); ++ ++/* use ethtool to change the level for any given device */ ++static int msg_level = -1; ++module_param(msg_level, int, 0); ++MODULE_PARM_DESC(msg_level, "Override default message level"); ++ ++MODULE_DEVICE_TABLE(usb, pegasus_ids); ++static const struct net_device_ops pegasus_netdev_ops; ++ ++/*****/ ++ ++static void async_ctrl_callback(struct urb *urb) ++{ ++ struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context; ++ int status = urb->status; ++ ++ if (status < 0) ++ dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status); ++ kfree(req); ++ usb_free_urb(urb); ++} ++ ++static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) ++{ ++ int ret; ++ ++ ret = usb_control_msg(pegasus->usb, usb_rcvctrlpipe(pegasus->usb, 0), ++ PEGASUS_REQ_GET_REGS, PEGASUS_REQT_READ, 0, ++ indx, data, size, 1000); ++ if (ret < 0) ++ netif_dbg(pegasus, drv, pegasus->net, ++ "%s returned %d\n", __func__, ret); ++ return ret; ++} ++ ++static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) ++{ ++ int ret; ++ ++ ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0), ++ PEGASUS_REQ_SET_REGS, PEGASUS_REQT_WRITE, 0, ++ indx, data, size, 100); ++ if (ret < 0) ++ netif_dbg(pegasus, drv, pegasus->net, ++ "%s returned %d\n", __func__, ret); ++ return ret; ++} ++ ++static int set_register(pegasus_t *pegasus, __u16 indx, __u8 data) ++{ ++ int ret; ++ ++ ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0), ++ PEGASUS_REQ_SET_REG, PEGASUS_REQT_WRITE, data, ++ indx, &data, 1, 1000); ++ if (ret < 0) ++ netif_dbg(pegasus, drv, pegasus->net, ++ "%s returned %d\n", __func__, ret); ++ return ret; ++} ++ ++static int update_eth_regs_async(pegasus_t *pegasus) ++{ ++ int ret = -ENOMEM; ++ struct urb *async_urb; ++ struct usb_ctrlrequest *req; ++ ++ req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); ++ if (req == NULL) ++ return ret; ++ ++ async_urb = usb_alloc_urb(0, GFP_ATOMIC); ++ if (async_urb == NULL) { ++ kfree(req); ++ return ret; ++ } ++ req->bRequestType = PEGASUS_REQT_WRITE; ++ req->bRequest = PEGASUS_REQ_SET_REGS; ++ req->wValue = cpu_to_le16(0); ++ req->wIndex = cpu_to_le16(EthCtrl0); ++ req->wLength = cpu_to_le16(3); ++ ++ usb_fill_control_urb(async_urb, pegasus->usb, ++ usb_sndctrlpipe(pegasus->usb, 0), (void *)req, ++ pegasus->eth_regs, 3, async_ctrl_callback, req); ++ ++ ret = usb_submit_urb(async_urb, GFP_ATOMIC); ++ if (ret) { ++ if (ret == -ENODEV) ++ netif_device_detach(pegasus->net); ++ netif_err(pegasus, drv, pegasus->net, ++ "%s returned %d\n", __func__, ret); ++ } ++ return ret; ++} ++ ++static int __mii_op(pegasus_t *p, __u8 phy, __u8 indx, __u16 *regd, __u8 cmd) ++{ ++ int i; ++ __u8 data[4] = { phy, 0, 0, indx }; ++ __le16 regdi; ++ int ret = -ETIMEDOUT; ++ ++ if (cmd & PHY_WRITE) { ++ __le16 *t = (__le16 *) & data[1]; ++ *t = cpu_to_le16(*regd); ++ } ++ set_register(p, PhyCtrl, 0); ++ set_registers(p, PhyAddr, sizeof(data), data); ++ set_register(p, PhyCtrl, (indx | cmd)); ++ for (i = 0; i < REG_TIMEOUT; i++) { ++ ret = get_registers(p, PhyCtrl, 1, data); ++ if (ret < 0) ++ goto fail; ++ if (data[0] & PHY_DONE) ++ break; ++ } ++ if (i >= REG_TIMEOUT) ++ goto fail; ++ if (cmd & PHY_READ) { ++ ret = get_registers(p, PhyData, 2, ®di); ++ *regd = le16_to_cpu(regdi); ++ return ret; ++ } ++ return 0; ++fail: ++ netif_dbg(p, drv, p->net, "%s failed\n", __func__); ++ return ret; ++} ++ ++/* Returns non-negative int on success, error on failure */ ++static int read_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd) ++{ ++ return __mii_op(pegasus, phy, indx, regd, PHY_READ); ++} ++ ++/* Returns zero on success, error on failure */ ++static int write_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd) ++{ ++ return __mii_op(pegasus, phy, indx, regd, PHY_WRITE); ++} ++ ++static int mdio_read(struct net_device *dev, int phy_id, int loc) ++{ ++ pegasus_t *pegasus = netdev_priv(dev); ++ u16 res; ++ ++ read_mii_word(pegasus, phy_id, loc, &res); ++ return (int)res; ++} ++ ++static void mdio_write(struct net_device *dev, int phy_id, int loc, int val) ++{ ++ pegasus_t *pegasus = netdev_priv(dev); ++ u16 data = val; ++ ++ write_mii_word(pegasus, phy_id, loc, &data); ++} ++ ++static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata) ++{ ++ int i; ++ __u8 tmp; ++ __le16 retdatai; ++ int ret; ++ ++ set_register(pegasus, EpromCtrl, 0); ++ set_register(pegasus, EpromOffset, index); ++ set_register(pegasus, EpromCtrl, EPROM_READ); ++ ++ for (i = 0; i < REG_TIMEOUT; i++) { ++ ret = get_registers(pegasus, EpromCtrl, 1, &tmp); ++ if (tmp & EPROM_DONE) ++ break; ++ if (ret == -ESHUTDOWN) ++ goto fail; ++ } ++ if (i >= REG_TIMEOUT) ++ goto fail; ++ ++ ret = get_registers(pegasus, EpromData, 2, &retdatai); ++ *retdata = le16_to_cpu(retdatai); ++ return ret; ++ ++fail: ++ netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__); ++ return -ETIMEDOUT; ++} ++ ++#ifdef PEGASUS_WRITE_EEPROM ++static inline void enable_eprom_write(pegasus_t *pegasus) ++{ ++ __u8 tmp; ++ ++ get_registers(pegasus, EthCtrl2, 1, &tmp); ++ set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE); ++} ++ ++static inline void disable_eprom_write(pegasus_t *pegasus) ++{ ++ __u8 tmp; ++ ++ get_registers(pegasus, EthCtrl2, 1, &tmp); ++ set_register(pegasus, EpromCtrl, 0); ++ set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE); ++} ++ ++static int write_eprom_word(pegasus_t *pegasus, __u8 index, __u16 data) ++{ ++ int i; ++ __u8 tmp, d[4] = { 0x3f, 0, 0, EPROM_WRITE }; ++ int ret; ++ __le16 le_data = cpu_to_le16(data); ++ ++ set_registers(pegasus, EpromOffset, 4, d); ++ enable_eprom_write(pegasus); ++ set_register(pegasus, EpromOffset, index); ++ set_registers(pegasus, EpromData, 2, &le_data); ++ set_register(pegasus, EpromCtrl, EPROM_WRITE); ++ ++ for (i = 0; i < REG_TIMEOUT; i++) { ++ ret = get_registers(pegasus, EpromCtrl, 1, &tmp); ++ if (ret == -ESHUTDOWN) ++ goto fail; ++ if (tmp & EPROM_DONE) ++ break; ++ } ++ disable_eprom_write(pegasus); ++ if (i >= REG_TIMEOUT) ++ goto fail; ++ ++ return ret; ++ ++fail: ++ netif_warn(pegasus, drv, pegasus->net, "%s failed\n", __func__); ++ return -ETIMEDOUT; ++} ++#endif /* PEGASUS_WRITE_EEPROM */ ++ ++static inline void get_node_id(pegasus_t *pegasus, __u8 *id) ++{ ++ int i; ++ __u16 w16; ++ ++ for (i = 0; i < 3; i++) { ++ read_eprom_word(pegasus, i, &w16); ++ ((__le16 *) id)[i] = cpu_to_le16(w16); ++ } ++} ++ ++static void set_ethernet_addr(pegasus_t *pegasus) ++{ ++ __u8 node_id[6]; ++ ++ if (pegasus->features & PEGASUS_II) { ++ get_registers(pegasus, 0x10, sizeof(node_id), node_id); ++ } else { ++ get_node_id(pegasus, node_id); ++ set_registers(pegasus, EthID, sizeof(node_id), node_id); ++ } ++ memcpy(pegasus->net->dev_addr, node_id, sizeof(node_id)); ++} ++ ++static inline int reset_mac(pegasus_t *pegasus) ++{ ++ __u8 data = 0x8; ++ int i; ++ ++ set_register(pegasus, EthCtrl1, data); ++ for (i = 0; i < REG_TIMEOUT; i++) { ++ get_registers(pegasus, EthCtrl1, 1, &data); ++ if (~data & 0x08) { ++ if (loopback) ++ break; ++ if (mii_mode && (pegasus->features & HAS_HOME_PNA)) ++ set_register(pegasus, Gpio1, 0x34); ++ else ++ set_register(pegasus, Gpio1, 0x26); ++ set_register(pegasus, Gpio0, pegasus->features); ++ set_register(pegasus, Gpio0, DEFAULT_GPIO_SET); ++ break; ++ } ++ } ++ if (i == REG_TIMEOUT) ++ return -ETIMEDOUT; ++ ++ if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || ++ usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { ++ set_register(pegasus, Gpio0, 0x24); ++ set_register(pegasus, Gpio0, 0x26); ++ } ++ if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) { ++ __u16 auxmode; ++ read_mii_word(pegasus, 3, 0x1b, &auxmode); ++ auxmode |= 4; ++ write_mii_word(pegasus, 3, 0x1b, &auxmode); ++ } ++ ++ return 0; ++} ++ ++static int enable_net_traffic(struct net_device *dev, struct usb_device *usb) ++{ ++ __u16 linkpart; ++ __u8 data[4]; ++ pegasus_t *pegasus = netdev_priv(dev); ++ int ret; ++ ++ read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart); ++ data[0] = 0xc9; ++ data[1] = 0; ++ if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL)) ++ data[1] |= 0x20; /* set full duplex */ ++ if (linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF)) ++ data[1] |= 0x10; /* set 100 Mbps */ ++ if (mii_mode) ++ data[1] = 0; ++ data[2] = loopback ? 0x09 : 0x01; ++ ++ memcpy(pegasus->eth_regs, data, sizeof(data)); ++ ret = set_registers(pegasus, EthCtrl0, 3, data); ++ ++ if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || ++ usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS2 || ++ usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { ++ u16 auxmode; ++ read_mii_word(pegasus, 0, 0x1b, &auxmode); ++ auxmode |= 4; ++ write_mii_word(pegasus, 0, 0x1b, &auxmode); ++ } ++ ++ return ret; ++} ++ ++static void read_bulk_callback(struct urb *urb) ++{ ++ pegasus_t *pegasus = urb->context; ++ struct net_device *net; ++ int rx_status, count = urb->actual_length; ++ int status = urb->status; ++ u8 *buf = urb->transfer_buffer; ++ __u16 pkt_len; ++ ++ if (!pegasus) ++ return; ++ ++ net = pegasus->net; ++ if (!netif_device_present(net) || !netif_running(net)) ++ return; ++ ++ switch (status) { ++ case 0: ++ break; ++ case -ETIME: ++ netif_dbg(pegasus, rx_err, net, "reset MAC\n"); ++ pegasus->flags &= ~PEGASUS_RX_BUSY; ++ break; ++ case -EPIPE: /* stall, or disconnect from TT */ ++ /* FIXME schedule work to clear the halt */ ++ netif_warn(pegasus, rx_err, net, "no rx stall recovery\n"); ++ return; ++ case -ENOENT: ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ netif_dbg(pegasus, ifdown, net, "rx unlink, %d\n", status); ++ return; ++ default: ++ netif_dbg(pegasus, rx_err, net, "RX status %d\n", status); ++ goto goon; ++ } ++ ++ if (!count || count < 4) ++ goto goon; ++ ++ rx_status = buf[count - 2]; ++ if (rx_status & 0x1e) { ++ netif_dbg(pegasus, rx_err, net, ++ "RX packet error %x\n", rx_status); ++ pegasus->stats.rx_errors++; ++ if (rx_status & 0x06) /* long or runt */ ++ pegasus->stats.rx_length_errors++; ++ if (rx_status & 0x08) ++ pegasus->stats.rx_crc_errors++; ++ if (rx_status & 0x10) /* extra bits */ ++ pegasus->stats.rx_frame_errors++; ++ goto goon; ++ } ++ if (pegasus->chip == 0x8513) { ++ pkt_len = le32_to_cpu(*(__le32 *)urb->transfer_buffer); ++ pkt_len &= 0x0fff; ++ pegasus->rx_skb->data += 2; ++ } else { ++ pkt_len = buf[count - 3] << 8; ++ pkt_len += buf[count - 4]; ++ pkt_len &= 0xfff; ++ pkt_len -= 8; ++ } ++ ++ /* ++ * If the packet is unreasonably long, quietly drop it rather than ++ * kernel panicing by calling skb_put. ++ */ ++ if (pkt_len > PEGASUS_MTU) ++ goto goon; ++ ++ /* ++ * at this point we are sure pegasus->rx_skb != NULL ++ * so we go ahead and pass up the packet. ++ */ ++ skb_put(pegasus->rx_skb, pkt_len); ++ pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net); ++ netif_rx(pegasus->rx_skb); ++ pegasus->stats.rx_packets++; ++ pegasus->stats.rx_bytes += pkt_len; ++ ++ if (pegasus->flags & PEGASUS_UNPLUG) ++ return; ++ ++ pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, PEGASUS_MTU, ++ GFP_ATOMIC); ++ ++ if (pegasus->rx_skb == NULL) ++ goto tl_sched; ++goon: ++ usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, ++ usb_rcvbulkpipe(pegasus->usb, 1), ++ pegasus->rx_skb->data, PEGASUS_MTU + 8, ++ read_bulk_callback, pegasus); ++ rx_status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); ++ if (rx_status == -ENODEV) ++ netif_device_detach(pegasus->net); ++ else if (rx_status) { ++ pegasus->flags |= PEGASUS_RX_URB_FAIL; ++ goto tl_sched; ++ } else { ++ pegasus->flags &= ~PEGASUS_RX_URB_FAIL; ++ } ++ ++ return; ++ ++tl_sched: ++ tasklet_schedule(&pegasus->rx_tl); ++} ++ ++static void rx_fixup(unsigned long data) ++{ ++ pegasus_t *pegasus; ++ int status; ++ ++ pegasus = (pegasus_t *) data; ++ if (pegasus->flags & PEGASUS_UNPLUG) ++ return; ++ ++ if (pegasus->flags & PEGASUS_RX_URB_FAIL) ++ if (pegasus->rx_skb) ++ goto try_again; ++ if (pegasus->rx_skb == NULL) ++ pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, ++ PEGASUS_MTU, ++ GFP_ATOMIC); ++ if (pegasus->rx_skb == NULL) { ++ netif_warn(pegasus, rx_err, pegasus->net, "low on memory\n"); ++ tasklet_schedule(&pegasus->rx_tl); ++ return; ++ } ++ usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, ++ usb_rcvbulkpipe(pegasus->usb, 1), ++ pegasus->rx_skb->data, PEGASUS_MTU + 8, ++ read_bulk_callback, pegasus); ++try_again: ++ status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); ++ if (status == -ENODEV) ++ netif_device_detach(pegasus->net); ++ else if (status) { ++ pegasus->flags |= PEGASUS_RX_URB_FAIL; ++ tasklet_schedule(&pegasus->rx_tl); ++ } else { ++ pegasus->flags &= ~PEGASUS_RX_URB_FAIL; ++ } ++} ++ ++static void write_bulk_callback(struct urb *urb) ++{ ++ pegasus_t *pegasus = urb->context; ++ struct net_device *net; ++ int status = urb->status; ++ ++ if (!pegasus) ++ return; ++ ++ net = pegasus->net; ++ ++ if (!netif_device_present(net) || !netif_running(net)) ++ return; ++ ++ switch (status) { ++ case -EPIPE: ++ /* FIXME schedule_work() to clear the tx halt */ ++ netif_stop_queue(net); ++ netif_warn(pegasus, tx_err, net, "no tx stall recovery\n"); ++ return; ++ case -ENOENT: ++ case -ECONNRESET: ++ case -ESHUTDOWN: ++ netif_dbg(pegasus, ifdown, net, "tx unlink, %d\n", status); ++ return; ++ default: ++ netif_info(pegasus, tx_err, net, "TX status %d\n", status); ++ /* FALL THROUGH */ ++ case 0: ++ break; ++ } ++ ++ net->trans_start = jiffies; /* prevent tx timeout */ ++ netif_wake_queue(net); ++} ++ ++static void intr_callback(struct urb *urb) ++{ ++ pegasus_t *pegasus = urb->context; ++ struct net_device *net; ++ int res, status = urb->status; ++ ++ if (!pegasus) ++ return; ++ net = pegasus->net; ++ ++ switch (status) { ++ case 0: ++ break; ++ case -ECONNRESET: /* unlink */ ++ case -ENOENT: ++ case -ESHUTDOWN: ++ return; ++ default: ++ /* some Pegasus-I products report LOTS of data ++ * toggle errors... avoid log spamming ++ */ ++ netif_dbg(pegasus, timer, net, "intr status %d\n", status); ++ } ++ ++ if (urb->actual_length >= 6) { ++ u8 *d = urb->transfer_buffer; ++ ++ /* byte 0 == tx_status1, reg 2B */ ++ if (d[0] & (TX_UNDERRUN|EXCESSIVE_COL ++ |LATE_COL|JABBER_TIMEOUT)) { ++ pegasus->stats.tx_errors++; ++ if (d[0] & TX_UNDERRUN) ++ pegasus->stats.tx_fifo_errors++; ++ if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT)) ++ pegasus->stats.tx_aborted_errors++; ++ if (d[0] & LATE_COL) ++ pegasus->stats.tx_window_errors++; ++ } ++ ++ /* d[5].LINK_STATUS lies on some adapters. ++ * d[0].NO_CARRIER kicks in only with failed TX. ++ * ... so monitoring with MII may be safest. ++ */ ++ ++ /* bytes 3-4 == rx_lostpkt, reg 2E/2F */ ++ pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4]; ++ } ++ ++ res = usb_submit_urb(urb, GFP_ATOMIC); ++ if (res == -ENODEV) ++ netif_device_detach(pegasus->net); ++ if (res) ++ netif_err(pegasus, timer, net, ++ "can't resubmit interrupt urb, %d\n", res); ++} ++ ++static void pegasus_tx_timeout(struct net_device *net) ++{ ++ pegasus_t *pegasus = netdev_priv(net); ++ netif_warn(pegasus, timer, net, "tx timeout\n"); ++ usb_unlink_urb(pegasus->tx_urb); ++ pegasus->stats.tx_errors++; ++} ++ ++static netdev_tx_t pegasus_start_xmit(struct sk_buff *skb, ++ struct net_device *net) ++{ ++ pegasus_t *pegasus = netdev_priv(net); ++ int count = ((skb->len + 2) & 0x3f) ? skb->len + 2 : skb->len + 3; ++ int res; ++ __u16 l16 = skb->len; ++ ++ netif_stop_queue(net); ++ ++ ((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16); ++ skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len); ++ usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb, ++ usb_sndbulkpipe(pegasus->usb, 2), ++ pegasus->tx_buff, count, ++ write_bulk_callback, pegasus); ++ if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) { ++ netif_warn(pegasus, tx_err, net, "fail tx, %d\n", res); ++ switch (res) { ++ case -EPIPE: /* stall, or disconnect from TT */ ++ /* cleanup should already have been scheduled */ ++ break; ++ case -ENODEV: /* disconnect() upcoming */ ++ case -EPERM: ++ netif_device_detach(pegasus->net); ++ break; ++ default: ++ pegasus->stats.tx_errors++; ++ netif_start_queue(net); ++ } ++ } else { ++ pegasus->stats.tx_packets++; ++ pegasus->stats.tx_bytes += skb->len; ++ } ++ dev_kfree_skb(skb); ++ ++ return NETDEV_TX_OK; ++} ++ ++static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) ++{ ++ return &((pegasus_t *) netdev_priv(dev))->stats; ++} ++ ++static inline void disable_net_traffic(pegasus_t *pegasus) ++{ ++ __le16 tmp = cpu_to_le16(0); ++ ++ set_registers(pegasus, EthCtrl0, sizeof(tmp), &tmp); ++} ++ ++static inline void get_interrupt_interval(pegasus_t *pegasus) ++{ ++ u16 data; ++ u8 interval; ++ ++ read_eprom_word(pegasus, 4, &data); ++ interval = data >> 8; ++ if (pegasus->usb->speed != USB_SPEED_HIGH) { ++ if (interval < 0x80) { ++ netif_info(pegasus, timer, pegasus->net, ++ "intr interval changed from %ums to %ums\n", ++ interval, 0x80); ++ interval = 0x80; ++ data = (data & 0x00FF) | ((u16)interval << 8); ++#ifdef PEGASUS_WRITE_EEPROM ++ write_eprom_word(pegasus, 4, data); ++#endif ++ } ++ } ++ pegasus->intr_interval = interval; ++} ++ ++static void set_carrier(struct net_device *net) ++{ ++ pegasus_t *pegasus = netdev_priv(net); ++ u16 tmp; ++ ++ if (read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp)) ++ return; ++ ++ if (tmp & BMSR_LSTATUS) ++ netif_carrier_on(net); ++ else ++ netif_carrier_off(net); ++} ++ ++static void free_all_urbs(pegasus_t *pegasus) ++{ ++ usb_free_urb(pegasus->intr_urb); ++ usb_free_urb(pegasus->tx_urb); ++ usb_free_urb(pegasus->rx_urb); ++} ++ ++static void unlink_all_urbs(pegasus_t *pegasus) ++{ ++ usb_kill_urb(pegasus->intr_urb); ++ usb_kill_urb(pegasus->tx_urb); ++ usb_kill_urb(pegasus->rx_urb); ++} ++ ++static int alloc_urbs(pegasus_t *pegasus) ++{ ++ int res = -ENOMEM; ++ ++ pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!pegasus->rx_urb) { ++ return res; ++ } ++ pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!pegasus->tx_urb) { ++ usb_free_urb(pegasus->rx_urb); ++ return res; ++ } ++ pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!pegasus->intr_urb) { ++ usb_free_urb(pegasus->tx_urb); ++ usb_free_urb(pegasus->rx_urb); ++ return res; ++ } ++ ++ return 0; ++} ++ ++static int pegasus_open(struct net_device *net) ++{ ++ pegasus_t *pegasus = netdev_priv(net); ++ int res=-ENOMEM; ++ ++ if (pegasus->rx_skb == NULL) ++ pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, ++ PEGASUS_MTU, ++ GFP_KERNEL); ++ if (!pegasus->rx_skb) ++ goto exit; ++ ++ res = set_registers(pegasus, EthID, 6, net->dev_addr); ++ ++ usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, ++ usb_rcvbulkpipe(pegasus->usb, 1), ++ pegasus->rx_skb->data, PEGASUS_MTU + 8, ++ read_bulk_callback, pegasus); ++ if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) { ++ if (res == -ENODEV) ++ netif_device_detach(pegasus->net); ++ netif_dbg(pegasus, ifup, net, "failed rx_urb, %d\n", res); ++ goto exit; ++ } ++ ++ usb_fill_int_urb(pegasus->intr_urb, pegasus->usb, ++ usb_rcvintpipe(pegasus->usb, 3), ++ pegasus->intr_buff, sizeof(pegasus->intr_buff), ++ intr_callback, pegasus, pegasus->intr_interval); ++ if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) { ++ if (res == -ENODEV) ++ netif_device_detach(pegasus->net); ++ netif_dbg(pegasus, ifup, net, "failed intr_urb, %d\n", res); ++ usb_kill_urb(pegasus->rx_urb); ++ goto exit; ++ } ++ res = enable_net_traffic(net, pegasus->usb); ++ if (res < 0) { ++ netif_dbg(pegasus, ifup, net, ++ "can't enable_net_traffic() - %d\n", res); ++ res = -EIO; ++ usb_kill_urb(pegasus->rx_urb); ++ usb_kill_urb(pegasus->intr_urb); ++ goto exit; ++ } ++ set_carrier(net); ++ netif_start_queue(net); ++ netif_dbg(pegasus, ifup, net, "open\n"); ++ res = 0; ++exit: ++ return res; ++} ++ ++static int pegasus_close(struct net_device *net) ++{ ++ pegasus_t *pegasus = netdev_priv(net); ++ ++ netif_stop_queue(net); ++ if (!(pegasus->flags & PEGASUS_UNPLUG)) ++ disable_net_traffic(pegasus); ++ tasklet_kill(&pegasus->rx_tl); ++ unlink_all_urbs(pegasus); ++ ++ return 0; ++} ++ ++static void pegasus_get_drvinfo(struct net_device *dev, ++ struct ethtool_drvinfo *info) ++{ ++ pegasus_t *pegasus = netdev_priv(dev); ++ ++ strlcpy(info->driver, driver_name, sizeof(info->driver)); ++ strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); ++ usb_make_path(pegasus->usb, info->bus_info, sizeof(info->bus_info)); ++} ++ ++/* also handles three patterns of some kind in hardware */ ++#define WOL_SUPPORTED (WAKE_MAGIC|WAKE_PHY) ++ ++static void ++pegasus_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) ++{ ++ pegasus_t *pegasus = netdev_priv(dev); ++ ++ wol->supported = WAKE_MAGIC | WAKE_PHY; ++ wol->wolopts = pegasus->wolopts; ++} ++ ++static int ++pegasus_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) ++{ ++ pegasus_t *pegasus = netdev_priv(dev); ++ u8 reg78 = 0x04; ++ int ret; ++ ++ if (wol->wolopts & ~WOL_SUPPORTED) ++ return -EINVAL; ++ ++ if (wol->wolopts & WAKE_MAGIC) ++ reg78 |= 0x80; ++ if (wol->wolopts & WAKE_PHY) ++ reg78 |= 0x40; ++ /* FIXME this 0x10 bit still needs to get set in the chip... */ ++ if (wol->wolopts) ++ pegasus->eth_regs[0] |= 0x10; ++ else ++ pegasus->eth_regs[0] &= ~0x10; ++ pegasus->wolopts = wol->wolopts; ++ ++ ret = set_register(pegasus, WakeupControl, reg78); ++ if (!ret) ++ ret = device_set_wakeup_enable(&pegasus->usb->dev, ++ wol->wolopts); ++ return ret; ++} ++ ++static inline void pegasus_reset_wol(struct net_device *dev) ++{ ++ struct ethtool_wolinfo wol; ++ ++ memset(&wol, 0, sizeof wol); ++ (void) pegasus_set_wol(dev, &wol); ++} ++ ++static int ++pegasus_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) ++{ ++ pegasus_t *pegasus; ++ ++ pegasus = netdev_priv(dev); ++ mii_ethtool_gset(&pegasus->mii, ecmd); ++ return 0; ++} ++ ++static int ++pegasus_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) ++{ ++ pegasus_t *pegasus = netdev_priv(dev); ++ return mii_ethtool_sset(&pegasus->mii, ecmd); ++} ++ ++static int pegasus_nway_reset(struct net_device *dev) ++{ ++ pegasus_t *pegasus = netdev_priv(dev); ++ return mii_nway_restart(&pegasus->mii); ++} ++ ++static u32 pegasus_get_link(struct net_device *dev) ++{ ++ pegasus_t *pegasus = netdev_priv(dev); ++ return mii_link_ok(&pegasus->mii); ++} ++ ++static u32 pegasus_get_msglevel(struct net_device *dev) ++{ ++ pegasus_t *pegasus = netdev_priv(dev); ++ return pegasus->msg_enable; ++} ++ ++static void pegasus_set_msglevel(struct net_device *dev, u32 v) ++{ ++ pegasus_t *pegasus = netdev_priv(dev); ++ pegasus->msg_enable = v; ++} ++ ++static const struct ethtool_ops ops = { ++ .get_drvinfo = pegasus_get_drvinfo, ++ .get_settings = pegasus_get_settings, ++ .set_settings = pegasus_set_settings, ++ .nway_reset = pegasus_nway_reset, ++ .get_link = pegasus_get_link, ++ .get_msglevel = pegasus_get_msglevel, ++ .set_msglevel = pegasus_set_msglevel, ++ .get_wol = pegasus_get_wol, ++ .set_wol = pegasus_set_wol, ++}; ++ ++static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) ++{ ++ __u16 *data = (__u16 *) &rq->ifr_ifru; ++ pegasus_t *pegasus = netdev_priv(net); ++ int res; ++ ++ switch (cmd) { ++ case SIOCDEVPRIVATE: ++ data[0] = pegasus->phy; ++ case SIOCDEVPRIVATE + 1: ++ read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]); ++ res = 0; ++ break; ++ case SIOCDEVPRIVATE + 2: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, &data[2]); ++ res = 0; ++ break; ++ default: ++ res = -EOPNOTSUPP; ++ } ++ return res; ++} ++ ++static void pegasus_set_multicast(struct net_device *net) ++{ ++ pegasus_t *pegasus = netdev_priv(net); ++ ++ if (net->flags & IFF_PROMISC) { ++ pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS; ++ netif_info(pegasus, link, net, "Promiscuous mode enabled\n"); ++ } else if (!netdev_mc_empty(net) || (net->flags & IFF_ALLMULTI)) { ++ pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST; ++ pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; ++ netif_dbg(pegasus, link, net, "set allmulti\n"); ++ } else { ++ pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST; ++ pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; ++ } ++ update_eth_regs_async(pegasus); ++} ++ ++static __u8 mii_phy_probe(pegasus_t *pegasus) ++{ ++ int i; ++ __u16 tmp; ++ ++ for (i = 0; i < 32; i++) { ++ read_mii_word(pegasus, i, MII_BMSR, &tmp); ++ if (tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0) ++ continue; ++ else ++ return i; ++ } ++ ++ return 0xff; ++} ++ ++static inline void setup_pegasus_II(pegasus_t *pegasus) ++{ ++ __u8 data = 0xa5; ++ ++ set_register(pegasus, Reg1d, 0); ++ set_register(pegasus, Reg7b, 1); ++ mdelay(100); ++ if ((pegasus->features & HAS_HOME_PNA) && mii_mode) ++ set_register(pegasus, Reg7b, 0); ++ else ++ set_register(pegasus, Reg7b, 2); ++ ++ set_register(pegasus, 0x83, data); ++ get_registers(pegasus, 0x83, 1, &data); ++ ++ if (data == 0xa5) ++ pegasus->chip = 0x8513; ++ else ++ pegasus->chip = 0; ++ ++ set_register(pegasus, 0x80, 0xc0); ++ set_register(pegasus, 0x83, 0xff); ++ set_register(pegasus, 0x84, 0x01); ++ ++ if (pegasus->features & HAS_HOME_PNA && mii_mode) ++ set_register(pegasus, Reg81, 6); ++ else ++ set_register(pegasus, Reg81, 2); ++} ++ ++ ++static int pegasus_count; ++static struct workqueue_struct *pegasus_workqueue; ++#define CARRIER_CHECK_DELAY (2 * HZ) ++ ++static void check_carrier(struct work_struct *work) ++{ ++ pegasus_t *pegasus = container_of(work, pegasus_t, carrier_check.work); ++ set_carrier(pegasus->net); ++ if (!(pegasus->flags & PEGASUS_UNPLUG)) { ++ queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check, ++ CARRIER_CHECK_DELAY); ++ } ++} ++ ++static int pegasus_blacklisted(struct usb_device *udev) ++{ ++ struct usb_device_descriptor *udd = &udev->descriptor; ++ ++ /* Special quirk to keep the driver from handling the Belkin Bluetooth ++ * dongle which happens to have the same ID. ++ */ ++ if ((udd->idVendor == cpu_to_le16(VENDOR_BELKIN)) && ++ (udd->idProduct == cpu_to_le16(0x0121)) && ++ (udd->bDeviceClass == USB_CLASS_WIRELESS_CONTROLLER) && ++ (udd->bDeviceProtocol == 1)) ++ return 1; ++ ++ return 0; ++} ++ ++/* we rely on probe() and remove() being serialized so we ++ * don't need extra locking on pegasus_count. ++ */ ++static void pegasus_dec_workqueue(void) ++{ ++ pegasus_count--; ++ if (pegasus_count == 0) { ++ destroy_workqueue(pegasus_workqueue); ++ pegasus_workqueue = NULL; ++ } ++} ++ ++static int pegasus_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ struct usb_device *dev = interface_to_usbdev(intf); ++ struct net_device *net; ++ pegasus_t *pegasus; ++ int dev_index = id - pegasus_ids; ++ int res = -ENOMEM; ++ ++ if (pegasus_blacklisted(dev)) ++ return -ENODEV; ++ ++ if (pegasus_count == 0) { ++ pegasus_workqueue = create_singlethread_workqueue("pegasus"); ++ if (!pegasus_workqueue) ++ return -ENOMEM; ++ } ++ pegasus_count++; ++ ++ net = alloc_etherdev(sizeof(struct pegasus)); ++ if (!net) ++ goto out; ++ ++ pegasus = netdev_priv(net); ++ pegasus->dev_index = dev_index; ++ ++ res = alloc_urbs(pegasus); ++ if (res < 0) { ++ dev_err(&intf->dev, "can't allocate %s\n", "urbs"); ++ goto out1; ++ } ++ ++ tasklet_init(&pegasus->rx_tl, rx_fixup, (unsigned long) pegasus); ++ ++ INIT_DELAYED_WORK(&pegasus->carrier_check, check_carrier); ++ ++ pegasus->intf = intf; ++ pegasus->usb = dev; ++ pegasus->net = net; ++ ++ ++ net->watchdog_timeo = PEGASUS_TX_TIMEOUT; ++ net->netdev_ops = &pegasus_netdev_ops; ++ net->ethtool_ops = &ops; ++ pegasus->mii.dev = net; ++ pegasus->mii.mdio_read = mdio_read; ++ pegasus->mii.mdio_write = mdio_write; ++ pegasus->mii.phy_id_mask = 0x1f; ++ pegasus->mii.reg_num_mask = 0x1f; ++ pegasus->msg_enable = netif_msg_init(msg_level, NETIF_MSG_DRV ++ | NETIF_MSG_PROBE | NETIF_MSG_LINK); ++ ++ pegasus->features = usb_dev_id[dev_index].private; ++ get_interrupt_interval(pegasus); ++ if (reset_mac(pegasus)) { ++ dev_err(&intf->dev, "can't reset MAC\n"); ++ res = -EIO; ++ goto out2; ++ } ++ set_ethernet_addr(pegasus); ++ if (pegasus->features & PEGASUS_II) { ++ dev_info(&intf->dev, "setup Pegasus II specific registers\n"); ++ setup_pegasus_II(pegasus); ++ } ++ pegasus->phy = mii_phy_probe(pegasus); ++ if (pegasus->phy == 0xff) { ++ dev_warn(&intf->dev, "can't locate MII phy, using default\n"); ++ pegasus->phy = 1; ++ } ++ pegasus->mii.phy_id = pegasus->phy; ++ usb_set_intfdata(intf, pegasus); ++ SET_NETDEV_DEV(net, &intf->dev); ++ pegasus_reset_wol(net); ++ res = register_netdev(net); ++ if (res) ++ goto out3; ++ queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check, ++ CARRIER_CHECK_DELAY); ++ dev_info(&intf->dev, "%s, %s, %pM\n", net->name, ++ usb_dev_id[dev_index].name, net->dev_addr); ++ return 0; ++ ++out3: ++ usb_set_intfdata(intf, NULL); ++out2: ++ free_all_urbs(pegasus); ++out1: ++ free_netdev(net); ++out: ++ pegasus_dec_workqueue(); ++ return res; ++} ++ ++static void pegasus_disconnect(struct usb_interface *intf) ++{ ++ struct pegasus *pegasus = usb_get_intfdata(intf); ++ ++ usb_set_intfdata(intf, NULL); ++ if (!pegasus) { ++ dev_dbg(&intf->dev, "unregistering non-bound device?\n"); ++ return; ++ } ++ ++ pegasus->flags |= PEGASUS_UNPLUG; ++ cancel_delayed_work(&pegasus->carrier_check); ++ unregister_netdev(pegasus->net); ++ unlink_all_urbs(pegasus); ++ free_all_urbs(pegasus); ++ if (pegasus->rx_skb != NULL) { ++ dev_kfree_skb(pegasus->rx_skb); ++ pegasus->rx_skb = NULL; ++ } ++ free_netdev(pegasus->net); ++ pegasus_dec_workqueue(); ++} ++ ++static int pegasus_suspend(struct usb_interface *intf, pm_message_t message) ++{ ++ struct pegasus *pegasus = usb_get_intfdata(intf); ++ ++ netif_device_detach(pegasus->net); ++ cancel_delayed_work(&pegasus->carrier_check); ++ if (netif_running(pegasus->net)) { ++ usb_kill_urb(pegasus->rx_urb); ++ usb_kill_urb(pegasus->intr_urb); ++ } ++ return 0; ++} ++ ++static int pegasus_resume(struct usb_interface *intf) ++{ ++ struct pegasus *pegasus = usb_get_intfdata(intf); ++ ++ netif_device_attach(pegasus->net); ++ if (netif_running(pegasus->net)) { ++ pegasus->rx_urb->status = 0; ++ pegasus->rx_urb->actual_length = 0; ++ read_bulk_callback(pegasus->rx_urb); ++ ++ pegasus->intr_urb->status = 0; ++ pegasus->intr_urb->actual_length = 0; ++ intr_callback(pegasus->intr_urb); ++ } ++ queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check, ++ CARRIER_CHECK_DELAY); ++ return 0; ++} ++ ++static const struct net_device_ops pegasus_netdev_ops = { ++ .ndo_open = pegasus_open, ++ .ndo_stop = pegasus_close, ++ .ndo_do_ioctl = pegasus_ioctl, ++ .ndo_start_xmit = pegasus_start_xmit, ++ .ndo_set_rx_mode = pegasus_set_multicast, ++ .ndo_get_stats = pegasus_netdev_stats, ++ .ndo_tx_timeout = pegasus_tx_timeout, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++}; ++ ++static struct usb_driver pegasus_driver = { ++ .name = driver_name, ++ .probe = pegasus_probe, ++ .disconnect = pegasus_disconnect, ++ .id_table = pegasus_ids, ++ .suspend = pegasus_suspend, ++ .resume = pegasus_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++static void __init parse_id(char *id) ++{ ++ unsigned int vendor_id = 0, device_id = 0, flags = 0, i = 0; ++ char *token, *name = NULL; ++ ++ if ((token = strsep(&id, ":")) != NULL) ++ name = token; ++ /* name now points to a null terminated string*/ ++ if ((token = strsep(&id, ":")) != NULL) ++ vendor_id = simple_strtoul(token, NULL, 16); ++ if ((token = strsep(&id, ":")) != NULL) ++ device_id = simple_strtoul(token, NULL, 16); ++ flags = simple_strtoul(id, NULL, 16); ++ pr_info("%s: new device %s, vendor ID 0x%04x, device ID 0x%04x, flags: 0x%x\n", ++ driver_name, name, vendor_id, device_id, flags); ++ ++ if (vendor_id > 0x10000 || vendor_id == 0) ++ return; ++ if (device_id > 0x10000 || device_id == 0) ++ return; ++ ++ for (i = 0; usb_dev_id[i].name; i++); ++ usb_dev_id[i].name = name; ++ usb_dev_id[i].vendor = vendor_id; ++ usb_dev_id[i].device = device_id; ++ usb_dev_id[i].private = flags; ++ pegasus_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE; ++ pegasus_ids[i].idVendor = vendor_id; ++ pegasus_ids[i].idProduct = device_id; ++} ++ ++static int __init pegasus_init(void) ++{ ++ pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION); ++ if (devid) ++ parse_id(devid); ++ return usb_register(&pegasus_driver); ++} ++ ++static void __exit pegasus_exit(void) ++{ ++ usb_deregister(&pegasus_driver); ++} ++ ++module_init(pegasus_init); ++module_exit(pegasus_exit); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/pegasus.h backports-3.18.1-1/drivers/net/usb/pegasus.h +--- backports-3.18.1-1.org/drivers/net/usb/pegasus.h 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/pegasus.h 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,308 @@ ++/* ++ * Copyright (c) 1999-2013 Petko Manolov (petkan@nucleusys.com) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++ ++#ifndef PEGASUS_DEV ++ ++#define PEGASUS_II 0x80000000 ++#define HAS_HOME_PNA 0x40000000 ++ ++#define PEGASUS_MTU 1536 ++ ++#define EPROM_WRITE 0x01 ++#define EPROM_READ 0x02 ++#define EPROM_DONE 0x04 ++#define EPROM_WR_ENABLE 0x10 ++#define EPROM_LOAD 0x20 ++ ++#define PHY_DONE 0x80 ++#define PHY_READ 0x40 ++#define PHY_WRITE 0x20 ++#define DEFAULT_GPIO_RESET 0x24 ++#define DEFAULT_GPIO_SET 0x26 ++ ++#define PEGASUS_PRESENT 0x00000001 ++#define PEGASUS_TX_BUSY 0x00000004 ++#define PEGASUS_RX_BUSY 0x00000008 ++#define CTRL_URB_RUNNING 0x00000010 ++#define CTRL_URB_SLEEP 0x00000020 ++#define PEGASUS_UNPLUG 0x00000040 ++#define PEGASUS_RX_URB_FAIL 0x00000080 ++ ++#define RX_MULTICAST 2 ++#define RX_PROMISCUOUS 4 ++ ++#define REG_TIMEOUT (HZ) ++#define PEGASUS_TX_TIMEOUT (HZ*10) ++ ++#define TX_UNDERRUN 0x80 ++#define EXCESSIVE_COL 0x40 ++#define LATE_COL 0x20 ++#define NO_CARRIER 0x10 ++#define LOSS_CARRIER 0x08 ++#define JABBER_TIMEOUT 0x04 ++ ++#define LINK_STATUS 0x01 ++ ++#define PEGASUS_REQT_READ 0xc0 ++#define PEGASUS_REQT_WRITE 0x40 ++#define PEGASUS_REQ_GET_REGS 0xf0 ++#define PEGASUS_REQ_SET_REGS 0xf1 ++#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS ++ ++enum pegasus_registers { ++ EthCtrl0 = 0, ++ EthCtrl1 = 1, ++ EthCtrl2 = 2, ++ EthID = 0x10, ++ Reg1d = 0x1d, ++ EpromOffset = 0x20, ++ EpromData = 0x21, /* 0x21 low, 0x22 high byte */ ++ EpromCtrl = 0x23, ++ PhyAddr = 0x25, ++ PhyData = 0x26, /* 0x26 low, 0x27 high byte */ ++ PhyCtrl = 0x28, ++ UsbStst = 0x2a, ++ EthTxStat0 = 0x2b, ++ EthTxStat1 = 0x2c, ++ EthRxStat = 0x2d, ++ WakeupControl = 0x78, ++ Reg7b = 0x7b, ++ Gpio0 = 0x7e, ++ Gpio1 = 0x7f, ++ Reg81 = 0x81, ++}; ++ ++ ++typedef struct pegasus { ++ struct usb_device *usb; ++ struct usb_interface *intf; ++ struct net_device *net; ++ struct net_device_stats stats; ++ struct mii_if_info mii; ++ unsigned flags; ++ unsigned features; ++ u32 msg_enable; ++ u32 wolopts; ++ int dev_index; ++ int intr_interval; ++ struct tasklet_struct rx_tl; ++ struct delayed_work carrier_check; ++ struct urb *rx_urb, *tx_urb, *intr_urb; ++ struct sk_buff *rx_skb; ++ int chip; ++ unsigned char intr_buff[8]; ++ __u8 tx_buff[PEGASUS_MTU]; ++ __u8 eth_regs[4]; ++ __u8 phy; ++ __u8 gpio_res; ++} pegasus_t; ++ ++ ++struct usb_eth_dev { ++ char *name; ++ __u16 vendor; ++ __u16 device; ++ __u32 private; /* LSB is gpio reset value */ ++}; ++ ++#define VENDOR_3COM 0x0506 ++#define VENDOR_ABOCOM 0x07b8 ++#define VENDOR_ACCTON 0x083a ++#define VENDOR_ADMTEK 0x07a6 ++#define VENDOR_AEILAB 0x3334 ++#define VENDOR_ALLIEDTEL 0x07c9 ++#define VENDOR_ATEN 0x0557 ++#define VENDOR_BELKIN 0x050d ++#define VENDOR_BILLIONTON 0x08dd ++#define VENDOR_COMPAQ 0x049f ++#define VENDOR_COREGA 0x07aa ++#define VENDOR_DLINK 0x2001 ++#define VENDOR_ELCON 0x0db7 ++#define VENDOR_ELECOM 0x056e ++#define VENDOR_ELSA 0x05cc ++#define VENDOR_GIGABYTE 0x1044 ++#define VENDOR_HAWKING 0x0e66 ++#define VENDOR_HP 0x03f0 ++#define VENDOR_IODATA 0x04bb ++#define VENDOR_KINGSTON 0x0951 ++#define VENDOR_LANEED 0x056e ++#define VENDOR_LINKSYS 0x066b ++#define VENDOR_LINKSYS2 0x077b ++#define VENDOR_MELCO 0x0411 ++#define VENDOR_MICROSOFT 0x045e ++#define VENDOR_MOBILITY 0x1342 ++#define VENDOR_NETGEAR 0x0846 ++#define VENDOR_OCT 0x0b39 ++#define VENDOR_SMARTBRIDGES 0x08d1 ++#define VENDOR_SMC 0x0707 ++#define VENDOR_SOHOWARE 0x15e8 ++#define VENDOR_SIEMENS 0x067c ++ ++ ++#else /* PEGASUS_DEV */ ++ ++PEGASUS_DEV("3Com USB Ethernet 3C460B", VENDOR_3COM, 0x4601, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("ATEN USB Ethernet UC-110T", VENDOR_ATEN, 0x2007, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("USB HPNA/Ethernet", VENDOR_ABOCOM, 0x110c, ++ DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA) ++PEGASUS_DEV("USB HPNA/Ethernet", VENDOR_ABOCOM, 0x4104, ++ DEFAULT_GPIO_RESET | HAS_HOME_PNA) ++PEGASUS_DEV("USB HPNA/Ethernet", VENDOR_ABOCOM, 0x4004, ++ DEFAULT_GPIO_RESET | HAS_HOME_PNA) ++PEGASUS_DEV("USB HPNA/Ethernet", VENDOR_ABOCOM, 0x4007, ++ DEFAULT_GPIO_RESET | HAS_HOME_PNA) ++PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x4102, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x4002, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x400b, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x400c, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0xabc1, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x200c, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Accton USB 10/100 Ethernet Adapter", VENDOR_ACCTON, 0x1046, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("SpeedStream USB 10/100 Ethernet", VENDOR_ACCTON, 0x5046, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Philips USB 10/100 Ethernet", VENDOR_ACCTON, 0xb004, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("ADMtek ADM8511 \"Pegasus II\" USB Ethernet", ++ VENDOR_ADMTEK, 0x8511, ++ DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA) ++PEGASUS_DEV("ADMtek ADM8513 \"Pegasus II\" USB Ethernet", ++ VENDOR_ADMTEK, 0x8513, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("ADMtek ADM8515 \"Pegasus II\" USB-2.0 Ethernet", ++ VENDOR_ADMTEK, 0x8515, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("ADMtek AN986 \"Pegasus\" USB Ethernet (evaluation board)", ++ VENDOR_ADMTEK, 0x0986, ++ DEFAULT_GPIO_RESET | HAS_HOME_PNA) ++PEGASUS_DEV("AN986A USB MAC", VENDOR_ADMTEK, 1986, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("AEI USB Fast Ethernet Adapter", VENDOR_AEILAB, 0x1701, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Allied Telesyn Int. AT-USB100", VENDOR_ALLIEDTEL, 0xb100, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++/* ++ * Distinguish between this Belkin adaptor and the Belkin bluetooth adaptors ++ * with the same product IDs by checking the device class too. ++ */ ++PEGASUS_DEV_CLASS("Belkin F5D5050 USB Ethernet", VENDOR_BELKIN, 0x0121, 0x00, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Belkin F5U122 10/100 USB Ethernet", VENDOR_BELKIN, 0x0122, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Billionton USB-100", VENDOR_BILLIONTON, 0x0986, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("Billionton USBLP-100", VENDOR_BILLIONTON, 0x0987, ++ DEFAULT_GPIO_RESET | HAS_HOME_PNA) ++PEGASUS_DEV("iPAQ Networking 10/100 USB", VENDOR_COMPAQ, 0x8511, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Billionton USBEL-100", VENDOR_BILLIONTON, 0x0988, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("Billionton USBE-100", VENDOR_BILLIONTON, 0x8511, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Corega FEther USB-TX", VENDOR_COREGA, 0x0004, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("Corega FEther USB-TXS", VENDOR_COREGA, 0x000d, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("D-Link DSB-650TX", VENDOR_DLINK, 0x4001, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("D-Link DSB-650TX", VENDOR_DLINK, 0x4002, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("D-Link DSB-650TX", VENDOR_DLINK, 0x4102, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("D-Link DSB-650TX", VENDOR_DLINK, 0x400b, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("D-Link DSB-650TX", VENDOR_DLINK, 0x200c, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("D-Link DSB-650TX(PNA)", VENDOR_DLINK, 0x4003, ++ DEFAULT_GPIO_RESET | HAS_HOME_PNA) ++PEGASUS_DEV("D-Link DSB-650", VENDOR_DLINK, 0xabc1, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("GOLDPFEIL USB Adapter", VENDOR_ELCON, 0x0002, ++ DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA) ++PEGASUS_DEV("ELECOM USB Ethernet LD-USB20", VENDOR_ELECOM, 0x4010, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("EasiDock Ethernet", VENDOR_MOBILITY, 0x0304, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("GIGABYTE GN-BR402W Wireless Router", VENDOR_GIGABYTE, 0x8002, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("Hawking UF100 10/100 Ethernet", VENDOR_HAWKING, 0x400c, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("HP hn210c Ethernet USB", VENDOR_HP, 0x811c, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("IO DATA USB ET/TX", VENDOR_IODATA, 0x0904, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("IO DATA USB ET/TX-S", VENDOR_IODATA, 0x0913, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("IO DATA USB ETX-US2", VENDOR_IODATA, 0x093a, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Kingston KNU101TX Ethernet", VENDOR_KINGSTON, 0x000a, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("LANEED USB Ethernet LD-USBL/TX", VENDOR_LANEED, 0x4005, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x400b, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("LANEED USB Ethernet LD-USB/T", VENDOR_LANEED, 0xabc1, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x200c, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Linksys USB10TX", VENDOR_LINKSYS, 0x2202, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("Linksys USB100TX", VENDOR_LINKSYS, 0x2203, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("Linksys USB100TX", VENDOR_LINKSYS, 0x2204, ++ DEFAULT_GPIO_RESET | HAS_HOME_PNA) ++PEGASUS_DEV("Linksys USB10T Ethernet Adapter", VENDOR_LINKSYS, 0x2206, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Linksys USBVPN1", VENDOR_LINKSYS2, 0x08b4, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("Linksys USB USB100TX", VENDOR_LINKSYS, 0x400b, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Linksys USB10TX", VENDOR_LINKSYS, 0x200c, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0001, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0005, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("MELCO/BUFFALO LUA2-TX", VENDOR_MELCO, 0x0009, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("Microsoft MN-110", VENDOR_MICROSOFT, 0x007a, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("NETGEAR FA101", VENDOR_NETGEAR, 0x1020, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("OCT Inc.", VENDOR_OCT, 0x0109, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("OCT USB TO Ethernet", VENDOR_OCT, 0x0901, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("smartNIC 2 PnP Adapter", VENDOR_SMARTBRIDGES, 0x0003, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("SMC 202 USB Ethernet", VENDOR_SMC, 0x0200, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("SMC 2206 USB Ethernet", VENDOR_SMC, 0x0201, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100, ++ DEFAULT_GPIO_RESET) ++PEGASUS_DEV("SOHOware NUB110 Ethernet", VENDOR_SOHOWARE, 0x9110, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++PEGASUS_DEV("SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001, ++ DEFAULT_GPIO_RESET | PEGASUS_II) ++ ++ ++#endif /* PEGASUS_DEV */ +diff -Naur backports-3.18.1-1.org/drivers/net/usb/plusb.c backports-3.18.1-1/drivers/net/usb/plusb.c +--- backports-3.18.1-1.org/drivers/net/usb/plusb.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/plusb.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,157 @@ ++/* ++ * PL-2301/2302 USB host-to-host link cables ++ * Copyright (C) 2000-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, see . ++ */ ++ ++// #define DEBUG // error path messages, extra info ++// #define VERBOSE // more; success messages ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * Prolific PL-2301/PL-2302 driver ... http://www.prolific.com.tw/ ++ * ++ * The protocol and handshaking used here should be bug-compatible ++ * with the Linux 2.2 "plusb" driver, by Deti Fliegl. ++ * ++ * HEADS UP: this handshaking isn't all that robust. This driver ++ * gets confused easily if you unplug one end of the cable then ++ * try to connect it again; you'll need to restart both ends. The ++ * "naplink" software (used by some PlayStation/2 deveopers) does ++ * the handshaking much better! Also, sometimes this hardware ++ * seems to get wedged under load. Prolific docs are weak, and ++ * don't identify differences between PL2301 and PL2302, much less ++ * anything to explain the different PL2302 versions observed. ++ * ++ * NOTE: pl2501 has several modes, including pl2301 and pl2302 ++ * compatibility. Some docs suggest the difference between 2301 ++ * and 2302 is only to make MS-Windows use a different driver... ++ * ++ * pl25a1 glue based on patch from Tony Gibbs. Prolific "docs" on ++ * this chip are as usual incomplete about what control messages ++ * are supported. ++ */ ++ ++/* ++ * Bits 0-4 can be used for software handshaking; they're set from ++ * one end, cleared from the other, "read" with the interrupt byte. ++ */ ++#define PL_S_EN (1<<7) /* (feature only) suspend enable */ ++/* reserved bit -- rx ready (6) ? */ ++#define PL_TX_READY (1<<5) /* (interrupt only) transmit ready */ ++#define PL_RESET_OUT (1<<4) /* reset output pipe */ ++#define PL_RESET_IN (1<<3) /* reset input pipe */ ++#define PL_TX_C (1<<2) /* transmission complete */ ++#define PL_TX_REQ (1<<1) /* transmission received */ ++#define PL_PEER_E (1<<0) /* peer exists */ ++ ++static inline int ++pl_vendor_req(struct usbnet *dev, u8 req, u8 val, u8 index) ++{ ++ return usbnet_read_cmd(dev, req, ++ USB_DIR_IN | USB_TYPE_VENDOR | ++ USB_RECIP_DEVICE, ++ val, index, NULL, 0); ++} ++ ++static inline int ++pl_clear_QuickLink_features(struct usbnet *dev, int val) ++{ ++ return pl_vendor_req(dev, 1, (u8) val, 0); ++} ++ ++static inline int ++pl_set_QuickLink_features(struct usbnet *dev, int val) ++{ ++ return pl_vendor_req(dev, 3, (u8) val, 0); ++} ++ ++static int pl_reset(struct usbnet *dev) ++{ ++ int status; ++ ++ /* some units seem to need this reset, others reject it utterly. ++ * FIXME be more like "naplink" or windows drivers. ++ */ ++ status = pl_set_QuickLink_features(dev, ++ PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E); ++ if (status != 0 && netif_msg_probe(dev)) ++ netif_dbg(dev, link, dev->net, "pl_reset --> %d\n", status); ++ return 0; ++} ++ ++static const struct driver_info prolific_info = { ++ .description = "Prolific PL-2301/PL-2302/PL-25A1", ++ .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT, ++ /* some PL-2302 versions seem to fail usb_set_interface() */ ++ .reset = pl_reset, ++}; ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * Proilific's name won't normally be on the cables, and ++ * may not be on the device. ++ */ ++ ++static const struct usb_device_id products [] = { ++ ++/* full speed cables */ ++{ ++ USB_DEVICE(0x067b, 0x0000), // PL-2301 ++ .driver_info = (unsigned long) &prolific_info, ++}, { ++ USB_DEVICE(0x067b, 0x0001), // PL-2302 ++ .driver_info = (unsigned long) &prolific_info, ++}, ++ ++/* high speed cables */ ++{ ++ USB_DEVICE(0x067b, 0x25a1), /* PL-25A1, no eeprom */ ++ .driver_info = (unsigned long) &prolific_info, ++}, { ++ USB_DEVICE(0x050d, 0x258a), /* Belkin F5U258/F5U279 (PL-25A1) */ ++ .driver_info = (unsigned long) &prolific_info, ++}, ++ ++ { }, // END ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver plusb_driver = { ++ .name = "plusb", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(plusb_driver); ++ ++MODULE_AUTHOR("David Brownell"); ++MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1 USB Host to Host Link Driver"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/r8152.c backports-3.18.1-1/drivers/net/usb/r8152.c +--- backports-3.18.1-1.org/drivers/net/usb/r8152.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/r8152.c 2015-01-03 13:42:25.000000000 +0100 +@@ -0,0 +1,3913 @@ ++/* ++ * Copyright (c) 2014 Realtek Semiconductor Corp. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Version Information */ ++#define DRIVER_VERSION "v1.07.0 (2014/10/09)" ++#define DRIVER_AUTHOR "Realtek linux nic maintainers " ++#define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters" ++#define MODULENAME "r8152" ++ ++#define R8152_PHY_ID 32 ++ ++#define PLA_IDR 0xc000 ++#define PLA_RCR 0xc010 ++#define PLA_RMS 0xc016 ++#define PLA_RXFIFO_CTRL0 0xc0a0 ++#define PLA_RXFIFO_CTRL1 0xc0a4 ++#define PLA_RXFIFO_CTRL2 0xc0a8 ++#define PLA_FMC 0xc0b4 ++#define PLA_CFG_WOL 0xc0b6 ++#define PLA_TEREDO_CFG 0xc0bc ++#define PLA_MAR 0xcd00 ++#define PLA_BACKUP 0xd000 ++#define PAL_BDC_CR 0xd1a0 ++#define PLA_TEREDO_TIMER 0xd2cc ++#define PLA_REALWOW_TIMER 0xd2e8 ++#define PLA_LEDSEL 0xdd90 ++#define PLA_LED_FEATURE 0xdd92 ++#define PLA_PHYAR 0xde00 ++#define PLA_BOOT_CTRL 0xe004 ++#define PLA_GPHY_INTR_IMR 0xe022 ++#define PLA_EEE_CR 0xe040 ++#define PLA_EEEP_CR 0xe080 ++#define PLA_MAC_PWR_CTRL 0xe0c0 ++#define PLA_MAC_PWR_CTRL2 0xe0ca ++#define PLA_MAC_PWR_CTRL3 0xe0cc ++#define PLA_MAC_PWR_CTRL4 0xe0ce ++#define PLA_WDT6_CTRL 0xe428 ++#define PLA_TCR0 0xe610 ++#define PLA_TCR1 0xe612 ++#define PLA_MTPS 0xe615 ++#define PLA_TXFIFO_CTRL 0xe618 ++#define PLA_RSTTALLY 0xe800 ++#define PLA_CR 0xe813 ++#define PLA_CRWECR 0xe81c ++#define PLA_CONFIG12 0xe81e /* CONFIG1, CONFIG2 */ ++#define PLA_CONFIG34 0xe820 /* CONFIG3, CONFIG4 */ ++#define PLA_CONFIG5 0xe822 ++#define PLA_PHY_PWR 0xe84c ++#define PLA_OOB_CTRL 0xe84f ++#define PLA_CPCR 0xe854 ++#define PLA_MISC_0 0xe858 ++#define PLA_MISC_1 0xe85a ++#define PLA_OCP_GPHY_BASE 0xe86c ++#define PLA_TALLYCNT 0xe890 ++#define PLA_SFF_STS_7 0xe8de ++#define PLA_PHYSTATUS 0xe908 ++#define PLA_BP_BA 0xfc26 ++#define PLA_BP_0 0xfc28 ++#define PLA_BP_1 0xfc2a ++#define PLA_BP_2 0xfc2c ++#define PLA_BP_3 0xfc2e ++#define PLA_BP_4 0xfc30 ++#define PLA_BP_5 0xfc32 ++#define PLA_BP_6 0xfc34 ++#define PLA_BP_7 0xfc36 ++#define PLA_BP_EN 0xfc38 ++ ++#define USB_U2P3_CTRL 0xb460 ++#define USB_DEV_STAT 0xb808 ++#define USB_USB_CTRL 0xd406 ++#define USB_PHY_CTRL 0xd408 ++#define USB_TX_AGG 0xd40a ++#define USB_RX_BUF_TH 0xd40c ++#define USB_USB_TIMER 0xd428 ++#define USB_RX_EARLY_AGG 0xd42c ++#define USB_PM_CTRL_STATUS 0xd432 ++#define USB_TX_DMA 0xd434 ++#define USB_TOLERANCE 0xd490 ++#define USB_LPM_CTRL 0xd41a ++#define USB_UPS_CTRL 0xd800 ++#define USB_MISC_0 0xd81a ++#define USB_POWER_CUT 0xd80a ++#define USB_AFE_CTRL2 0xd824 ++#define USB_WDT11_CTRL 0xe43c ++#define USB_BP_BA 0xfc26 ++#define USB_BP_0 0xfc28 ++#define USB_BP_1 0xfc2a ++#define USB_BP_2 0xfc2c ++#define USB_BP_3 0xfc2e ++#define USB_BP_4 0xfc30 ++#define USB_BP_5 0xfc32 ++#define USB_BP_6 0xfc34 ++#define USB_BP_7 0xfc36 ++#define USB_BP_EN 0xfc38 ++ ++/* OCP Registers */ ++#define OCP_ALDPS_CONFIG 0x2010 ++#define OCP_EEE_CONFIG1 0x2080 ++#define OCP_EEE_CONFIG2 0x2092 ++#define OCP_EEE_CONFIG3 0x2094 ++#define OCP_BASE_MII 0xa400 ++#define OCP_EEE_AR 0xa41a ++#define OCP_EEE_DATA 0xa41c ++#define OCP_PHY_STATUS 0xa420 ++#define OCP_POWER_CFG 0xa430 ++#define OCP_EEE_CFG 0xa432 ++#define OCP_SRAM_ADDR 0xa436 ++#define OCP_SRAM_DATA 0xa438 ++#define OCP_DOWN_SPEED 0xa442 ++#define OCP_EEE_ABLE 0xa5c4 ++#define OCP_EEE_ADV 0xa5d0 ++#define OCP_EEE_LPABLE 0xa5d2 ++#define OCP_ADC_CFG 0xbc06 ++ ++/* SRAM Register */ ++#define SRAM_LPF_CFG 0x8012 ++#define SRAM_10M_AMP1 0x8080 ++#define SRAM_10M_AMP2 0x8082 ++#define SRAM_IMPEDANCE 0x8084 ++ ++/* PLA_RCR */ ++#define RCR_AAP 0x00000001 ++#define RCR_APM 0x00000002 ++#define RCR_AM 0x00000004 ++#define RCR_AB 0x00000008 ++#define RCR_ACPT_ALL (RCR_AAP | RCR_APM | RCR_AM | RCR_AB) ++ ++/* PLA_RXFIFO_CTRL0 */ ++#define RXFIFO_THR1_NORMAL 0x00080002 ++#define RXFIFO_THR1_OOB 0x01800003 ++ ++/* PLA_RXFIFO_CTRL1 */ ++#define RXFIFO_THR2_FULL 0x00000060 ++#define RXFIFO_THR2_HIGH 0x00000038 ++#define RXFIFO_THR2_OOB 0x0000004a ++#define RXFIFO_THR2_NORMAL 0x00a0 ++ ++/* PLA_RXFIFO_CTRL2 */ ++#define RXFIFO_THR3_FULL 0x00000078 ++#define RXFIFO_THR3_HIGH 0x00000048 ++#define RXFIFO_THR3_OOB 0x0000005a ++#define RXFIFO_THR3_NORMAL 0x0110 ++ ++/* PLA_TXFIFO_CTRL */ ++#define TXFIFO_THR_NORMAL 0x00400008 ++#define TXFIFO_THR_NORMAL2 0x01000008 ++ ++/* PLA_FMC */ ++#define FMC_FCR_MCU_EN 0x0001 ++ ++/* PLA_EEEP_CR */ ++#define EEEP_CR_EEEP_TX 0x0002 ++ ++/* PLA_WDT6_CTRL */ ++#define WDT6_SET_MODE 0x0010 ++ ++/* PLA_TCR0 */ ++#define TCR0_TX_EMPTY 0x0800 ++#define TCR0_AUTO_FIFO 0x0080 ++ ++/* PLA_TCR1 */ ++#define VERSION_MASK 0x7cf0 ++ ++/* PLA_MTPS */ ++#define MTPS_JUMBO (12 * 1024 / 64) ++#define MTPS_DEFAULT (6 * 1024 / 64) ++ ++/* PLA_RSTTALLY */ ++#define TALLY_RESET 0x0001 ++ ++/* PLA_CR */ ++#define CR_RST 0x10 ++#define CR_RE 0x08 ++#define CR_TE 0x04 ++ ++/* PLA_CRWECR */ ++#define CRWECR_NORAML 0x00 ++#define CRWECR_CONFIG 0xc0 ++ ++/* PLA_OOB_CTRL */ ++#define NOW_IS_OOB 0x80 ++#define TXFIFO_EMPTY 0x20 ++#define RXFIFO_EMPTY 0x10 ++#define LINK_LIST_READY 0x02 ++#define DIS_MCU_CLROOB 0x01 ++#define FIFO_EMPTY (TXFIFO_EMPTY | RXFIFO_EMPTY) ++ ++/* PLA_MISC_1 */ ++#define RXDY_GATED_EN 0x0008 ++ ++/* PLA_SFF_STS_7 */ ++#define RE_INIT_LL 0x8000 ++#define MCU_BORW_EN 0x4000 ++ ++/* PLA_CPCR */ ++#define CPCR_RX_VLAN 0x0040 ++ ++/* PLA_CFG_WOL */ ++#define MAGIC_EN 0x0001 ++ ++/* PLA_TEREDO_CFG */ ++#define TEREDO_SEL 0x8000 ++#define TEREDO_WAKE_MASK 0x7f00 ++#define TEREDO_RS_EVENT_MASK 0x00fe ++#define OOB_TEREDO_EN 0x0001 ++ ++/* PAL_BDC_CR */ ++#define ALDPS_PROXY_MODE 0x0001 ++ ++/* PLA_CONFIG34 */ ++#define LINK_ON_WAKE_EN 0x0010 ++#define LINK_OFF_WAKE_EN 0x0008 ++ ++/* PLA_CONFIG5 */ ++#define BWF_EN 0x0040 ++#define MWF_EN 0x0020 ++#define UWF_EN 0x0010 ++#define LAN_WAKE_EN 0x0002 ++ ++/* PLA_LED_FEATURE */ ++#define LED_MODE_MASK 0x0700 ++ ++/* PLA_PHY_PWR */ ++#define TX_10M_IDLE_EN 0x0080 ++#define PFM_PWM_SWITCH 0x0040 ++ ++/* PLA_MAC_PWR_CTRL */ ++#define D3_CLK_GATED_EN 0x00004000 ++#define MCU_CLK_RATIO 0x07010f07 ++#define MCU_CLK_RATIO_MASK 0x0f0f0f0f ++#define ALDPS_SPDWN_RATIO 0x0f87 ++ ++/* PLA_MAC_PWR_CTRL2 */ ++#define EEE_SPDWN_RATIO 0x8007 ++ ++/* PLA_MAC_PWR_CTRL3 */ ++#define PKT_AVAIL_SPDWN_EN 0x0100 ++#define SUSPEND_SPDWN_EN 0x0004 ++#define U1U2_SPDWN_EN 0x0002 ++#define L1_SPDWN_EN 0x0001 ++ ++/* PLA_MAC_PWR_CTRL4 */ ++#define PWRSAVE_SPDWN_EN 0x1000 ++#define RXDV_SPDWN_EN 0x0800 ++#define TX10MIDLE_EN 0x0100 ++#define TP100_SPDWN_EN 0x0020 ++#define TP500_SPDWN_EN 0x0010 ++#define TP1000_SPDWN_EN 0x0008 ++#define EEE_SPDWN_EN 0x0001 ++ ++/* PLA_GPHY_INTR_IMR */ ++#define GPHY_STS_MSK 0x0001 ++#define SPEED_DOWN_MSK 0x0002 ++#define SPDWN_RXDV_MSK 0x0004 ++#define SPDWN_LINKCHG_MSK 0x0008 ++ ++/* PLA_PHYAR */ ++#define PHYAR_FLAG 0x80000000 ++ ++/* PLA_EEE_CR */ ++#define EEE_RX_EN 0x0001 ++#define EEE_TX_EN 0x0002 ++ ++/* PLA_BOOT_CTRL */ ++#define AUTOLOAD_DONE 0x0002 ++ ++/* USB_DEV_STAT */ ++#define STAT_SPEED_MASK 0x0006 ++#define STAT_SPEED_HIGH 0x0000 ++#define STAT_SPEED_FULL 0x0002 ++ ++/* USB_TX_AGG */ ++#define TX_AGG_MAX_THRESHOLD 0x03 ++ ++/* USB_RX_BUF_TH */ ++#define RX_THR_SUPPER 0x0c350180 ++#define RX_THR_HIGH 0x7a120180 ++#define RX_THR_SLOW 0xffff0180 ++ ++/* USB_TX_DMA */ ++#define TEST_MODE_DISABLE 0x00000001 ++#define TX_SIZE_ADJUST1 0x00000100 ++ ++/* USB_UPS_CTRL */ ++#define POWER_CUT 0x0100 ++ ++/* USB_PM_CTRL_STATUS */ ++#define RESUME_INDICATE 0x0001 ++ ++/* USB_USB_CTRL */ ++#define RX_AGG_DISABLE 0x0010 ++ ++/* USB_U2P3_CTRL */ ++#define U2P3_ENABLE 0x0001 ++ ++/* USB_POWER_CUT */ ++#define PWR_EN 0x0001 ++#define PHASE2_EN 0x0008 ++ ++/* USB_MISC_0 */ ++#define PCUT_STATUS 0x0001 ++ ++/* USB_RX_EARLY_AGG */ ++#define EARLY_AGG_SUPPER 0x0e832981 ++#define EARLY_AGG_HIGH 0x0e837a12 ++#define EARLY_AGG_SLOW 0x0e83ffff ++ ++/* USB_WDT11_CTRL */ ++#define TIMER11_EN 0x0001 ++ ++/* USB_LPM_CTRL */ ++#define LPM_TIMER_MASK 0x0c ++#define LPM_TIMER_500MS 0x04 /* 500 ms */ ++#define LPM_TIMER_500US 0x0c /* 500 us */ ++ ++/* USB_AFE_CTRL2 */ ++#define SEN_VAL_MASK 0xf800 ++#define SEN_VAL_NORMAL 0xa000 ++#define SEL_RXIDLE 0x0100 ++ ++/* OCP_ALDPS_CONFIG */ ++#define ENPWRSAVE 0x8000 ++#define ENPDNPS 0x0200 ++#define LINKENA 0x0100 ++#define DIS_SDSAVE 0x0010 ++ ++/* OCP_PHY_STATUS */ ++#define PHY_STAT_MASK 0x0007 ++#define PHY_STAT_LAN_ON 3 ++#define PHY_STAT_PWRDN 5 ++ ++/* OCP_POWER_CFG */ ++#define EEE_CLKDIV_EN 0x8000 ++#define EN_ALDPS 0x0004 ++#define EN_10M_PLLOFF 0x0001 ++ ++/* OCP_EEE_CONFIG1 */ ++#define RG_TXLPI_MSK_HFDUP 0x8000 ++#define RG_MATCLR_EN 0x4000 ++#define EEE_10_CAP 0x2000 ++#define EEE_NWAY_EN 0x1000 ++#define TX_QUIET_EN 0x0200 ++#define RX_QUIET_EN 0x0100 ++#define sd_rise_time_mask 0x0070 ++#define sd_rise_time(x) (min(x, 7) << 4) /* bit 4 ~ 6 */ ++#define RG_RXLPI_MSK_HFDUP 0x0008 ++#define SDFALLTIME 0x0007 /* bit 0 ~ 2 */ ++ ++/* OCP_EEE_CONFIG2 */ ++#define RG_LPIHYS_NUM 0x7000 /* bit 12 ~ 15 */ ++#define RG_DACQUIET_EN 0x0400 ++#define RG_LDVQUIET_EN 0x0200 ++#define RG_CKRSEL 0x0020 ++#define RG_EEEPRG_EN 0x0010 ++ ++/* OCP_EEE_CONFIG3 */ ++#define fast_snr_mask 0xff80 ++#define fast_snr(x) (min(x, 0x1ff) << 7) /* bit 7 ~ 15 */ ++#define RG_LFS_SEL 0x0060 /* bit 6 ~ 5 */ ++#define MSK_PH 0x0006 /* bit 0 ~ 3 */ ++ ++/* OCP_EEE_AR */ ++/* bit[15:14] function */ ++#define FUN_ADDR 0x0000 ++#define FUN_DATA 0x4000 ++/* bit[4:0] device addr */ ++ ++/* OCP_EEE_CFG */ ++#define CTAP_SHORT_EN 0x0040 ++#define EEE10_EN 0x0010 ++ ++/* OCP_DOWN_SPEED */ ++#define EN_10M_BGOFF 0x0080 ++ ++/* OCP_ADC_CFG */ ++#define CKADSEL_L 0x0100 ++#define ADC_EN 0x0080 ++#define EN_EMI_L 0x0040 ++ ++/* SRAM_LPF_CFG */ ++#define LPF_AUTO_TUNE 0x8000 ++ ++/* SRAM_10M_AMP1 */ ++#define GDAC_IB_UPALL 0x0008 ++ ++/* SRAM_10M_AMP2 */ ++#define AMP_DN 0x0200 ++ ++/* SRAM_IMPEDANCE */ ++#define RX_DRIVING_MASK 0x6000 ++ ++enum rtl_register_content { ++ _1000bps = 0x10, ++ _100bps = 0x08, ++ _10bps = 0x04, ++ LINK_STATUS = 0x02, ++ FULL_DUP = 0x01, ++}; ++ ++#define RTL8152_MAX_TX 4 ++#define RTL8152_MAX_RX 10 ++#define INTBUFSIZE 2 ++#define CRC_SIZE 4 ++#define TX_ALIGN 4 ++#define RX_ALIGN 8 ++ ++#define INTR_LINK 0x0004 ++ ++#define RTL8152_REQT_READ 0xc0 ++#define RTL8152_REQT_WRITE 0x40 ++#define RTL8152_REQ_GET_REGS 0x05 ++#define RTL8152_REQ_SET_REGS 0x05 ++ ++#define BYTE_EN_DWORD 0xff ++#define BYTE_EN_WORD 0x33 ++#define BYTE_EN_BYTE 0x11 ++#define BYTE_EN_SIX_BYTES 0x3f ++#define BYTE_EN_START_MASK 0x0f ++#define BYTE_EN_END_MASK 0xf0 ++ ++#define RTL8153_MAX_PACKET 9216 /* 9K */ ++#define RTL8153_MAX_MTU (RTL8153_MAX_PACKET - VLAN_ETH_HLEN - VLAN_HLEN) ++#define RTL8152_RMS (VLAN_ETH_FRAME_LEN + VLAN_HLEN) ++#define RTL8153_RMS RTL8153_MAX_PACKET ++#define RTL8152_TX_TIMEOUT (5 * HZ) ++ ++/* rtl8152 flags */ ++enum rtl8152_flags { ++ RTL8152_UNPLUG = 0, ++ RTL8152_SET_RX_MODE, ++ WORK_ENABLE, ++ RTL8152_LINK_CHG, ++ SELECTIVE_SUSPEND, ++ PHY_RESET, ++ SCHEDULE_TASKLET, ++}; ++ ++/* Define these values to match your device */ ++#define VENDOR_ID_REALTEK 0x0bda ++#define PRODUCT_ID_RTL8152 0x8152 ++#define PRODUCT_ID_RTL8153 0x8153 ++ ++#define VENDOR_ID_SAMSUNG 0x04e8 ++#define PRODUCT_ID_SAMSUNG 0xa101 ++ ++#define MCU_TYPE_PLA 0x0100 ++#define MCU_TYPE_USB 0x0000 ++ ++#define REALTEK_USB_DEVICE(vend, prod) \ ++ USB_DEVICE_INTERFACE_CLASS(vend, prod, USB_CLASS_VENDOR_SPEC) ++ ++struct tally_counter { ++ __le64 tx_packets; ++ __le64 rx_packets; ++ __le64 tx_errors; ++ __le32 rx_errors; ++ __le16 rx_missed; ++ __le16 align_errors; ++ __le32 tx_one_collision; ++ __le32 tx_multi_collision; ++ __le64 rx_unicast; ++ __le64 rx_broadcast; ++ __le32 rx_multicast; ++ __le16 tx_aborted; ++ __le16 tx_underun; ++}; ++ ++struct rx_desc { ++ __le32 opts1; ++#define RX_LEN_MASK 0x7fff ++ ++ __le32 opts2; ++#define RD_UDP_CS (1 << 23) ++#define RD_TCP_CS (1 << 22) ++#define RD_IPV6_CS (1 << 20) ++#define RD_IPV4_CS (1 << 19) ++ ++ __le32 opts3; ++#define IPF (1 << 23) /* IP checksum fail */ ++#define UDPF (1 << 22) /* UDP checksum fail */ ++#define TCPF (1 << 21) /* TCP checksum fail */ ++#define RX_VLAN_TAG (1 << 16) ++ ++ __le32 opts4; ++ __le32 opts5; ++ __le32 opts6; ++}; ++ ++struct tx_desc { ++ __le32 opts1; ++#define TX_FS (1 << 31) /* First segment of a packet */ ++#define TX_LS (1 << 30) /* Final segment of a packet */ ++#define GTSENDV4 (1 << 28) ++#define GTSENDV6 (1 << 27) ++#define GTTCPHO_SHIFT 18 ++#define GTTCPHO_MAX 0x7fU ++#define TX_LEN_MAX 0x3ffffU ++ ++ __le32 opts2; ++#define UDP_CS (1 << 31) /* Calculate UDP/IP checksum */ ++#define TCP_CS (1 << 30) /* Calculate TCP/IP checksum */ ++#define IPV4_CS (1 << 29) /* Calculate IPv4 checksum */ ++#define IPV6_CS (1 << 28) /* Calculate IPv6 checksum */ ++#define MSS_SHIFT 17 ++#define MSS_MAX 0x7ffU ++#define TCPHO_SHIFT 17 ++#define TCPHO_MAX 0x7ffU ++#define TX_VLAN_TAG (1 << 16) ++}; ++ ++struct r8152; ++ ++struct rx_agg { ++ struct list_head list; ++ struct urb *urb; ++ struct r8152 *context; ++ void *buffer; ++ void *head; ++}; ++ ++struct tx_agg { ++ struct list_head list; ++ struct urb *urb; ++ struct r8152 *context; ++ void *buffer; ++ void *head; ++ u32 skb_num; ++ u32 skb_len; ++}; ++ ++struct r8152 { ++ unsigned long flags; ++ struct usb_device *udev; ++ struct tasklet_struct tl; ++ struct usb_interface *intf; ++ struct net_device *netdev; ++ struct urb *intr_urb; ++ struct tx_agg tx_info[RTL8152_MAX_TX]; ++ struct rx_agg rx_info[RTL8152_MAX_RX]; ++ struct list_head rx_done, tx_free; ++ struct sk_buff_head tx_queue; ++ spinlock_t rx_lock, tx_lock; ++ struct delayed_work schedule; ++ struct mii_if_info mii; ++ struct mutex control; /* use for hw setting */ ++ ++ struct rtl_ops { ++ void (*init)(struct r8152 *); ++ int (*enable)(struct r8152 *); ++ void (*disable)(struct r8152 *); ++ void (*up)(struct r8152 *); ++ void (*down)(struct r8152 *); ++ void (*unload)(struct r8152 *); ++ int (*eee_get)(struct r8152 *, struct ethtool_eee *); ++ int (*eee_set)(struct r8152 *, struct ethtool_eee *); ++ } __no_const rtl_ops; ++ ++ int intr_interval; ++ u32 saved_wolopts; ++ u32 msg_enable; ++ u32 tx_qlen; ++ u16 ocp_base; ++ u8 *intr_buff; ++ u8 version; ++ u8 speed; ++}; ++ ++enum rtl_version { ++ RTL_VER_UNKNOWN = 0, ++ RTL_VER_01, ++ RTL_VER_02, ++ RTL_VER_03, ++ RTL_VER_04, ++ RTL_VER_05, ++ RTL_VER_MAX ++}; ++ ++enum tx_csum_stat { ++ TX_CSUM_SUCCESS = 0, ++ TX_CSUM_TSO, ++ TX_CSUM_NONE ++}; ++ ++/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). ++ * The RTL chips use a 64 element hash table based on the Ethernet CRC. ++ */ ++static const int multicast_filter_limit = 32; ++static unsigned int agg_buf_sz = 16384; ++ ++#define RTL_LIMITED_TSO_SIZE (agg_buf_sz - sizeof(struct tx_desc) - \ ++ VLAN_ETH_HLEN - VLAN_HLEN) ++ ++static ++int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) ++{ ++ int ret; ++ void *tmp; ++ ++ tmp = kmalloc(size, GFP_KERNEL); ++ if (!tmp) ++ return -ENOMEM; ++ ++ ret = usb_control_msg(tp->udev, usb_rcvctrlpipe(tp->udev, 0), ++ RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, ++ value, index, tmp, size, 500); ++ ++ memcpy(data, tmp, size); ++ kfree(tmp); ++ ++ return ret; ++} ++ ++static ++int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) ++{ ++ int ret; ++ void *tmp; ++ ++ tmp = kmemdup(data, size, GFP_KERNEL); ++ if (!tmp) ++ return -ENOMEM; ++ ++ ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0), ++ RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, ++ value, index, tmp, size, 500); ++ ++ kfree(tmp); ++ ++ return ret; ++} ++ ++static int generic_ocp_read(struct r8152 *tp, u16 index, u16 size, ++ void *data, u16 type) ++{ ++ u16 limit = 64; ++ int ret = 0; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return -ENODEV; ++ ++ /* both size and indix must be 4 bytes align */ ++ if ((size & 3) || !size || (index & 3) || !data) ++ return -EPERM; ++ ++ if ((u32)index + (u32)size > 0xffff) ++ return -EPERM; ++ ++ while (size) { ++ if (size > limit) { ++ ret = get_registers(tp, index, type, limit, data); ++ if (ret < 0) ++ break; ++ ++ index += limit; ++ data += limit; ++ size -= limit; ++ } else { ++ ret = get_registers(tp, index, type, size, data); ++ if (ret < 0) ++ break; ++ ++ index += size; ++ data += size; ++ size = 0; ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++static int generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen, ++ u16 size, void *data, u16 type) ++{ ++ int ret; ++ u16 byteen_start, byteen_end, byen; ++ u16 limit = 512; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return -ENODEV; ++ ++ /* both size and indix must be 4 bytes align */ ++ if ((size & 3) || !size || (index & 3) || !data) ++ return -EPERM; ++ ++ if ((u32)index + (u32)size > 0xffff) ++ return -EPERM; ++ ++ byteen_start = byteen & BYTE_EN_START_MASK; ++ byteen_end = byteen & BYTE_EN_END_MASK; ++ ++ byen = byteen_start | (byteen_start << 4); ++ ret = set_registers(tp, index, type | byen, 4, data); ++ if (ret < 0) ++ goto error1; ++ ++ index += 4; ++ data += 4; ++ size -= 4; ++ ++ if (size) { ++ size -= 4; ++ ++ while (size) { ++ if (size > limit) { ++ ret = set_registers(tp, index, ++ type | BYTE_EN_DWORD, ++ limit, data); ++ if (ret < 0) ++ goto error1; ++ ++ index += limit; ++ data += limit; ++ size -= limit; ++ } else { ++ ret = set_registers(tp, index, ++ type | BYTE_EN_DWORD, ++ size, data); ++ if (ret < 0) ++ goto error1; ++ ++ index += size; ++ data += size; ++ size = 0; ++ break; ++ } ++ } ++ ++ byen = byteen_end | (byteen_end >> 4); ++ ret = set_registers(tp, index, type | byen, 4, data); ++ if (ret < 0) ++ goto error1; ++ } ++ ++error1: ++ return ret; ++} ++ ++static inline ++int pla_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) ++{ ++ return generic_ocp_read(tp, index, size, data, MCU_TYPE_PLA); ++} ++ ++static inline ++int pla_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) ++{ ++ return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_PLA); ++} ++ ++static inline ++int usb_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) ++{ ++ return generic_ocp_read(tp, index, size, data, MCU_TYPE_USB); ++} ++ ++static inline ++int usb_ocp_write(struct r8152 *tp, u16 index, u16 byteen, u16 size, void *data) ++{ ++ return generic_ocp_write(tp, index, byteen, size, data, MCU_TYPE_USB); ++} ++ ++static u32 ocp_read_dword(struct r8152 *tp, u16 type, u16 index) ++{ ++ __le32 data; ++ ++ generic_ocp_read(tp, index, sizeof(data), &data, type); ++ ++ return __le32_to_cpu(data); ++} ++ ++static void ocp_write_dword(struct r8152 *tp, u16 type, u16 index, u32 data) ++{ ++ __le32 tmp = __cpu_to_le32(data); ++ ++ generic_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(tmp), &tmp, type); ++} ++ ++static u16 ocp_read_word(struct r8152 *tp, u16 type, u16 index) ++{ ++ u32 data; ++ __le32 tmp; ++ u8 shift = index & 2; ++ ++ index &= ~3; ++ ++ generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); ++ ++ data = __le32_to_cpu(tmp); ++ data >>= (shift * 8); ++ data &= 0xffff; ++ ++ return (u16)data; ++} ++ ++static void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data) ++{ ++ u32 mask = 0xffff; ++ __le32 tmp; ++ u16 byen = BYTE_EN_WORD; ++ u8 shift = index & 2; ++ ++ data &= mask; ++ ++ if (index & 2) { ++ byen <<= shift; ++ mask <<= (shift * 8); ++ data <<= (shift * 8); ++ index &= ~3; ++ } ++ ++ generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); ++ ++ data |= __le32_to_cpu(tmp) & ~mask; ++ tmp = __cpu_to_le32(data); ++ ++ generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); ++} ++ ++static u8 ocp_read_byte(struct r8152 *tp, u16 type, u16 index) ++{ ++ u32 data; ++ __le32 tmp; ++ u8 shift = index & 3; ++ ++ index &= ~3; ++ ++ generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); ++ ++ data = __le32_to_cpu(tmp); ++ data >>= (shift * 8); ++ data &= 0xff; ++ ++ return (u8)data; ++} ++ ++static void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data) ++{ ++ u32 mask = 0xff; ++ __le32 tmp; ++ u16 byen = BYTE_EN_BYTE; ++ u8 shift = index & 3; ++ ++ data &= mask; ++ ++ if (index & 3) { ++ byen <<= shift; ++ mask <<= (shift * 8); ++ data <<= (shift * 8); ++ index &= ~3; ++ } ++ ++ generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); ++ ++ data |= __le32_to_cpu(tmp) & ~mask; ++ tmp = __cpu_to_le32(data); ++ ++ generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); ++} ++ ++static u16 ocp_reg_read(struct r8152 *tp, u16 addr) ++{ ++ u16 ocp_base, ocp_index; ++ ++ ocp_base = addr & 0xf000; ++ if (ocp_base != tp->ocp_base) { ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); ++ tp->ocp_base = ocp_base; ++ } ++ ++ ocp_index = (addr & 0x0fff) | 0xb000; ++ return ocp_read_word(tp, MCU_TYPE_PLA, ocp_index); ++} ++ ++static void ocp_reg_write(struct r8152 *tp, u16 addr, u16 data) ++{ ++ u16 ocp_base, ocp_index; ++ ++ ocp_base = addr & 0xf000; ++ if (ocp_base != tp->ocp_base) { ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, ocp_base); ++ tp->ocp_base = ocp_base; ++ } ++ ++ ocp_index = (addr & 0x0fff) | 0xb000; ++ ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data); ++} ++ ++static inline void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value) ++{ ++ ocp_reg_write(tp, OCP_BASE_MII + reg_addr * 2, value); ++} ++ ++static inline int r8152_mdio_read(struct r8152 *tp, u32 reg_addr) ++{ ++ return ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2); ++} ++ ++static void sram_write(struct r8152 *tp, u16 addr, u16 data) ++{ ++ ocp_reg_write(tp, OCP_SRAM_ADDR, addr); ++ ocp_reg_write(tp, OCP_SRAM_DATA, data); ++} ++ ++static u16 sram_read(struct r8152 *tp, u16 addr) ++{ ++ ocp_reg_write(tp, OCP_SRAM_ADDR, addr); ++ return ocp_reg_read(tp, OCP_SRAM_DATA); ++} ++ ++static int read_mii_word(struct net_device *netdev, int phy_id, int reg) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ int ret; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return -ENODEV; ++ ++ if (phy_id != R8152_PHY_ID) ++ return -EINVAL; ++ ++ ret = r8152_mdio_read(tp, reg); ++ ++ return ret; ++} ++ ++static ++void write_mii_word(struct net_device *netdev, int phy_id, int reg, int val) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ if (phy_id != R8152_PHY_ID) ++ return; ++ ++ r8152_mdio_write(tp, reg, val); ++} ++ ++static int ++r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags); ++ ++static int rtl8152_set_mac_address(struct net_device *netdev, void *p) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ struct sockaddr *addr = p; ++ int ret = -EADDRNOTAVAIL; ++ ++ if (!is_valid_ether_addr(addr->sa_data)) ++ goto out1; ++ ++ ret = usb_autopm_get_interface(tp->intf); ++ if (ret < 0) ++ goto out1; ++ ++ mutex_lock(&tp->control); ++ ++ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ++ pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, 8, addr->sa_data); ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ++ ++ mutex_unlock(&tp->control); ++ ++ usb_autopm_put_interface(tp->intf); ++out1: ++ return ret; ++} ++ ++static int set_ethernet_addr(struct r8152 *tp) ++{ ++ struct net_device *dev = tp->netdev; ++ struct sockaddr sa; ++ int ret; ++ ++ if (tp->version == RTL_VER_01) ++ ret = pla_ocp_read(tp, PLA_IDR, 8, sa.sa_data); ++ else ++ ret = pla_ocp_read(tp, PLA_BACKUP, 8, sa.sa_data); ++ ++ if (ret < 0) { ++ netif_err(tp, probe, dev, "Get ether addr fail\n"); ++ } else if (!is_valid_ether_addr(sa.sa_data)) { ++ netif_err(tp, probe, dev, "Invalid ether addr %pM\n", ++ sa.sa_data); ++ eth_hw_addr_random(dev); ++ ether_addr_copy(sa.sa_data, dev->dev_addr); ++ ret = rtl8152_set_mac_address(dev, &sa); ++ netif_info(tp, probe, dev, "Random ether addr %pM\n", ++ sa.sa_data); ++ } else { ++ if (tp->version == RTL_VER_01) ++ ether_addr_copy(dev->dev_addr, sa.sa_data); ++ else ++ ret = rtl8152_set_mac_address(dev, &sa); ++ } ++ ++ return ret; ++} ++ ++static void read_bulk_callback(struct urb *urb) ++{ ++ struct net_device *netdev; ++ int status = urb->status; ++ struct rx_agg *agg; ++ struct r8152 *tp; ++ int result; ++ ++ agg = urb->context; ++ if (!agg) ++ return; ++ ++ tp = agg->context; ++ if (!tp) ++ return; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ if (!test_bit(WORK_ENABLE, &tp->flags)) ++ return; ++ ++ netdev = tp->netdev; ++ ++ /* When link down, the driver would cancel all bulks. */ ++ /* This avoid the re-submitting bulk */ ++ if (!netif_carrier_ok(netdev)) ++ return; ++ ++ usb_mark_last_busy(tp->udev); ++ ++ switch (status) { ++ case 0: ++ if (urb->actual_length < ETH_ZLEN) ++ break; ++ ++ spin_lock(&tp->rx_lock); ++ list_add_tail(&agg->list, &tp->rx_done); ++ spin_unlock(&tp->rx_lock); ++ tasklet_schedule(&tp->tl); ++ return; ++ case -ESHUTDOWN: ++ set_bit(RTL8152_UNPLUG, &tp->flags); ++ netif_device_detach(tp->netdev); ++ return; ++ case -ENOENT: ++ return; /* the urb is in unlink state */ ++ case -ETIME: ++ if (net_ratelimit()) ++ netdev_warn(netdev, "maybe reset is needed?\n"); ++ break; ++ default: ++ if (net_ratelimit()) ++ netdev_warn(netdev, "Rx status %d\n", status); ++ break; ++ } ++ ++ result = r8152_submit_rx(tp, agg, GFP_ATOMIC); ++ if (result == -ENODEV) { ++ netif_device_detach(tp->netdev); ++ } else if (result) { ++ spin_lock(&tp->rx_lock); ++ list_add_tail(&agg->list, &tp->rx_done); ++ spin_unlock(&tp->rx_lock); ++ tasklet_schedule(&tp->tl); ++ } ++} ++ ++static void write_bulk_callback(struct urb *urb) ++{ ++ struct net_device_stats *stats; ++ struct net_device *netdev; ++ struct tx_agg *agg; ++ struct r8152 *tp; ++ int status = urb->status; ++ ++ agg = urb->context; ++ if (!agg) ++ return; ++ ++ tp = agg->context; ++ if (!tp) ++ return; ++ ++ netdev = tp->netdev; ++ stats = &netdev->stats; ++ if (status) { ++ if (net_ratelimit()) ++ netdev_warn(netdev, "Tx status %d\n", status); ++ stats->tx_errors += agg->skb_num; ++ } else { ++ stats->tx_packets += agg->skb_num; ++ stats->tx_bytes += agg->skb_len; ++ } ++ ++ spin_lock(&tp->tx_lock); ++ list_add_tail(&agg->list, &tp->tx_free); ++ spin_unlock(&tp->tx_lock); ++ ++ usb_autopm_put_interface_async(tp->intf); ++ ++ if (!netif_carrier_ok(netdev)) ++ return; ++ ++ if (!test_bit(WORK_ENABLE, &tp->flags)) ++ return; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ if (!skb_queue_empty(&tp->tx_queue)) ++ tasklet_schedule(&tp->tl); ++} ++ ++static void intr_callback(struct urb *urb) ++{ ++ struct r8152 *tp; ++ __le16 *d; ++ int status = urb->status; ++ int res; ++ ++ tp = urb->context; ++ if (!tp) ++ return; ++ ++ if (!test_bit(WORK_ENABLE, &tp->flags)) ++ return; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ switch (status) { ++ case 0: /* success */ ++ break; ++ case -ECONNRESET: /* unlink */ ++ case -ESHUTDOWN: ++ netif_device_detach(tp->netdev); ++ case -ENOENT: ++ case -EPROTO: ++ netif_info(tp, intr, tp->netdev, ++ "Stop submitting intr, status %d\n", status); ++ return; ++ case -EOVERFLOW: ++ netif_info(tp, intr, tp->netdev, "intr status -EOVERFLOW\n"); ++ goto resubmit; ++ /* -EPIPE: should clear the halt */ ++ default: ++ netif_info(tp, intr, tp->netdev, "intr status %d\n", status); ++ goto resubmit; ++ } ++ ++ d = urb->transfer_buffer; ++ if (INTR_LINK & __le16_to_cpu(d[0])) { ++ if (!(tp->speed & LINK_STATUS)) { ++ set_bit(RTL8152_LINK_CHG, &tp->flags); ++ schedule_delayed_work(&tp->schedule, 0); ++ } ++ } else { ++ if (tp->speed & LINK_STATUS) { ++ set_bit(RTL8152_LINK_CHG, &tp->flags); ++ schedule_delayed_work(&tp->schedule, 0); ++ } ++ } ++ ++resubmit: ++ res = usb_submit_urb(urb, GFP_ATOMIC); ++ if (res == -ENODEV) ++ netif_device_detach(tp->netdev); ++ else if (res) ++ netif_err(tp, intr, tp->netdev, ++ "can't resubmit intr, status %d\n", res); ++} ++ ++static inline void *rx_agg_align(void *data) ++{ ++ return (void *)ALIGN((uintptr_t)data, RX_ALIGN); ++} ++ ++static inline void *tx_agg_align(void *data) ++{ ++ return (void *)ALIGN((uintptr_t)data, TX_ALIGN); ++} ++ ++static void free_all_mem(struct r8152 *tp) ++{ ++ int i; ++ ++ for (i = 0; i < RTL8152_MAX_RX; i++) { ++ usb_free_urb(tp->rx_info[i].urb); ++ tp->rx_info[i].urb = NULL; ++ ++ kfree(tp->rx_info[i].buffer); ++ tp->rx_info[i].buffer = NULL; ++ tp->rx_info[i].head = NULL; ++ } ++ ++ for (i = 0; i < RTL8152_MAX_TX; i++) { ++ usb_free_urb(tp->tx_info[i].urb); ++ tp->tx_info[i].urb = NULL; ++ ++ kfree(tp->tx_info[i].buffer); ++ tp->tx_info[i].buffer = NULL; ++ tp->tx_info[i].head = NULL; ++ } ++ ++ usb_free_urb(tp->intr_urb); ++ tp->intr_urb = NULL; ++ ++ kfree(tp->intr_buff); ++ tp->intr_buff = NULL; ++} ++ ++static int alloc_all_mem(struct r8152 *tp) ++{ ++ struct net_device *netdev = tp->netdev; ++ struct usb_interface *intf = tp->intf; ++ struct usb_host_interface *alt = intf->cur_altsetting; ++ struct usb_host_endpoint *ep_intr = alt->endpoint + 2; ++ struct urb *urb; ++ int node, i; ++ u8 *buf; ++ ++ node = netdev->dev.parent ? dev_to_node(netdev->dev.parent) : -1; ++ ++ spin_lock_init(&tp->rx_lock); ++ spin_lock_init(&tp->tx_lock); ++ INIT_LIST_HEAD(&tp->rx_done); ++ INIT_LIST_HEAD(&tp->tx_free); ++ skb_queue_head_init(&tp->tx_queue); ++ ++ for (i = 0; i < RTL8152_MAX_RX; i++) { ++ buf = kmalloc_node(agg_buf_sz, GFP_KERNEL, node); ++ if (!buf) ++ goto err1; ++ ++ if (buf != rx_agg_align(buf)) { ++ kfree(buf); ++ buf = kmalloc_node(agg_buf_sz + RX_ALIGN, GFP_KERNEL, ++ node); ++ if (!buf) ++ goto err1; ++ } ++ ++ urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb) { ++ kfree(buf); ++ goto err1; ++ } ++ ++ INIT_LIST_HEAD(&tp->rx_info[i].list); ++ tp->rx_info[i].context = tp; ++ tp->rx_info[i].urb = urb; ++ tp->rx_info[i].buffer = buf; ++ tp->rx_info[i].head = rx_agg_align(buf); ++ } ++ ++ for (i = 0; i < RTL8152_MAX_TX; i++) { ++ buf = kmalloc_node(agg_buf_sz, GFP_KERNEL, node); ++ if (!buf) ++ goto err1; ++ ++ if (buf != tx_agg_align(buf)) { ++ kfree(buf); ++ buf = kmalloc_node(agg_buf_sz + TX_ALIGN, GFP_KERNEL, ++ node); ++ if (!buf) ++ goto err1; ++ } ++ ++ urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb) { ++ kfree(buf); ++ goto err1; ++ } ++ ++ INIT_LIST_HEAD(&tp->tx_info[i].list); ++ tp->tx_info[i].context = tp; ++ tp->tx_info[i].urb = urb; ++ tp->tx_info[i].buffer = buf; ++ tp->tx_info[i].head = tx_agg_align(buf); ++ ++ list_add_tail(&tp->tx_info[i].list, &tp->tx_free); ++ } ++ ++ tp->intr_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!tp->intr_urb) ++ goto err1; ++ ++ tp->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL); ++ if (!tp->intr_buff) ++ goto err1; ++ ++ tp->intr_interval = (int)ep_intr->desc.bInterval; ++ usb_fill_int_urb(tp->intr_urb, tp->udev, usb_rcvintpipe(tp->udev, 3), ++ tp->intr_buff, INTBUFSIZE, intr_callback, ++ tp, tp->intr_interval); ++ ++ return 0; ++ ++err1: ++ free_all_mem(tp); ++ return -ENOMEM; ++} ++ ++static struct tx_agg *r8152_get_tx_agg(struct r8152 *tp) ++{ ++ struct tx_agg *agg = NULL; ++ unsigned long flags; ++ ++ if (list_empty(&tp->tx_free)) ++ return NULL; ++ ++ spin_lock_irqsave(&tp->tx_lock, flags); ++ if (!list_empty(&tp->tx_free)) { ++ struct list_head *cursor; ++ ++ cursor = tp->tx_free.next; ++ list_del_init(cursor); ++ agg = list_entry(cursor, struct tx_agg, list); ++ } ++ spin_unlock_irqrestore(&tp->tx_lock, flags); ++ ++ return agg; ++} ++ ++static inline __be16 get_protocol(struct sk_buff *skb) ++{ ++ __be16 protocol; ++ ++ if (skb->protocol == htons(ETH_P_8021Q)) ++ protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; ++ else ++ protocol = skb->protocol; ++ ++ return protocol; ++} ++ ++/* r8152_csum_workaround() ++ * The hw limites the value the transport offset. When the offset is out of the ++ * range, calculate the checksum by sw. ++ */ ++static void r8152_csum_workaround(struct r8152 *tp, struct sk_buff *skb, ++ struct sk_buff_head *list) ++{ ++ if (skb_shinfo(skb)->gso_size) { ++ netdev_features_t features = tp->netdev->features; ++ struct sk_buff_head seg_list; ++ struct sk_buff *segs, *nskb; ++ ++ features &= ~(NETIF_F_SG | NETIF_F_IPV6_CSUM | NETIF_F_TSO6); ++ segs = skb_gso_segment(skb, features); ++ if (IS_ERR(segs) || !segs) ++ goto drop; ++ ++ __skb_queue_head_init(&seg_list); ++ ++ do { ++ nskb = segs; ++ segs = segs->next; ++ nskb->next = NULL; ++ __skb_queue_tail(&seg_list, nskb); ++ } while (segs); ++ ++ skb_queue_splice(&seg_list, list); ++ dev_kfree_skb(skb); ++ } else if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ if (skb_checksum_help(skb) < 0) ++ goto drop; ++ ++ __skb_queue_head(list, skb); ++ } else { ++ struct net_device_stats *stats; ++ ++drop: ++ stats = &tp->netdev->stats; ++ stats->tx_dropped++; ++ dev_kfree_skb(skb); ++ } ++} ++ ++/* msdn_giant_send_check() ++ * According to the document of microsoft, the TCP Pseudo Header excludes the ++ * packet length for IPv6 TCP large packets. ++ */ ++static int msdn_giant_send_check(struct sk_buff *skb) ++{ ++ const struct ipv6hdr *ipv6h; ++ struct tcphdr *th; ++ int ret; ++ ++ ret = skb_cow_head(skb, 0); ++ if (ret) ++ return ret; ++ ++ ipv6h = ipv6_hdr(skb); ++ th = tcp_hdr(skb); ++ ++ th->check = 0; ++ th->check = ~tcp_v6_check(0, &ipv6h->saddr, &ipv6h->daddr, 0); ++ ++ return ret; ++} ++ ++static inline void rtl_tx_vlan_tag(struct tx_desc *desc, struct sk_buff *skb) ++{ ++ if (vlan_tx_tag_present(skb)) { ++ u32 opts2; ++ ++ opts2 = TX_VLAN_TAG | swab16(vlan_tx_tag_get(skb)); ++ desc->opts2 |= cpu_to_le32(opts2); ++ } ++} ++ ++static inline void rtl_rx_vlan_tag(struct rx_desc *desc, struct sk_buff *skb) ++{ ++ u32 opts2 = le32_to_cpu(desc->opts2); ++ ++ if (opts2 & RX_VLAN_TAG) ++ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ++ swab16(opts2 & 0xffff)); ++} ++ ++static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, ++ struct sk_buff *skb, u32 len, u32 transport_offset) ++{ ++ u32 mss = skb_shinfo(skb)->gso_size; ++ u32 opts1, opts2 = 0; ++ int ret = TX_CSUM_SUCCESS; ++ ++ WARN_ON_ONCE(len > TX_LEN_MAX); ++ ++ opts1 = len | TX_FS | TX_LS; ++ ++ if (mss) { ++ if (transport_offset > GTTCPHO_MAX) { ++ netif_warn(tp, tx_err, tp->netdev, ++ "Invalid transport offset 0x%x for TSO\n", ++ transport_offset); ++ ret = TX_CSUM_TSO; ++ goto unavailable; ++ } ++ ++ switch (get_protocol(skb)) { ++ case htons(ETH_P_IP): ++ opts1 |= GTSENDV4; ++ break; ++ ++ case htons(ETH_P_IPV6): ++ if (msdn_giant_send_check(skb)) { ++ ret = TX_CSUM_TSO; ++ goto unavailable; ++ } ++ opts1 |= GTSENDV6; ++ break; ++ ++ default: ++ WARN_ON_ONCE(1); ++ break; ++ } ++ ++ opts1 |= transport_offset << GTTCPHO_SHIFT; ++ opts2 |= min(mss, MSS_MAX) << MSS_SHIFT; ++ } else if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ u8 ip_protocol; ++ ++ if (transport_offset > TCPHO_MAX) { ++ netif_warn(tp, tx_err, tp->netdev, ++ "Invalid transport offset 0x%x\n", ++ transport_offset); ++ ret = TX_CSUM_NONE; ++ goto unavailable; ++ } ++ ++ switch (get_protocol(skb)) { ++ case htons(ETH_P_IP): ++ opts2 |= IPV4_CS; ++ ip_protocol = ip_hdr(skb)->protocol; ++ break; ++ ++ case htons(ETH_P_IPV6): ++ opts2 |= IPV6_CS; ++ ip_protocol = ipv6_hdr(skb)->nexthdr; ++ break; ++ ++ default: ++ ip_protocol = IPPROTO_RAW; ++ break; ++ } ++ ++ if (ip_protocol == IPPROTO_TCP) ++ opts2 |= TCP_CS; ++ else if (ip_protocol == IPPROTO_UDP) ++ opts2 |= UDP_CS; ++ else ++ WARN_ON_ONCE(1); ++ ++ opts2 |= transport_offset << TCPHO_SHIFT; ++ } ++ ++ desc->opts2 = cpu_to_le32(opts2); ++ desc->opts1 = cpu_to_le32(opts1); ++ ++unavailable: ++ return ret; ++} ++ ++static int r8152_tx_agg_fill(struct r8152 *tp, struct tx_agg *agg) ++{ ++ struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue; ++ int remain, ret; ++ u8 *tx_data; ++ ++ __skb_queue_head_init(&skb_head); ++ spin_lock(&tx_queue->lock); ++ skb_queue_splice_init(tx_queue, &skb_head); ++ spin_unlock(&tx_queue->lock); ++ ++ tx_data = agg->head; ++ agg->skb_num = 0; ++ agg->skb_len = 0; ++ remain = agg_buf_sz; ++ ++ while (remain >= ETH_ZLEN + sizeof(struct tx_desc)) { ++ struct tx_desc *tx_desc; ++ struct sk_buff *skb; ++ unsigned int len; ++ u32 offset; ++ ++ skb = __skb_dequeue(&skb_head); ++ if (!skb) ++ break; ++ ++ len = skb->len + sizeof(*tx_desc); ++ ++ if (len > remain) { ++ __skb_queue_head(&skb_head, skb); ++ break; ++ } ++ ++ tx_data = tx_agg_align(tx_data); ++ tx_desc = (struct tx_desc *)tx_data; ++ ++ offset = (u32)skb_transport_offset(skb); ++ ++ if (r8152_tx_csum(tp, tx_desc, skb, skb->len, offset)) { ++ r8152_csum_workaround(tp, skb, &skb_head); ++ continue; ++ } ++ ++ rtl_tx_vlan_tag(tx_desc, skb); ++ ++ tx_data += sizeof(*tx_desc); ++ ++ len = skb->len; ++ if (skb_copy_bits(skb, 0, tx_data, len) < 0) { ++ struct net_device_stats *stats = &tp->netdev->stats; ++ ++ stats->tx_dropped++; ++ dev_kfree_skb_any(skb); ++ tx_data -= sizeof(*tx_desc); ++ continue; ++ } ++ ++ tx_data += len; ++ agg->skb_len += len; ++ agg->skb_num++; ++ ++ dev_kfree_skb_any(skb); ++ ++ remain = agg_buf_sz - (int)(tx_agg_align(tx_data) - agg->head); ++ } ++ ++ if (!skb_queue_empty(&skb_head)) { ++ spin_lock(&tx_queue->lock); ++ skb_queue_splice(&skb_head, tx_queue); ++ spin_unlock(&tx_queue->lock); ++ } ++ ++ netif_tx_lock(tp->netdev); ++ ++ if (netif_queue_stopped(tp->netdev) && ++ skb_queue_len(&tp->tx_queue) < tp->tx_qlen) ++ netif_wake_queue(tp->netdev); ++ ++ netif_tx_unlock(tp->netdev); ++ ++ ret = usb_autopm_get_interface_async(tp->intf); ++ if (ret < 0) ++ goto out_tx_fill; ++ ++ usb_fill_bulk_urb(agg->urb, tp->udev, usb_sndbulkpipe(tp->udev, 2), ++ agg->head, (int)(tx_data - (u8 *)agg->head), ++ (usb_complete_t)write_bulk_callback, agg); ++ ++ ret = usb_submit_urb(agg->urb, GFP_ATOMIC); ++ if (ret < 0) ++ usb_autopm_put_interface_async(tp->intf); ++ ++out_tx_fill: ++ return ret; ++} ++ ++static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc) ++{ ++ u8 checksum = CHECKSUM_NONE; ++ u32 opts2, opts3; ++ ++ if (tp->version == RTL_VER_01) ++ goto return_result; ++ ++ opts2 = le32_to_cpu(rx_desc->opts2); ++ opts3 = le32_to_cpu(rx_desc->opts3); ++ ++ if (opts2 & RD_IPV4_CS) { ++ if (opts3 & IPF) ++ checksum = CHECKSUM_NONE; ++ else if ((opts2 & RD_UDP_CS) && (opts3 & UDPF)) ++ checksum = CHECKSUM_NONE; ++ else if ((opts2 & RD_TCP_CS) && (opts3 & TCPF)) ++ checksum = CHECKSUM_NONE; ++ else ++ checksum = CHECKSUM_UNNECESSARY; ++ } else if (RD_IPV6_CS) { ++ if ((opts2 & RD_UDP_CS) && !(opts3 & UDPF)) ++ checksum = CHECKSUM_UNNECESSARY; ++ else if ((opts2 & RD_TCP_CS) && !(opts3 & TCPF)) ++ checksum = CHECKSUM_UNNECESSARY; ++ } ++ ++return_result: ++ return checksum; ++} ++ ++static void rx_bottom(struct r8152 *tp) ++{ ++ unsigned long flags; ++ struct list_head *cursor, *next, rx_queue; ++ ++ if (list_empty(&tp->rx_done)) ++ return; ++ ++ INIT_LIST_HEAD(&rx_queue); ++ spin_lock_irqsave(&tp->rx_lock, flags); ++ list_splice_init(&tp->rx_done, &rx_queue); ++ spin_unlock_irqrestore(&tp->rx_lock, flags); ++ ++ list_for_each_safe(cursor, next, &rx_queue) { ++ struct rx_desc *rx_desc; ++ struct rx_agg *agg; ++ int len_used = 0; ++ struct urb *urb; ++ u8 *rx_data; ++ int ret; ++ ++ list_del_init(cursor); ++ ++ agg = list_entry(cursor, struct rx_agg, list); ++ urb = agg->urb; ++ if (urb->actual_length < ETH_ZLEN) ++ goto submit; ++ ++ rx_desc = agg->head; ++ rx_data = agg->head; ++ len_used += sizeof(struct rx_desc); ++ ++ while (urb->actual_length > len_used) { ++ struct net_device *netdev = tp->netdev; ++ struct net_device_stats *stats = &netdev->stats; ++ unsigned int pkt_len; ++ struct sk_buff *skb; ++ ++ pkt_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; ++ if (pkt_len < ETH_ZLEN) ++ break; ++ ++ len_used += pkt_len; ++ if (urb->actual_length < len_used) ++ break; ++ ++ pkt_len -= CRC_SIZE; ++ rx_data += sizeof(struct rx_desc); ++ ++ skb = netdev_alloc_skb_ip_align(netdev, pkt_len); ++ if (!skb) { ++ stats->rx_dropped++; ++ goto find_next_rx; ++ } ++ ++ skb->ip_summed = r8152_rx_csum(tp, rx_desc); ++ memcpy(skb->data, rx_data, pkt_len); ++ skb_put(skb, pkt_len); ++ skb->protocol = eth_type_trans(skb, netdev); ++ rtl_rx_vlan_tag(rx_desc, skb); ++ netif_receive_skb(skb); ++ stats->rx_packets++; ++ stats->rx_bytes += pkt_len; ++ ++find_next_rx: ++ rx_data = rx_agg_align(rx_data + pkt_len + CRC_SIZE); ++ rx_desc = (struct rx_desc *)rx_data; ++ len_used = (int)(rx_data - (u8 *)agg->head); ++ len_used += sizeof(struct rx_desc); ++ } ++ ++submit: ++ ret = r8152_submit_rx(tp, agg, GFP_ATOMIC); ++ if (ret && ret != -ENODEV) { ++ spin_lock_irqsave(&tp->rx_lock, flags); ++ list_add_tail(&agg->list, &tp->rx_done); ++ spin_unlock_irqrestore(&tp->rx_lock, flags); ++ tasklet_schedule(&tp->tl); ++ } ++ } ++} ++ ++static void tx_bottom(struct r8152 *tp) ++{ ++ int res; ++ ++ do { ++ struct tx_agg *agg; ++ ++ if (skb_queue_empty(&tp->tx_queue)) ++ break; ++ ++ agg = r8152_get_tx_agg(tp); ++ if (!agg) ++ break; ++ ++ res = r8152_tx_agg_fill(tp, agg); ++ if (res) { ++ struct net_device *netdev = tp->netdev; ++ ++ if (res == -ENODEV) { ++ netif_device_detach(netdev); ++ } else { ++ struct net_device_stats *stats = &netdev->stats; ++ unsigned long flags; ++ ++ netif_warn(tp, tx_err, netdev, ++ "failed tx_urb %d\n", res); ++ stats->tx_dropped += agg->skb_num; ++ ++ spin_lock_irqsave(&tp->tx_lock, flags); ++ list_add_tail(&agg->list, &tp->tx_free); ++ spin_unlock_irqrestore(&tp->tx_lock, flags); ++ } ++ } ++ } while (res == 0); ++} ++ ++static void bottom_half(unsigned long data) ++{ ++ struct r8152 *tp; ++ ++ tp = (struct r8152 *)data; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ if (!test_bit(WORK_ENABLE, &tp->flags)) ++ return; ++ ++ /* When link down, the driver would cancel all bulks. */ ++ /* This avoid the re-submitting bulk */ ++ if (!netif_carrier_ok(tp->netdev)) ++ return; ++ ++ rx_bottom(tp); ++ tx_bottom(tp); ++} ++ ++static ++int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags) ++{ ++ usb_fill_bulk_urb(agg->urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1), ++ agg->head, agg_buf_sz, ++ (usb_complete_t)read_bulk_callback, agg); ++ ++ return usb_submit_urb(agg->urb, mem_flags); ++} ++ ++static void rtl_drop_queued_tx(struct r8152 *tp) ++{ ++ struct net_device_stats *stats = &tp->netdev->stats; ++ struct sk_buff_head skb_head, *tx_queue = &tp->tx_queue; ++ struct sk_buff *skb; ++ ++ if (skb_queue_empty(tx_queue)) ++ return; ++ ++ __skb_queue_head_init(&skb_head); ++ spin_lock_bh(&tx_queue->lock); ++ skb_queue_splice_init(tx_queue, &skb_head); ++ spin_unlock_bh(&tx_queue->lock); ++ ++ while ((skb = __skb_dequeue(&skb_head))) { ++ dev_kfree_skb(skb); ++ stats->tx_dropped++; ++ } ++} ++ ++static void rtl8152_tx_timeout(struct net_device *netdev) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ int i; ++ ++ netif_warn(tp, tx_err, netdev, "Tx timeout\n"); ++ for (i = 0; i < RTL8152_MAX_TX; i++) ++ usb_unlink_urb(tp->tx_info[i].urb); ++} ++ ++static void rtl8152_set_rx_mode(struct net_device *netdev) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ ++ if (tp->speed & LINK_STATUS) { ++ set_bit(RTL8152_SET_RX_MODE, &tp->flags); ++ schedule_delayed_work(&tp->schedule, 0); ++ } ++} ++ ++static void _rtl8152_set_rx_mode(struct net_device *netdev) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ u32 mc_filter[2]; /* Multicast hash filter */ ++ __le32 tmp[2]; ++ u32 ocp_data; ++ ++ clear_bit(RTL8152_SET_RX_MODE, &tp->flags); ++ netif_stop_queue(netdev); ++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ++ ocp_data &= ~RCR_ACPT_ALL; ++ ocp_data |= RCR_AB | RCR_APM; ++ ++ if (netdev->flags & IFF_PROMISC) { ++ /* Unconditionally log net taps. */ ++ netif_notice(tp, link, netdev, "Promiscuous mode enabled\n"); ++ ocp_data |= RCR_AM | RCR_AAP; ++ mc_filter[1] = 0xffffffff; ++ mc_filter[0] = 0xffffffff; ++ } else if ((netdev_mc_count(netdev) > multicast_filter_limit) || ++ (netdev->flags & IFF_ALLMULTI)) { ++ /* Too many to filter perfectly -- accept all multicasts. */ ++ ocp_data |= RCR_AM; ++ mc_filter[1] = 0xffffffff; ++ mc_filter[0] = 0xffffffff; ++ } else { ++ struct netdev_hw_addr *ha; ++ ++ mc_filter[1] = 0; ++ mc_filter[0] = 0; ++ netdev_for_each_mc_addr(ha, netdev) { ++ int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; ++ ++ mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); ++ ocp_data |= RCR_AM; ++ } ++ } ++ ++ tmp[0] = __cpu_to_le32(swab32(mc_filter[1])); ++ tmp[1] = __cpu_to_le32(swab32(mc_filter[0])); ++ ++ pla_ocp_write(tp, PLA_MAR, BYTE_EN_DWORD, sizeof(tmp), tmp); ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); ++ netif_wake_queue(netdev); ++} ++ ++static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ ++ skb_tx_timestamp(skb); ++ ++ skb_queue_tail(&tp->tx_queue, skb); ++ ++ if (!list_empty(&tp->tx_free)) { ++ if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { ++ set_bit(SCHEDULE_TASKLET, &tp->flags); ++ schedule_delayed_work(&tp->schedule, 0); ++ } else { ++ usb_mark_last_busy(tp->udev); ++ tasklet_schedule(&tp->tl); ++ } ++ } else if (skb_queue_len(&tp->tx_queue) > tp->tx_qlen) { ++ netif_stop_queue(netdev); ++ } ++ ++ return NETDEV_TX_OK; ++} ++ ++static void r8152b_reset_packet_filter(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC); ++ ocp_data &= ~FMC_FCR_MCU_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); ++ ocp_data |= FMC_FCR_MCU_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); ++} ++ ++static void rtl8152_nic_reset(struct r8152 *tp) ++{ ++ int i; ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, CR_RST); ++ ++ for (i = 0; i < 1000; i++) { ++ if (!(ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR) & CR_RST)) ++ break; ++ usleep_range(100, 400); ++ } ++} ++ ++static void set_tx_qlen(struct r8152 *tp) ++{ ++ struct net_device *netdev = tp->netdev; ++ ++ tp->tx_qlen = agg_buf_sz / (netdev->mtu + VLAN_ETH_HLEN + VLAN_HLEN + ++ sizeof(struct tx_desc)); ++} ++ ++static inline u8 rtl8152_get_speed(struct r8152 *tp) ++{ ++ return ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS); ++} ++ ++static void rtl_set_eee_plus(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u8 speed; ++ ++ speed = rtl8152_get_speed(tp); ++ if (speed & _10bps) { ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); ++ ocp_data |= EEEP_CR_EEEP_TX; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); ++ } else { ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); ++ ocp_data &= ~EEEP_CR_EEEP_TX; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); ++ } ++} ++ ++static void rxdy_gated_en(struct r8152 *tp, bool enable) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); ++ if (enable) ++ ocp_data |= RXDY_GATED_EN; ++ else ++ ocp_data &= ~RXDY_GATED_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); ++} ++ ++static int rtl_start_rx(struct r8152 *tp) ++{ ++ int i, ret = 0; ++ ++ INIT_LIST_HEAD(&tp->rx_done); ++ for (i = 0; i < RTL8152_MAX_RX; i++) { ++ INIT_LIST_HEAD(&tp->rx_info[i].list); ++ ret = r8152_submit_rx(tp, &tp->rx_info[i], GFP_KERNEL); ++ if (ret) ++ break; ++ } ++ ++ return ret; ++} ++ ++static int rtl_stop_rx(struct r8152 *tp) ++{ ++ int i; ++ ++ for (i = 0; i < RTL8152_MAX_RX; i++) ++ usb_kill_urb(tp->rx_info[i].urb); ++ ++ return 0; ++} ++ ++static int rtl_enable(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ ++ r8152b_reset_packet_filter(tp); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR); ++ ocp_data |= CR_RE | CR_TE; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data); ++ ++ rxdy_gated_en(tp, false); ++ ++ return rtl_start_rx(tp); ++} ++ ++static int rtl8152_enable(struct r8152 *tp) ++{ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return -ENODEV; ++ ++ set_tx_qlen(tp); ++ rtl_set_eee_plus(tp); ++ ++ return rtl_enable(tp); ++} ++ ++static void r8153_set_rx_agg(struct r8152 *tp) ++{ ++ u8 speed; ++ ++ speed = rtl8152_get_speed(tp); ++ if (speed & _1000bps) { ++ if (tp->udev->speed == USB_SPEED_SUPER) { ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, ++ RX_THR_SUPPER); ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_EARLY_AGG, ++ EARLY_AGG_SUPPER); ++ } else { ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, ++ RX_THR_HIGH); ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_EARLY_AGG, ++ EARLY_AGG_HIGH); ++ } ++ } else { ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_SLOW); ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_EARLY_AGG, ++ EARLY_AGG_SLOW); ++ } ++} ++ ++static int rtl8153_enable(struct r8152 *tp) ++{ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return -ENODEV; ++ ++ set_tx_qlen(tp); ++ rtl_set_eee_plus(tp); ++ r8153_set_rx_agg(tp); ++ ++ return rtl_enable(tp); ++} ++ ++static void rtl_disable(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ int i; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) { ++ rtl_drop_queued_tx(tp); ++ return; ++ } ++ ++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ++ ocp_data &= ~RCR_ACPT_ALL; ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); ++ ++ rtl_drop_queued_tx(tp); ++ ++ for (i = 0; i < RTL8152_MAX_TX; i++) ++ usb_kill_urb(tp->tx_info[i].urb); ++ ++ rxdy_gated_en(tp, true); ++ ++ for (i = 0; i < 1000; i++) { ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ if ((ocp_data & FIFO_EMPTY) == FIFO_EMPTY) ++ break; ++ usleep_range(1000, 2000); ++ } ++ ++ for (i = 0; i < 1000; i++) { ++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0) & TCR0_TX_EMPTY) ++ break; ++ usleep_range(1000, 2000); ++ } ++ ++ rtl_stop_rx(tp); ++ ++ rtl8152_nic_reset(tp); ++} ++ ++static void r8152_power_cut_en(struct r8152 *tp, bool enable) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); ++ if (enable) ++ ocp_data |= POWER_CUT; ++ else ++ ocp_data &= ~POWER_CUT; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS); ++ ocp_data &= ~RESUME_INDICATE; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data); ++} ++ ++static void rtl_rx_vlan_en(struct r8152 *tp, bool enable) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); ++ if (enable) ++ ocp_data |= CPCR_RX_VLAN; ++ else ++ ocp_data &= ~CPCR_RX_VLAN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); ++} ++ ++static int rtl8152_set_features(struct net_device *dev, ++ netdev_features_t features) ++{ ++ netdev_features_t changed = features ^ dev->features; ++ struct r8152 *tp = netdev_priv(dev); ++ int ret; ++ ++ ret = usb_autopm_get_interface(tp->intf); ++ if (ret < 0) ++ goto out; ++ ++ mutex_lock(&tp->control); ++ ++ if (changed & NETIF_F_HW_VLAN_CTAG_RX) { ++ if (features & NETIF_F_HW_VLAN_CTAG_RX) ++ rtl_rx_vlan_en(tp, true); ++ else ++ rtl_rx_vlan_en(tp, false); ++ } ++ ++ mutex_unlock(&tp->control); ++ ++ usb_autopm_put_interface(tp->intf); ++ ++out: ++ return ret; ++} ++ ++#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) ++ ++static u32 __rtl_get_wol(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u32 wolopts = 0; ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5); ++ if (!(ocp_data & LAN_WAKE_EN)) ++ return 0; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); ++ if (ocp_data & LINK_ON_WAKE_EN) ++ wolopts |= WAKE_PHY; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5); ++ if (ocp_data & UWF_EN) ++ wolopts |= WAKE_UCAST; ++ if (ocp_data & BWF_EN) ++ wolopts |= WAKE_BCAST; ++ if (ocp_data & MWF_EN) ++ wolopts |= WAKE_MCAST; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); ++ if (ocp_data & MAGIC_EN) ++ wolopts |= WAKE_MAGIC; ++ ++ return wolopts; ++} ++ ++static void __rtl_set_wol(struct r8152 *tp, u32 wolopts) ++{ ++ u32 ocp_data; ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); ++ ocp_data &= ~LINK_ON_WAKE_EN; ++ if (wolopts & WAKE_PHY) ++ ocp_data |= LINK_ON_WAKE_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5); ++ ocp_data &= ~(UWF_EN | BWF_EN | MWF_EN | LAN_WAKE_EN); ++ if (wolopts & WAKE_UCAST) ++ ocp_data |= UWF_EN; ++ if (wolopts & WAKE_BCAST) ++ ocp_data |= BWF_EN; ++ if (wolopts & WAKE_MCAST) ++ ocp_data |= MWF_EN; ++ if (wolopts & WAKE_ANY) ++ ocp_data |= LAN_WAKE_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data); ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL); ++ ocp_data &= ~MAGIC_EN; ++ if (wolopts & WAKE_MAGIC) ++ ocp_data |= MAGIC_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CFG_WOL, ocp_data); ++ ++ if (wolopts & WAKE_ANY) ++ device_set_wakeup_enable(&tp->udev->dev, true); ++ else ++ device_set_wakeup_enable(&tp->udev->dev, false); ++} ++ ++static void rtl_runtime_suspend_enable(struct r8152 *tp, bool enable) ++{ ++ if (enable) { ++ u32 ocp_data; ++ ++ __rtl_set_wol(tp, WAKE_ANY); ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34); ++ ocp_data |= LINK_OFF_WAKE_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data); ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ++ } else { ++ __rtl_set_wol(tp, tp->saved_wolopts); ++ } ++} ++ ++static void rtl_phy_reset(struct r8152 *tp) ++{ ++ u16 data; ++ int i; ++ ++ clear_bit(PHY_RESET, &tp->flags); ++ ++ data = r8152_mdio_read(tp, MII_BMCR); ++ ++ /* don't reset again before the previous one complete */ ++ if (data & BMCR_RESET) ++ return; ++ ++ data |= BMCR_RESET; ++ r8152_mdio_write(tp, MII_BMCR, data); ++ ++ for (i = 0; i < 50; i++) { ++ msleep(20); ++ if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0) ++ break; ++ } ++} ++ ++static void r8153_teredo_off(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG); ++ ocp_data &= ~(TEREDO_SEL | TEREDO_RS_EVENT_MASK | OOB_TEREDO_EN); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data); ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_REALWOW_TIMER, 0); ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0); ++} ++ ++static void r8152b_disable_aldps(struct r8152 *tp) ++{ ++ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE); ++ msleep(20); ++} ++ ++static inline void r8152b_enable_aldps(struct r8152 *tp) ++{ ++ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | ++ LINKENA | DIS_SDSAVE); ++} ++ ++static void rtl8152_disable(struct r8152 *tp) ++{ ++ r8152b_disable_aldps(tp); ++ rtl_disable(tp); ++ r8152b_enable_aldps(tp); ++} ++ ++static void r8152b_hw_phy_cfg(struct r8152 *tp) ++{ ++ u16 data; ++ ++ data = r8152_mdio_read(tp, MII_BMCR); ++ if (data & BMCR_PDOWN) { ++ data &= ~BMCR_PDOWN; ++ r8152_mdio_write(tp, MII_BMCR, data); ++ } ++ ++ set_bit(PHY_RESET, &tp->flags); ++} ++ ++static void r8152b_exit_oob(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ int i; ++ ++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ++ ocp_data &= ~RCR_ACPT_ALL; ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); ++ ++ rxdy_gated_en(tp, true); ++ r8153_teredo_off(tp); ++ r8152b_hw_phy_cfg(tp); ++ ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, 0x00); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ ocp_data &= ~NOW_IS_OOB; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ++ ocp_data &= ~MCU_BORW_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); ++ ++ for (i = 0; i < 1000; i++) { ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ if (ocp_data & LINK_LIST_READY) ++ break; ++ usleep_range(1000, 2000); ++ } ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ++ ocp_data |= RE_INIT_LL; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); ++ ++ for (i = 0; i < 1000; i++) { ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ if (ocp_data & LINK_LIST_READY) ++ break; ++ usleep_range(1000, 2000); ++ } ++ ++ rtl8152_nic_reset(tp); ++ ++ /* rx share fifo credit full threshold */ ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); ++ ++ if (tp->udev->speed == USB_SPEED_FULL || ++ tp->udev->speed == USB_SPEED_LOW) { ++ /* rx share fifo credit near full threshold */ ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, ++ RXFIFO_THR2_FULL); ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, ++ RXFIFO_THR3_FULL); ++ } else { ++ /* rx share fifo credit near full threshold */ ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, ++ RXFIFO_THR2_HIGH); ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, ++ RXFIFO_THR3_HIGH); ++ } ++ ++ /* TX share fifo free credit full threshold */ ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL); ++ ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD); ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH); ++ ocp_write_dword(tp, MCU_TYPE_USB, USB_TX_DMA, ++ TEST_MODE_DISABLE | TX_SIZE_ADJUST1); ++ ++ rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); ++ ocp_data |= TCR0_AUTO_FIFO; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); ++} ++ ++static void r8152b_enter_oob(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ int i; ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ ocp_data &= ~NOW_IS_OOB; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ++ ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_OOB); ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_OOB); ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_OOB); ++ ++ rtl_disable(tp); ++ ++ for (i = 0; i < 1000; i++) { ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ if (ocp_data & LINK_LIST_READY) ++ break; ++ usleep_range(1000, 2000); ++ } ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ++ ocp_data |= RE_INIT_LL; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); ++ ++ for (i = 0; i < 1000; i++) { ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ if (ocp_data & LINK_LIST_READY) ++ break; ++ usleep_range(1000, 2000); ++ } ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); ++ ++ rtl_rx_vlan_en(tp, true); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR); ++ ocp_data |= ALDPS_PROXY_MODE; ++ ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ++ ++ rxdy_gated_en(tp, false); ++ ++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ++ ocp_data |= RCR_APM | RCR_AM | RCR_AB; ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); ++} ++ ++static void r8153_hw_phy_cfg(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u16 data; ++ ++ ocp_reg_write(tp, OCP_ADC_CFG, CKADSEL_L | ADC_EN | EN_EMI_L); ++ data = r8152_mdio_read(tp, MII_BMCR); ++ if (data & BMCR_PDOWN) { ++ data &= ~BMCR_PDOWN; ++ r8152_mdio_write(tp, MII_BMCR, data); ++ } ++ ++ if (tp->version == RTL_VER_03) { ++ data = ocp_reg_read(tp, OCP_EEE_CFG); ++ data &= ~CTAP_SHORT_EN; ++ ocp_reg_write(tp, OCP_EEE_CFG, data); ++ } ++ ++ data = ocp_reg_read(tp, OCP_POWER_CFG); ++ data |= EEE_CLKDIV_EN; ++ ocp_reg_write(tp, OCP_POWER_CFG, data); ++ ++ data = ocp_reg_read(tp, OCP_DOWN_SPEED); ++ data |= EN_10M_BGOFF; ++ ocp_reg_write(tp, OCP_DOWN_SPEED, data); ++ data = ocp_reg_read(tp, OCP_POWER_CFG); ++ data |= EN_10M_PLLOFF; ++ ocp_reg_write(tp, OCP_POWER_CFG, data); ++ data = sram_read(tp, SRAM_IMPEDANCE); ++ data &= ~RX_DRIVING_MASK; ++ sram_write(tp, SRAM_IMPEDANCE, data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); ++ ocp_data |= PFM_PWM_SWITCH; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); ++ ++ data = sram_read(tp, SRAM_LPF_CFG); ++ data |= LPF_AUTO_TUNE; ++ sram_write(tp, SRAM_LPF_CFG, data); ++ ++ data = sram_read(tp, SRAM_10M_AMP1); ++ data |= GDAC_IB_UPALL; ++ sram_write(tp, SRAM_10M_AMP1, data); ++ data = sram_read(tp, SRAM_10M_AMP2); ++ data |= AMP_DN; ++ sram_write(tp, SRAM_10M_AMP2, data); ++ ++ set_bit(PHY_RESET, &tp->flags); ++} ++ ++static void r8153_u1u2en(struct r8152 *tp, bool enable) ++{ ++ u8 u1u2[8]; ++ ++ if (enable) ++ memset(u1u2, 0xff, sizeof(u1u2)); ++ else ++ memset(u1u2, 0x00, sizeof(u1u2)); ++ ++ usb_ocp_write(tp, USB_TOLERANCE, BYTE_EN_SIX_BYTES, sizeof(u1u2), u1u2); ++} ++ ++static void r8153_u2p3en(struct r8152 *tp, bool enable) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL); ++ if (enable) ++ ocp_data |= U2P3_ENABLE; ++ else ++ ocp_data &= ~U2P3_ENABLE; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data); ++} ++ ++static void r8153_power_cut_en(struct r8152 *tp, bool enable) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_POWER_CUT); ++ if (enable) ++ ocp_data |= PWR_EN | PHASE2_EN; ++ else ++ ocp_data &= ~(PWR_EN | PHASE2_EN); ++ ocp_write_word(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); ++ ocp_data &= ~PCUT_STATUS; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); ++} ++ ++static void r8153_first_init(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ int i; ++ ++ rxdy_gated_en(tp, true); ++ r8153_teredo_off(tp); ++ ++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ++ ocp_data &= ~RCR_ACPT_ALL; ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); ++ ++ r8153_hw_phy_cfg(tp); ++ ++ rtl8152_nic_reset(tp); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ ocp_data &= ~NOW_IS_OOB; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ++ ocp_data &= ~MCU_BORW_EN; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); ++ ++ for (i = 0; i < 1000; i++) { ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ if (ocp_data & LINK_LIST_READY) ++ break; ++ usleep_range(1000, 2000); ++ } ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ++ ocp_data |= RE_INIT_LL; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); ++ ++ for (i = 0; i < 1000; i++) { ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ if (ocp_data & LINK_LIST_READY) ++ break; ++ usleep_range(1000, 2000); ++ } ++ ++ rtl_rx_vlan_en(tp, tp->netdev->features & NETIF_F_HW_VLAN_CTAG_RX); ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8153_RMS); ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); ++ ocp_data |= TCR0_AUTO_FIFO; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); ++ ++ rtl8152_nic_reset(tp); ++ ++ /* rx share fifo credit full threshold */ ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, RXFIFO_THR1_NORMAL); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, RXFIFO_THR2_NORMAL); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, RXFIFO_THR3_NORMAL); ++ /* TX share fifo free credit full threshold */ ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, TXFIFO_THR_NORMAL2); ++ ++ /* rx aggregation */ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); ++ ocp_data &= ~RX_AGG_DISABLE; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); ++} ++ ++static void r8153_enter_oob(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ int i; ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ ocp_data &= ~NOW_IS_OOB; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ++ ++ rtl_disable(tp); ++ ++ for (i = 0; i < 1000; i++) { ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ if (ocp_data & LINK_LIST_READY) ++ break; ++ usleep_range(1000, 2000); ++ } ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); ++ ocp_data |= RE_INIT_LL; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); ++ ++ for (i = 0; i < 1000; i++) { ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ if (ocp_data & LINK_LIST_READY) ++ break; ++ usleep_range(1000, 2000); ++ } ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8153_RMS); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG); ++ ocp_data &= ~TEREDO_WAKE_MASK; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data); ++ ++ rtl_rx_vlan_en(tp, true); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR); ++ ocp_data |= ALDPS_PROXY_MODE; ++ ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ++ ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; ++ ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); ++ ++ rxdy_gated_en(tp, false); ++ ++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ++ ocp_data |= RCR_APM | RCR_AM | RCR_AB; ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); ++} ++ ++static void r8153_disable_aldps(struct r8152 *tp) ++{ ++ u16 data; ++ ++ data = ocp_reg_read(tp, OCP_POWER_CFG); ++ data &= ~EN_ALDPS; ++ ocp_reg_write(tp, OCP_POWER_CFG, data); ++ msleep(20); ++} ++ ++static void r8153_enable_aldps(struct r8152 *tp) ++{ ++ u16 data; ++ ++ data = ocp_reg_read(tp, OCP_POWER_CFG); ++ data |= EN_ALDPS; ++ ocp_reg_write(tp, OCP_POWER_CFG, data); ++} ++ ++static void rtl8153_disable(struct r8152 *tp) ++{ ++ r8153_disable_aldps(tp); ++ rtl_disable(tp); ++ r8153_enable_aldps(tp); ++} ++ ++static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) ++{ ++ u16 bmcr, anar, gbcr; ++ int ret = 0; ++ ++ cancel_delayed_work_sync(&tp->schedule); ++ anar = r8152_mdio_read(tp, MII_ADVERTISE); ++ anar &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | ++ ADVERTISE_100HALF | ADVERTISE_100FULL); ++ if (tp->mii.supports_gmii) { ++ gbcr = r8152_mdio_read(tp, MII_CTRL1000); ++ gbcr &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); ++ } else { ++ gbcr = 0; ++ } ++ ++ if (autoneg == AUTONEG_DISABLE) { ++ if (speed == SPEED_10) { ++ bmcr = 0; ++ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; ++ } else if (speed == SPEED_100) { ++ bmcr = BMCR_SPEED100; ++ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; ++ } else if (speed == SPEED_1000 && tp->mii.supports_gmii) { ++ bmcr = BMCR_SPEED1000; ++ gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; ++ } else { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (duplex == DUPLEX_FULL) ++ bmcr |= BMCR_FULLDPLX; ++ } else { ++ if (speed == SPEED_10) { ++ if (duplex == DUPLEX_FULL) ++ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; ++ else ++ anar |= ADVERTISE_10HALF; ++ } else if (speed == SPEED_100) { ++ if (duplex == DUPLEX_FULL) { ++ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; ++ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; ++ } else { ++ anar |= ADVERTISE_10HALF; ++ anar |= ADVERTISE_100HALF; ++ } ++ } else if (speed == SPEED_1000 && tp->mii.supports_gmii) { ++ if (duplex == DUPLEX_FULL) { ++ anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; ++ anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; ++ gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; ++ } else { ++ anar |= ADVERTISE_10HALF; ++ anar |= ADVERTISE_100HALF; ++ gbcr |= ADVERTISE_1000HALF; ++ } ++ } else { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ bmcr = BMCR_ANENABLE | BMCR_ANRESTART; ++ } ++ ++ if (test_bit(PHY_RESET, &tp->flags)) ++ bmcr |= BMCR_RESET; ++ ++ if (tp->mii.supports_gmii) ++ r8152_mdio_write(tp, MII_CTRL1000, gbcr); ++ ++ r8152_mdio_write(tp, MII_ADVERTISE, anar); ++ r8152_mdio_write(tp, MII_BMCR, bmcr); ++ ++ if (test_bit(PHY_RESET, &tp->flags)) { ++ int i; ++ ++ clear_bit(PHY_RESET, &tp->flags); ++ for (i = 0; i < 50; i++) { ++ msleep(20); ++ if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0) ++ break; ++ } ++ } ++ ++out: ++ ++ return ret; ++} ++ ++static void rtl8152_up(struct r8152 *tp) ++{ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ r8152b_disable_aldps(tp); ++ r8152b_exit_oob(tp); ++ r8152b_enable_aldps(tp); ++} ++ ++static void rtl8152_down(struct r8152 *tp) ++{ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) { ++ rtl_drop_queued_tx(tp); ++ return; ++ } ++ ++ r8152_power_cut_en(tp, false); ++ r8152b_disable_aldps(tp); ++ r8152b_enter_oob(tp); ++ r8152b_enable_aldps(tp); ++} ++ ++static void rtl8153_up(struct r8152 *tp) ++{ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ r8153_disable_aldps(tp); ++ r8153_first_init(tp); ++ r8153_enable_aldps(tp); ++} ++ ++static void rtl8153_down(struct r8152 *tp) ++{ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) { ++ rtl_drop_queued_tx(tp); ++ return; ++ } ++ ++ r8153_u1u2en(tp, false); ++ r8153_power_cut_en(tp, false); ++ r8153_disable_aldps(tp); ++ r8153_enter_oob(tp); ++ r8153_enable_aldps(tp); ++} ++ ++static void set_carrier(struct r8152 *tp) ++{ ++ struct net_device *netdev = tp->netdev; ++ u8 speed; ++ ++ clear_bit(RTL8152_LINK_CHG, &tp->flags); ++ speed = rtl8152_get_speed(tp); ++ ++ if (speed & LINK_STATUS) { ++ if (!(tp->speed & LINK_STATUS)) { ++ tp->rtl_ops.enable(tp); ++ set_bit(RTL8152_SET_RX_MODE, &tp->flags); ++ netif_carrier_on(netdev); ++ } ++ } else { ++ if (tp->speed & LINK_STATUS) { ++ netif_carrier_off(netdev); ++ tasklet_disable(&tp->tl); ++ tp->rtl_ops.disable(tp); ++ tasklet_enable(&tp->tl); ++ } ++ } ++ tp->speed = speed; ++} ++ ++static void rtl_work_func_t(struct work_struct *work) ++{ ++ struct r8152 *tp = container_of(work, struct r8152, schedule.work); ++ ++ if (usb_autopm_get_interface(tp->intf) < 0) ++ return; ++ ++ if (!test_bit(WORK_ENABLE, &tp->flags)) ++ goto out1; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ goto out1; ++ ++ if (!mutex_trylock(&tp->control)) { ++ schedule_delayed_work(&tp->schedule, 0); ++ goto out1; ++ } ++ ++ if (test_bit(RTL8152_LINK_CHG, &tp->flags)) ++ set_carrier(tp); ++ ++ if (test_bit(RTL8152_SET_RX_MODE, &tp->flags)) ++ _rtl8152_set_rx_mode(tp->netdev); ++ ++ if (test_bit(SCHEDULE_TASKLET, &tp->flags) && ++ (tp->speed & LINK_STATUS)) { ++ clear_bit(SCHEDULE_TASKLET, &tp->flags); ++ tasklet_schedule(&tp->tl); ++ } ++ ++ if (test_bit(PHY_RESET, &tp->flags)) ++ rtl_phy_reset(tp); ++ ++ mutex_unlock(&tp->control); ++ ++out1: ++ usb_autopm_put_interface(tp->intf); ++} ++ ++static int rtl8152_open(struct net_device *netdev) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ int res = 0; ++ ++ res = alloc_all_mem(tp); ++ if (res) ++ goto out; ++ ++ /* set speed to 0 to avoid autoresume try to submit rx */ ++ tp->speed = 0; ++ ++ res = usb_autopm_get_interface(tp->intf); ++ if (res < 0) { ++ free_all_mem(tp); ++ goto out; ++ } ++ ++ mutex_lock(&tp->control); ++ ++ /* The WORK_ENABLE may be set when autoresume occurs */ ++ if (test_bit(WORK_ENABLE, &tp->flags)) { ++ clear_bit(WORK_ENABLE, &tp->flags); ++ usb_kill_urb(tp->intr_urb); ++ cancel_delayed_work_sync(&tp->schedule); ++ ++ /* disable the tx/rx, if the workqueue has enabled them. */ ++ if (tp->speed & LINK_STATUS) ++ tp->rtl_ops.disable(tp); ++ } ++ ++ tp->rtl_ops.up(tp); ++ ++ rtl8152_set_speed(tp, AUTONEG_ENABLE, ++ tp->mii.supports_gmii ? SPEED_1000 : SPEED_100, ++ DUPLEX_FULL); ++ tp->speed = 0; ++ netif_carrier_off(netdev); ++ netif_start_queue(netdev); ++ set_bit(WORK_ENABLE, &tp->flags); ++ ++ res = usb_submit_urb(tp->intr_urb, GFP_KERNEL); ++ if (res) { ++ if (res == -ENODEV) ++ netif_device_detach(tp->netdev); ++ netif_warn(tp, ifup, netdev, "intr_urb submit failed: %d\n", ++ res); ++ free_all_mem(tp); ++ } ++ ++ mutex_unlock(&tp->control); ++ ++ usb_autopm_put_interface(tp->intf); ++ ++out: ++ return res; ++} ++ ++static int rtl8152_close(struct net_device *netdev) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ int res = 0; ++ ++ clear_bit(WORK_ENABLE, &tp->flags); ++ usb_kill_urb(tp->intr_urb); ++ cancel_delayed_work_sync(&tp->schedule); ++ netif_stop_queue(netdev); ++ ++ res = usb_autopm_get_interface(tp->intf); ++ if (res < 0) { ++ rtl_drop_queued_tx(tp); ++ } else { ++ mutex_lock(&tp->control); ++ ++ /* The autosuspend may have been enabled and wouldn't ++ * be disable when autoresume occurs, because the ++ * netif_running() would be false. ++ */ ++ rtl_runtime_suspend_enable(tp, false); ++ ++ tasklet_disable(&tp->tl); ++ tp->rtl_ops.down(tp); ++ tasklet_enable(&tp->tl); ++ ++ mutex_unlock(&tp->control); ++ ++ usb_autopm_put_interface(tp->intf); ++ } ++ ++ free_all_mem(tp); ++ ++ return res; ++} ++ ++static inline void r8152_mmd_indirect(struct r8152 *tp, u16 dev, u16 reg) ++{ ++ ocp_reg_write(tp, OCP_EEE_AR, FUN_ADDR | dev); ++ ocp_reg_write(tp, OCP_EEE_DATA, reg); ++ ocp_reg_write(tp, OCP_EEE_AR, FUN_DATA | dev); ++} ++ ++static u16 r8152_mmd_read(struct r8152 *tp, u16 dev, u16 reg) ++{ ++ u16 data; ++ ++ r8152_mmd_indirect(tp, dev, reg); ++ data = ocp_reg_read(tp, OCP_EEE_DATA); ++ ocp_reg_write(tp, OCP_EEE_AR, 0x0000); ++ ++ return data; ++} ++ ++static void r8152_mmd_write(struct r8152 *tp, u16 dev, u16 reg, u16 data) ++{ ++ r8152_mmd_indirect(tp, dev, reg); ++ ocp_reg_write(tp, OCP_EEE_DATA, data); ++ ocp_reg_write(tp, OCP_EEE_AR, 0x0000); ++} ++ ++static void r8152_eee_en(struct r8152 *tp, bool enable) ++{ ++ u16 config1, config2, config3; ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); ++ config1 = ocp_reg_read(tp, OCP_EEE_CONFIG1) & ~sd_rise_time_mask; ++ config2 = ocp_reg_read(tp, OCP_EEE_CONFIG2); ++ config3 = ocp_reg_read(tp, OCP_EEE_CONFIG3) & ~fast_snr_mask; ++ ++ if (enable) { ++ ocp_data |= EEE_RX_EN | EEE_TX_EN; ++ config1 |= EEE_10_CAP | EEE_NWAY_EN | TX_QUIET_EN | RX_QUIET_EN; ++ config1 |= sd_rise_time(1); ++ config2 |= RG_DACQUIET_EN | RG_LDVQUIET_EN; ++ config3 |= fast_snr(42); ++ } else { ++ ocp_data &= ~(EEE_RX_EN | EEE_TX_EN); ++ config1 &= ~(EEE_10_CAP | EEE_NWAY_EN | TX_QUIET_EN | ++ RX_QUIET_EN); ++ config1 |= sd_rise_time(7); ++ config2 &= ~(RG_DACQUIET_EN | RG_LDVQUIET_EN); ++ config3 |= fast_snr(511); ++ } ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_CR, ocp_data); ++ ocp_reg_write(tp, OCP_EEE_CONFIG1, config1); ++ ocp_reg_write(tp, OCP_EEE_CONFIG2, config2); ++ ocp_reg_write(tp, OCP_EEE_CONFIG3, config3); ++} ++ ++static void r8152b_enable_eee(struct r8152 *tp) ++{ ++ r8152_eee_en(tp, true); ++ r8152_mmd_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, MDIO_EEE_100TX); ++} ++ ++static void r8153_eee_en(struct r8152 *tp, bool enable) ++{ ++ u32 ocp_data; ++ u16 config; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); ++ config = ocp_reg_read(tp, OCP_EEE_CFG); ++ ++ if (enable) { ++ ocp_data |= EEE_RX_EN | EEE_TX_EN; ++ config |= EEE10_EN; ++ } else { ++ ocp_data &= ~(EEE_RX_EN | EEE_TX_EN); ++ config &= ~EEE10_EN; ++ } ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEE_CR, ocp_data); ++ ocp_reg_write(tp, OCP_EEE_CFG, config); ++} ++ ++static void r8153_enable_eee(struct r8152 *tp) ++{ ++ r8153_eee_en(tp, true); ++ ocp_reg_write(tp, OCP_EEE_ADV, MDIO_EEE_1000T | MDIO_EEE_100TX); ++} ++ ++static void r8152b_enable_fc(struct r8152 *tp) ++{ ++ u16 anar; ++ ++ anar = r8152_mdio_read(tp, MII_ADVERTISE); ++ anar |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; ++ r8152_mdio_write(tp, MII_ADVERTISE, anar); ++} ++ ++static void rtl_tally_reset(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY); ++ ocp_data |= TALLY_RESET; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data); ++} ++ ++static void r8152b_init(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ r8152b_disable_aldps(tp); ++ ++ if (tp->version == RTL_VER_01) { ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); ++ ocp_data &= ~LED_MODE_MASK; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); ++ } ++ ++ r8152_power_cut_en(tp, false); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); ++ ocp_data |= TX_10M_IDLE_EN | PFM_PWM_SWITCH; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); ++ ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL); ++ ocp_data &= ~MCU_CLK_RATIO_MASK; ++ ocp_data |= MCU_CLK_RATIO | D3_CLK_GATED_EN; ++ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ocp_data); ++ ocp_data = GPHY_STS_MSK | SPEED_DOWN_MSK | ++ SPDWN_RXDV_MSK | SPDWN_LINKCHG_MSK; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data); ++ ++ r8152b_enable_eee(tp); ++ r8152b_enable_aldps(tp); ++ r8152b_enable_fc(tp); ++ rtl_tally_reset(tp); ++ ++ /* enable rx aggregation */ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); ++ ocp_data &= ~RX_AGG_DISABLE; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); ++} ++ ++static void r8153_init(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ int i; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ r8153_disable_aldps(tp); ++ r8153_u1u2en(tp, false); ++ ++ for (i = 0; i < 500; i++) { ++ if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) & ++ AUTOLOAD_DONE) ++ break; ++ msleep(20); ++ } ++ ++ for (i = 0; i < 500; i++) { ++ ocp_data = ocp_reg_read(tp, OCP_PHY_STATUS) & PHY_STAT_MASK; ++ if (ocp_data == PHY_STAT_LAN_ON || ocp_data == PHY_STAT_PWRDN) ++ break; ++ msleep(20); ++ } ++ ++ r8153_u2p3en(tp, false); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL); ++ ocp_data &= ~TIMER11_EN; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); ++ ocp_data &= ~LED_MODE_MASK; ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); ++ ++ ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL); ++ ocp_data &= ~LPM_TIMER_MASK; ++ if (tp->udev->speed == USB_SPEED_SUPER) ++ ocp_data |= LPM_TIMER_500US; ++ else ++ ocp_data |= LPM_TIMER_500MS; ++ ocp_write_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL, ocp_data); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2); ++ ocp_data &= ~SEN_VAL_MASK; ++ ocp_data |= SEN_VAL_NORMAL | SEL_RXIDLE; ++ ocp_write_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2, ocp_data); ++ ++ r8153_power_cut_en(tp, false); ++ r8153_u1u2en(tp, true); ++ ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ALDPS_SPDWN_RATIO); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, EEE_SPDWN_RATIO); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ++ PKT_AVAIL_SPDWN_EN | SUSPEND_SPDWN_EN | ++ U1U2_SPDWN_EN | L1_SPDWN_EN); ++ ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, ++ PWRSAVE_SPDWN_EN | RXDV_SPDWN_EN | TX10MIDLE_EN | ++ TP100_SPDWN_EN | TP500_SPDWN_EN | TP1000_SPDWN_EN | ++ EEE_SPDWN_EN); ++ ++ r8153_enable_eee(tp); ++ r8153_enable_aldps(tp); ++ r8152b_enable_fc(tp); ++ rtl_tally_reset(tp); ++} ++ ++static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) ++{ ++ struct r8152 *tp = usb_get_intfdata(intf); ++ struct net_device *netdev = tp->netdev; ++ int ret = 0; ++ ++ mutex_lock(&tp->control); ++ ++ if (PMSG_IS_AUTO(message)) { ++ if (netif_running(netdev) && work_busy(&tp->schedule.work)) { ++ ret = -EBUSY; ++ goto out1; ++ } ++ ++ set_bit(SELECTIVE_SUSPEND, &tp->flags); ++ } else { ++ netif_device_detach(netdev); ++ } ++ ++ if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) { ++ clear_bit(WORK_ENABLE, &tp->flags); ++ usb_kill_urb(tp->intr_urb); ++ tasklet_disable(&tp->tl); ++ if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { ++ rtl_stop_rx(tp); ++ rtl_runtime_suspend_enable(tp, true); ++ } else { ++ cancel_delayed_work_sync(&tp->schedule); ++ tp->rtl_ops.down(tp); ++ } ++ tasklet_enable(&tp->tl); ++ } ++out1: ++ mutex_unlock(&tp->control); ++ ++ return ret; ++} ++ ++static int rtl8152_resume(struct usb_interface *intf) ++{ ++ struct r8152 *tp = usb_get_intfdata(intf); ++ ++ mutex_lock(&tp->control); ++ ++ if (!test_bit(SELECTIVE_SUSPEND, &tp->flags)) { ++ tp->rtl_ops.init(tp); ++ netif_device_attach(tp->netdev); ++ } ++ ++ if (netif_running(tp->netdev)) { ++ if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { ++ rtl_runtime_suspend_enable(tp, false); ++ clear_bit(SELECTIVE_SUSPEND, &tp->flags); ++ set_bit(WORK_ENABLE, &tp->flags); ++ if (tp->speed & LINK_STATUS) ++ rtl_start_rx(tp); ++ } else { ++ tp->rtl_ops.up(tp); ++ rtl8152_set_speed(tp, AUTONEG_ENABLE, ++ tp->mii.supports_gmii ? ++ SPEED_1000 : SPEED_100, ++ DUPLEX_FULL); ++ tp->speed = 0; ++ netif_carrier_off(tp->netdev); ++ set_bit(WORK_ENABLE, &tp->flags); ++ } ++ usb_submit_urb(tp->intr_urb, GFP_KERNEL); ++ } else if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { ++ clear_bit(SELECTIVE_SUSPEND, &tp->flags); ++ } ++ ++ mutex_unlock(&tp->control); ++ ++ return 0; ++} ++ ++static void rtl8152_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) ++{ ++ struct r8152 *tp = netdev_priv(dev); ++ ++ if (usb_autopm_get_interface(tp->intf) < 0) ++ return; ++ ++ mutex_lock(&tp->control); ++ ++ wol->supported = WAKE_ANY; ++ wol->wolopts = __rtl_get_wol(tp); ++ ++ mutex_unlock(&tp->control); ++ ++ usb_autopm_put_interface(tp->intf); ++} ++ ++static int rtl8152_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) ++{ ++ struct r8152 *tp = netdev_priv(dev); ++ int ret; ++ ++ ret = usb_autopm_get_interface(tp->intf); ++ if (ret < 0) ++ goto out_set_wol; ++ ++ mutex_lock(&tp->control); ++ ++ __rtl_set_wol(tp, wol->wolopts); ++ tp->saved_wolopts = wol->wolopts & WAKE_ANY; ++ ++ mutex_unlock(&tp->control); ++ ++ usb_autopm_put_interface(tp->intf); ++ ++out_set_wol: ++ return ret; ++} ++ ++static u32 rtl8152_get_msglevel(struct net_device *dev) ++{ ++ struct r8152 *tp = netdev_priv(dev); ++ ++ return tp->msg_enable; ++} ++ ++static void rtl8152_set_msglevel(struct net_device *dev, u32 value) ++{ ++ struct r8152 *tp = netdev_priv(dev); ++ ++ tp->msg_enable = value; ++} ++ ++static void rtl8152_get_drvinfo(struct net_device *netdev, ++ struct ethtool_drvinfo *info) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ ++ strlcpy(info->driver, MODULENAME, sizeof(info->driver)); ++ strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); ++ usb_make_path(tp->udev, info->bus_info, sizeof(info->bus_info)); ++} ++ ++static ++int rtl8152_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ int ret; ++ ++ if (!tp->mii.mdio_read) ++ return -EOPNOTSUPP; ++ ++ ret = usb_autopm_get_interface(tp->intf); ++ if (ret < 0) ++ goto out; ++ ++ mutex_lock(&tp->control); ++ ++ ret = mii_ethtool_gset(&tp->mii, cmd); ++ ++ mutex_unlock(&tp->control); ++ ++ usb_autopm_put_interface(tp->intf); ++ ++out: ++ return ret; ++} ++ ++static int rtl8152_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) ++{ ++ struct r8152 *tp = netdev_priv(dev); ++ int ret; ++ ++ ret = usb_autopm_get_interface(tp->intf); ++ if (ret < 0) ++ goto out; ++ ++ mutex_lock(&tp->control); ++ ++ ret = rtl8152_set_speed(tp, cmd->autoneg, cmd->speed, cmd->duplex); ++ ++ mutex_unlock(&tp->control); ++ ++ usb_autopm_put_interface(tp->intf); ++ ++out: ++ return ret; ++} ++ ++static const char rtl8152_gstrings[][ETH_GSTRING_LEN] = { ++ "tx_packets", ++ "rx_packets", ++ "tx_errors", ++ "rx_errors", ++ "rx_missed", ++ "align_errors", ++ "tx_single_collisions", ++ "tx_multi_collisions", ++ "rx_unicast", ++ "rx_broadcast", ++ "rx_multicast", ++ "tx_aborted", ++ "tx_underrun", ++}; ++ ++static int rtl8152_get_sset_count(struct net_device *dev, int sset) ++{ ++ switch (sset) { ++ case ETH_SS_STATS: ++ return ARRAY_SIZE(rtl8152_gstrings); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static void rtl8152_get_ethtool_stats(struct net_device *dev, ++ struct ethtool_stats *stats, u64 *data) ++{ ++ struct r8152 *tp = netdev_priv(dev); ++ struct tally_counter tally; ++ ++ if (usb_autopm_get_interface(tp->intf) < 0) ++ return; ++ ++ generic_ocp_read(tp, PLA_TALLYCNT, sizeof(tally), &tally, MCU_TYPE_PLA); ++ ++ usb_autopm_put_interface(tp->intf); ++ ++ data[0] = le64_to_cpu(tally.tx_packets); ++ data[1] = le64_to_cpu(tally.rx_packets); ++ data[2] = le64_to_cpu(tally.tx_errors); ++ data[3] = le32_to_cpu(tally.rx_errors); ++ data[4] = le16_to_cpu(tally.rx_missed); ++ data[5] = le16_to_cpu(tally.align_errors); ++ data[6] = le32_to_cpu(tally.tx_one_collision); ++ data[7] = le32_to_cpu(tally.tx_multi_collision); ++ data[8] = le64_to_cpu(tally.rx_unicast); ++ data[9] = le64_to_cpu(tally.rx_broadcast); ++ data[10] = le32_to_cpu(tally.rx_multicast); ++ data[11] = le16_to_cpu(tally.tx_aborted); ++ data[12] = le16_to_cpu(tally.tx_underun); ++} ++ ++static void rtl8152_get_strings(struct net_device *dev, u32 stringset, u8 *data) ++{ ++ switch (stringset) { ++ case ETH_SS_STATS: ++ memcpy(data, *rtl8152_gstrings, sizeof(rtl8152_gstrings)); ++ break; ++ } ++} ++ ++static int r8152_get_eee(struct r8152 *tp, struct ethtool_eee *eee) ++{ ++ u32 ocp_data, lp, adv, supported = 0; ++ u16 val; ++ ++ val = r8152_mmd_read(tp, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); ++ supported = mmd_eee_cap_to_ethtool_sup_t(val); ++ ++ val = r8152_mmd_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV); ++ adv = mmd_eee_adv_to_ethtool_adv_t(val); ++ ++ val = r8152_mmd_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); ++ lp = mmd_eee_adv_to_ethtool_adv_t(val); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); ++ ocp_data &= EEE_RX_EN | EEE_TX_EN; ++ ++ eee->eee_enabled = !!ocp_data; ++ eee->eee_active = !!(supported & adv & lp); ++ eee->supported = supported; ++ eee->advertised = adv; ++ eee->lp_advertised = lp; ++ ++ return 0; ++} ++ ++static int r8152_set_eee(struct r8152 *tp, struct ethtool_eee *eee) ++{ ++ u16 val = ethtool_adv_to_mmd_eee_adv_t(eee->advertised); ++ ++ r8152_eee_en(tp, eee->eee_enabled); ++ ++ if (!eee->eee_enabled) ++ val = 0; ++ ++ r8152_mmd_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val); ++ ++ return 0; ++} ++ ++static int r8153_get_eee(struct r8152 *tp, struct ethtool_eee *eee) ++{ ++ u32 ocp_data, lp, adv, supported = 0; ++ u16 val; ++ ++ val = ocp_reg_read(tp, OCP_EEE_ABLE); ++ supported = mmd_eee_cap_to_ethtool_sup_t(val); ++ ++ val = ocp_reg_read(tp, OCP_EEE_ADV); ++ adv = mmd_eee_adv_to_ethtool_adv_t(val); ++ ++ val = ocp_reg_read(tp, OCP_EEE_LPABLE); ++ lp = mmd_eee_adv_to_ethtool_adv_t(val); ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEE_CR); ++ ocp_data &= EEE_RX_EN | EEE_TX_EN; ++ ++ eee->eee_enabled = !!ocp_data; ++ eee->eee_active = !!(supported & adv & lp); ++ eee->supported = supported; ++ eee->advertised = adv; ++ eee->lp_advertised = lp; ++ ++ return 0; ++} ++ ++static int r8153_set_eee(struct r8152 *tp, struct ethtool_eee *eee) ++{ ++ u16 val = ethtool_adv_to_mmd_eee_adv_t(eee->advertised); ++ ++ r8153_eee_en(tp, eee->eee_enabled); ++ ++ if (!eee->eee_enabled) ++ val = 0; ++ ++ ocp_reg_write(tp, OCP_EEE_ADV, val); ++ ++ return 0; ++} ++ ++static int ++rtl_ethtool_get_eee(struct net_device *net, struct ethtool_eee *edata) ++{ ++ struct r8152 *tp = netdev_priv(net); ++ int ret; ++ ++ ret = usb_autopm_get_interface(tp->intf); ++ if (ret < 0) ++ goto out; ++ ++ mutex_lock(&tp->control); ++ ++ ret = tp->rtl_ops.eee_get(tp, edata); ++ ++ mutex_unlock(&tp->control); ++ ++ usb_autopm_put_interface(tp->intf); ++ ++out: ++ return ret; ++} ++ ++static int ++rtl_ethtool_set_eee(struct net_device *net, struct ethtool_eee *edata) ++{ ++ struct r8152 *tp = netdev_priv(net); ++ int ret; ++ ++ ret = usb_autopm_get_interface(tp->intf); ++ if (ret < 0) ++ goto out; ++ ++ mutex_lock(&tp->control); ++ ++ ret = tp->rtl_ops.eee_set(tp, edata); ++ if (!ret) ++ ret = mii_nway_restart(&tp->mii); ++ ++ mutex_unlock(&tp->control); ++ ++ usb_autopm_put_interface(tp->intf); ++ ++out: ++ return ret; ++} ++ ++static struct ethtool_ops ops = { ++ .get_drvinfo = rtl8152_get_drvinfo, ++ .get_settings = rtl8152_get_settings, ++ .set_settings = rtl8152_set_settings, ++ .get_link = ethtool_op_get_link, ++ .get_msglevel = rtl8152_get_msglevel, ++ .set_msglevel = rtl8152_set_msglevel, ++ .get_wol = rtl8152_get_wol, ++ .set_wol = rtl8152_set_wol, ++ .get_strings = rtl8152_get_strings, ++ .get_sset_count = rtl8152_get_sset_count, ++ .get_ethtool_stats = rtl8152_get_ethtool_stats, ++ .get_eee = rtl_ethtool_get_eee, ++ .set_eee = rtl_ethtool_set_eee, ++}; ++ ++static int rtl8152_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) ++{ ++ struct r8152 *tp = netdev_priv(netdev); ++ struct mii_ioctl_data *data = if_mii(rq); ++ int res; ++ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return -ENODEV; ++ ++ res = usb_autopm_get_interface(tp->intf); ++ if (res < 0) ++ goto out; ++ ++ switch (cmd) { ++ case SIOCGMIIPHY: ++ data->phy_id = R8152_PHY_ID; /* Internal PHY */ ++ break; ++ ++ case SIOCGMIIREG: ++ mutex_lock(&tp->control); ++ data->val_out = r8152_mdio_read(tp, data->reg_num); ++ mutex_unlock(&tp->control); ++ break; ++ ++ case SIOCSMIIREG: ++ if (!capable(CAP_NET_ADMIN)) { ++ res = -EPERM; ++ break; ++ } ++ mutex_lock(&tp->control); ++ r8152_mdio_write(tp, data->reg_num, data->val_in); ++ mutex_unlock(&tp->control); ++ break; ++ ++ default: ++ res = -EOPNOTSUPP; ++ } ++ ++ usb_autopm_put_interface(tp->intf); ++ ++out: ++ return res; ++} ++ ++static int rtl8152_change_mtu(struct net_device *dev, int new_mtu) ++{ ++ struct r8152 *tp = netdev_priv(dev); ++ ++ switch (tp->version) { ++ case RTL_VER_01: ++ case RTL_VER_02: ++ return eth_change_mtu(dev, new_mtu); ++ default: ++ break; ++ } ++ ++ if (new_mtu < 68 || new_mtu > RTL8153_MAX_MTU) ++ return -EINVAL; ++ ++ dev->mtu = new_mtu; ++ ++ return 0; ++} ++ ++static const struct net_device_ops rtl8152_netdev_ops = { ++ .ndo_open = rtl8152_open, ++ .ndo_stop = rtl8152_close, ++ .ndo_do_ioctl = rtl8152_ioctl, ++ .ndo_start_xmit = rtl8152_start_xmit, ++ .ndo_tx_timeout = rtl8152_tx_timeout, ++ .ndo_set_features = rtl8152_set_features, ++ .ndo_set_rx_mode = rtl8152_set_rx_mode, ++ .ndo_set_mac_address = rtl8152_set_mac_address, ++ .ndo_change_mtu = rtl8152_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++}; ++ ++static void r8152b_get_version(struct r8152 *tp) ++{ ++ u32 ocp_data; ++ u16 version; ++ ++ ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1); ++ version = (u16)(ocp_data & VERSION_MASK); ++ ++ switch (version) { ++ case 0x4c00: ++ tp->version = RTL_VER_01; ++ break; ++ case 0x4c10: ++ tp->version = RTL_VER_02; ++ break; ++ case 0x5c00: ++ tp->version = RTL_VER_03; ++ tp->mii.supports_gmii = 1; ++ break; ++ case 0x5c10: ++ tp->version = RTL_VER_04; ++ tp->mii.supports_gmii = 1; ++ break; ++ case 0x5c20: ++ tp->version = RTL_VER_05; ++ tp->mii.supports_gmii = 1; ++ break; ++ default: ++ netif_info(tp, probe, tp->netdev, ++ "Unknown version 0x%04x\n", version); ++ break; ++ } ++} ++ ++static void rtl8152_unload(struct r8152 *tp) ++{ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ if (tp->version != RTL_VER_01) ++ r8152_power_cut_en(tp, true); ++} ++ ++static void rtl8153_unload(struct r8152 *tp) ++{ ++ if (test_bit(RTL8152_UNPLUG, &tp->flags)) ++ return; ++ ++ r8153_power_cut_en(tp, false); ++} ++ ++static int rtl_ops_init(struct r8152 *tp, const struct usb_device_id *id) ++{ ++ struct rtl_ops *ops = &tp->rtl_ops; ++ int ret = -ENODEV; ++ ++ switch (id->idVendor) { ++ case VENDOR_ID_REALTEK: ++ switch (id->idProduct) { ++ case PRODUCT_ID_RTL8152: ++ ops->init = r8152b_init; ++ ops->enable = rtl8152_enable; ++ ops->disable = rtl8152_disable; ++ ops->up = rtl8152_up; ++ ops->down = rtl8152_down; ++ ops->unload = rtl8152_unload; ++ ops->eee_get = r8152_get_eee; ++ ops->eee_set = r8152_set_eee; ++ ret = 0; ++ break; ++ case PRODUCT_ID_RTL8153: ++ ops->init = r8153_init; ++ ops->enable = rtl8153_enable; ++ ops->disable = rtl8153_disable; ++ ops->up = rtl8153_up; ++ ops->down = rtl8153_down; ++ ops->unload = rtl8153_unload; ++ ops->eee_get = r8153_get_eee; ++ ops->eee_set = r8153_set_eee; ++ ret = 0; ++ break; ++ default: ++ break; ++ } ++ break; ++ ++ case VENDOR_ID_SAMSUNG: ++ switch (id->idProduct) { ++ case PRODUCT_ID_SAMSUNG: ++ ops->init = r8153_init; ++ ops->enable = rtl8153_enable; ++ ops->disable = rtl8153_disable; ++ ops->up = rtl8153_up; ++ ops->down = rtl8153_down; ++ ops->unload = rtl8153_unload; ++ ops->eee_get = r8153_get_eee; ++ ops->eee_set = r8153_set_eee; ++ ret = 0; ++ break; ++ default: ++ break; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (ret) ++ netif_err(tp, probe, tp->netdev, "Unknown Device\n"); ++ ++ return ret; ++} ++ ++static int rtl8152_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ struct usb_device *udev = interface_to_usbdev(intf); ++ struct r8152 *tp; ++ struct net_device *netdev; ++ int ret; ++ ++ if (udev->actconfig->desc.bConfigurationValue != 1) { ++ usb_driver_set_configuration(udev, 1); ++ return -ENODEV; ++ } ++ ++ usb_reset_device(udev); ++ netdev = alloc_etherdev(sizeof(struct r8152)); ++ if (!netdev) { ++ dev_err(&intf->dev, "Out of memory\n"); ++ return -ENOMEM; ++ } ++ ++ SET_NETDEV_DEV(netdev, &intf->dev); ++ tp = netdev_priv(netdev); ++ tp->msg_enable = 0x7FFF; ++ ++ tp->udev = udev; ++ tp->netdev = netdev; ++ tp->intf = intf; ++ ++ ret = rtl_ops_init(tp, id); ++ if (ret) ++ goto out; ++ ++ tasklet_init(&tp->tl, bottom_half, (unsigned long)tp); ++ mutex_init(&tp->control); ++ INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t); ++ ++ netdev->netdev_ops = &rtl8152_netdev_ops; ++ netdev->watchdog_timeo = RTL8152_TX_TIMEOUT; ++ ++ netdev->features |= NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG | ++ NETIF_F_TSO | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | ++ NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_RX | ++ NETIF_F_HW_VLAN_CTAG_TX; ++ netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG | ++ NETIF_F_TSO | NETIF_F_FRAGLIST | ++ NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | ++ NETIF_F_HW_VLAN_CTAG_RX | ++ NETIF_F_HW_VLAN_CTAG_TX; ++ netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | ++ NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | ++ NETIF_F_IPV6_CSUM | NETIF_F_TSO6; ++ ++ netdev->ethtool_ops = &ops; ++ netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE); ++ ++ tp->mii.dev = netdev; ++ tp->mii.mdio_read = read_mii_word; ++ tp->mii.mdio_write = write_mii_word; ++ tp->mii.phy_id_mask = 0x3f; ++ tp->mii.reg_num_mask = 0x1f; ++ tp->mii.phy_id = R8152_PHY_ID; ++ tp->mii.supports_gmii = 0; ++ ++ intf->needs_remote_wakeup = 1; ++ ++ r8152b_get_version(tp); ++ tp->rtl_ops.init(tp); ++ set_ethernet_addr(tp); ++ ++ usb_set_intfdata(intf, tp); ++ ++ ret = register_netdev(netdev); ++ if (ret != 0) { ++ netif_err(tp, probe, netdev, "couldn't register the device\n"); ++ goto out1; ++ } ++ ++ tp->saved_wolopts = __rtl_get_wol(tp); ++ if (tp->saved_wolopts) ++ device_set_wakeup_enable(&udev->dev, true); ++ else ++ device_set_wakeup_enable(&udev->dev, false); ++ ++ netif_info(tp, probe, netdev, "%s\n", DRIVER_VERSION); ++ ++ return 0; ++ ++out1: ++ usb_set_intfdata(intf, NULL); ++out: ++ free_netdev(netdev); ++ return ret; ++} ++ ++static void rtl8152_disconnect(struct usb_interface *intf) ++{ ++ struct r8152 *tp = usb_get_intfdata(intf); ++ ++ usb_set_intfdata(intf, NULL); ++ if (tp) { ++ struct usb_device *udev = tp->udev; ++ ++ if (udev->state == USB_STATE_NOTATTACHED) ++ set_bit(RTL8152_UNPLUG, &tp->flags); ++ ++ tasklet_kill(&tp->tl); ++ unregister_netdev(tp->netdev); ++ tp->rtl_ops.unload(tp); ++ free_netdev(tp->netdev); ++ } ++} ++ ++/* table of devices that work with this driver */ ++static struct usb_device_id rtl8152_table[] = { ++ {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8152)}, ++ {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8153)}, ++ {USB_DEVICE(VENDOR_ID_SAMSUNG, PRODUCT_ID_SAMSUNG)}, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(usb, rtl8152_table); ++ ++static struct usb_driver rtl8152_driver = { ++ .name = MODULENAME, ++ .id_table = rtl8152_table, ++ .probe = rtl8152_probe, ++ .disconnect = rtl8152_disconnect, ++ .suspend = rtl8152_suspend, ++ .resume = rtl8152_resume, ++ .reset_resume = rtl8152_resume, ++ .supports_autosuspend = 1, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(rtl8152_driver); ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/rtl8150.c backports-3.18.1-1/drivers/net/usb/rtl8150.c +--- backports-3.18.1-1.org/drivers/net/usb/rtl8150.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/rtl8150.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,950 @@ ++/* ++ * Copyright (c) 2002 Petko Manolov (petkan@users.sourceforge.net) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Version Information */ ++#define DRIVER_VERSION "v0.6.2 (2004/08/27)" ++#define DRIVER_AUTHOR "Petko Manolov " ++#define DRIVER_DESC "rtl8150 based usb-ethernet driver" ++ ++#define IDR 0x0120 ++#define MAR 0x0126 ++#define CR 0x012e ++#define TCR 0x012f ++#define RCR 0x0130 ++#define TSR 0x0132 ++#define RSR 0x0133 ++#define CON0 0x0135 ++#define CON1 0x0136 ++#define MSR 0x0137 ++#define PHYADD 0x0138 ++#define PHYDAT 0x0139 ++#define PHYCNT 0x013b ++#define GPPC 0x013d ++#define BMCR 0x0140 ++#define BMSR 0x0142 ++#define ANAR 0x0144 ++#define ANLP 0x0146 ++#define AER 0x0148 ++#define CSCR 0x014C /* This one has the link status */ ++#define CSCR_LINK_STATUS (1 << 3) ++ ++#define IDR_EEPROM 0x1202 ++ ++#define PHY_READ 0 ++#define PHY_WRITE 0x20 ++#define PHY_GO 0x40 ++ ++#define MII_TIMEOUT 10 ++#define INTBUFSIZE 8 ++ ++#define RTL8150_REQT_READ 0xc0 ++#define RTL8150_REQT_WRITE 0x40 ++#define RTL8150_REQ_GET_REGS 0x05 ++#define RTL8150_REQ_SET_REGS 0x05 ++ ++ ++/* Transmit status register errors */ ++#define TSR_ECOL (1<<5) ++#define TSR_LCOL (1<<4) ++#define TSR_LOSS_CRS (1<<3) ++#define TSR_JBR (1<<2) ++#define TSR_ERRORS (TSR_ECOL | TSR_LCOL | TSR_LOSS_CRS | TSR_JBR) ++/* Receive status register errors */ ++#define RSR_CRC (1<<2) ++#define RSR_FAE (1<<1) ++#define RSR_ERRORS (RSR_CRC | RSR_FAE) ++ ++/* Media status register definitions */ ++#define MSR_DUPLEX (1<<4) ++#define MSR_SPEED (1<<3) ++#define MSR_LINK (1<<2) ++ ++/* Interrupt pipe data */ ++#define INT_TSR 0x00 ++#define INT_RSR 0x01 ++#define INT_MSR 0x02 ++#define INT_WAKSR 0x03 ++#define INT_TXOK_CNT 0x04 ++#define INT_RXLOST_CNT 0x05 ++#define INT_CRERR_CNT 0x06 ++#define INT_COL_CNT 0x07 ++ ++ ++#define RTL8150_MTU 1540 ++#define RTL8150_TX_TIMEOUT (HZ) ++#define RX_SKB_POOL_SIZE 4 ++ ++/* rtl8150 flags */ ++#define RTL8150_HW_CRC 0 ++#define RX_REG_SET 1 ++#define RTL8150_UNPLUG 2 ++#define RX_URB_FAIL 3 ++ ++/* Define these values to match your device */ ++#define VENDOR_ID_REALTEK 0x0bda ++#define VENDOR_ID_MELCO 0x0411 ++#define VENDOR_ID_MICRONET 0x3980 ++#define VENDOR_ID_LONGSHINE 0x07b8 ++#define VENDOR_ID_OQO 0x1557 ++#define VENDOR_ID_ZYXEL 0x0586 ++ ++#define PRODUCT_ID_RTL8150 0x8150 ++#define PRODUCT_ID_LUAKTX 0x0012 ++#define PRODUCT_ID_LCS8138TX 0x401a ++#define PRODUCT_ID_SP128AR 0x0003 ++#define PRODUCT_ID_PRESTIGE 0x401a ++ ++#undef EEPROM_WRITE ++ ++/* table of devices that work with this driver */ ++static struct usb_device_id rtl8150_table[] = { ++ {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150)}, ++ {USB_DEVICE(VENDOR_ID_MELCO, PRODUCT_ID_LUAKTX)}, ++ {USB_DEVICE(VENDOR_ID_MICRONET, PRODUCT_ID_SP128AR)}, ++ {USB_DEVICE(VENDOR_ID_LONGSHINE, PRODUCT_ID_LCS8138TX)}, ++ {USB_DEVICE(VENDOR_ID_OQO, PRODUCT_ID_RTL8150)}, ++ {USB_DEVICE(VENDOR_ID_ZYXEL, PRODUCT_ID_PRESTIGE)}, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(usb, rtl8150_table); ++ ++struct rtl8150 { ++ unsigned long flags; ++ struct usb_device *udev; ++ struct tasklet_struct tl; ++ struct net_device *netdev; ++ struct urb *rx_urb, *tx_urb, *intr_urb; ++ struct sk_buff *tx_skb, *rx_skb; ++ struct sk_buff *rx_skb_pool[RX_SKB_POOL_SIZE]; ++ spinlock_t rx_pool_lock; ++ struct usb_ctrlrequest dr; ++ int intr_interval; ++ u8 *intr_buff; ++ u8 phy; ++}; ++ ++typedef struct rtl8150 rtl8150_t; ++ ++struct async_req { ++ struct usb_ctrlrequest dr; ++ u16 rx_creg; ++}; ++ ++static const char driver_name [] = "rtl8150"; ++ ++/* ++** ++** device related part of the code ++** ++*/ ++static int get_registers(rtl8150_t * dev, u16 indx, u16 size, void *data) ++{ ++ return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), ++ RTL8150_REQ_GET_REGS, RTL8150_REQT_READ, ++ indx, 0, data, size, 500); ++} ++ ++static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data) ++{ ++ return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), ++ RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE, ++ indx, 0, data, size, 500); ++} ++ ++static void async_set_reg_cb(struct urb *urb) ++{ ++ struct async_req *req = (struct async_req *)urb->context; ++ int status = urb->status; ++ ++ if (status < 0) ++ dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status); ++ kfree(req); ++ usb_free_urb(urb); ++} ++ ++static int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, u16 reg) ++{ ++ int res = -ENOMEM; ++ struct urb *async_urb; ++ struct async_req *req; ++ ++ req = kmalloc(sizeof(struct async_req), GFP_ATOMIC); ++ if (req == NULL) ++ return res; ++ async_urb = usb_alloc_urb(0, GFP_ATOMIC); ++ if (async_urb == NULL) { ++ kfree(req); ++ return res; ++ } ++ req->rx_creg = cpu_to_le16(reg); ++ req->dr.bRequestType = RTL8150_REQT_WRITE; ++ req->dr.bRequest = RTL8150_REQ_SET_REGS; ++ req->dr.wIndex = 0; ++ req->dr.wValue = cpu_to_le16(indx); ++ req->dr.wLength = cpu_to_le16(size); ++ usb_fill_control_urb(async_urb, dev->udev, ++ usb_sndctrlpipe(dev->udev, 0), (void *)&req->dr, ++ &req->rx_creg, size, async_set_reg_cb, req); ++ res = usb_submit_urb(async_urb, GFP_ATOMIC); ++ if (res) { ++ if (res == -ENODEV) ++ netif_device_detach(dev->netdev); ++ dev_err(&dev->udev->dev, "%s failed with %d\n", __func__, res); ++ } ++ return res; ++} ++ ++static int read_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 * reg) ++{ ++ int i; ++ u8 data[3], tmp; ++ ++ data[0] = phy; ++ data[1] = data[2] = 0; ++ tmp = indx | PHY_READ | PHY_GO; ++ i = 0; ++ ++ set_registers(dev, PHYADD, sizeof(data), data); ++ set_registers(dev, PHYCNT, 1, &tmp); ++ do { ++ get_registers(dev, PHYCNT, 1, data); ++ } while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT)); ++ ++ if (i <= MII_TIMEOUT) { ++ get_registers(dev, PHYDAT, 2, data); ++ *reg = data[0] | (data[1] << 8); ++ return 0; ++ } else ++ return 1; ++} ++ ++static int write_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 reg) ++{ ++ int i; ++ u8 data[3], tmp; ++ ++ data[0] = phy; ++ data[1] = reg & 0xff; ++ data[2] = (reg >> 8) & 0xff; ++ tmp = indx | PHY_WRITE | PHY_GO; ++ i = 0; ++ ++ set_registers(dev, PHYADD, sizeof(data), data); ++ set_registers(dev, PHYCNT, 1, &tmp); ++ do { ++ get_registers(dev, PHYCNT, 1, data); ++ } while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT)); ++ ++ if (i <= MII_TIMEOUT) ++ return 0; ++ else ++ return 1; ++} ++ ++static inline void set_ethernet_addr(rtl8150_t * dev) ++{ ++ u8 node_id[6]; ++ ++ get_registers(dev, IDR, sizeof(node_id), node_id); ++ memcpy(dev->netdev->dev_addr, node_id, sizeof(node_id)); ++} ++ ++static int rtl8150_set_mac_address(struct net_device *netdev, void *p) ++{ ++ struct sockaddr *addr = p; ++ rtl8150_t *dev = netdev_priv(netdev); ++ ++ if (netif_running(netdev)) ++ return -EBUSY; ++ ++ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); ++ netdev_dbg(netdev, "Setting MAC address to %pM\n", netdev->dev_addr); ++ /* Set the IDR registers. */ ++ set_registers(dev, IDR, netdev->addr_len, netdev->dev_addr); ++#ifdef EEPROM_WRITE ++ { ++ int i; ++ u8 cr; ++ /* Get the CR contents. */ ++ get_registers(dev, CR, 1, &cr); ++ /* Set the WEPROM bit (eeprom write enable). */ ++ cr |= 0x20; ++ set_registers(dev, CR, 1, &cr); ++ /* Write the MAC address into eeprom. Eeprom writes must be word-sized, ++ so we need to split them up. */ ++ for (i = 0; i * 2 < netdev->addr_len; i++) { ++ set_registers(dev, IDR_EEPROM + (i * 2), 2, ++ netdev->dev_addr + (i * 2)); ++ } ++ /* Clear the WEPROM bit (preventing accidental eeprom writes). */ ++ cr &= 0xdf; ++ set_registers(dev, CR, 1, &cr); ++ } ++#endif ++ return 0; ++} ++ ++static int rtl8150_reset(rtl8150_t * dev) ++{ ++ u8 data = 0x10; ++ int i = HZ; ++ ++ set_registers(dev, CR, 1, &data); ++ do { ++ get_registers(dev, CR, 1, &data); ++ } while ((data & 0x10) && --i); ++ ++ return (i > 0) ? 1 : 0; ++} ++ ++static int alloc_all_urbs(rtl8150_t * dev) ++{ ++ dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!dev->rx_urb) ++ return 0; ++ dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!dev->tx_urb) { ++ usb_free_urb(dev->rx_urb); ++ return 0; ++ } ++ dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!dev->intr_urb) { ++ usb_free_urb(dev->rx_urb); ++ usb_free_urb(dev->tx_urb); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static void free_all_urbs(rtl8150_t * dev) ++{ ++ usb_free_urb(dev->rx_urb); ++ usb_free_urb(dev->tx_urb); ++ usb_free_urb(dev->intr_urb); ++} ++ ++static void unlink_all_urbs(rtl8150_t * dev) ++{ ++ usb_kill_urb(dev->rx_urb); ++ usb_kill_urb(dev->tx_urb); ++ usb_kill_urb(dev->intr_urb); ++} ++ ++static inline struct sk_buff *pull_skb(rtl8150_t *dev) ++{ ++ struct sk_buff *skb; ++ int i; ++ ++ for (i = 0; i < RX_SKB_POOL_SIZE; i++) { ++ if (dev->rx_skb_pool[i]) { ++ skb = dev->rx_skb_pool[i]; ++ dev->rx_skb_pool[i] = NULL; ++ return skb; ++ } ++ } ++ return NULL; ++} ++ ++static void read_bulk_callback(struct urb *urb) ++{ ++ rtl8150_t *dev; ++ unsigned pkt_len, res; ++ struct sk_buff *skb; ++ struct net_device *netdev; ++ u16 rx_stat; ++ int status = urb->status; ++ int result; ++ ++ dev = urb->context; ++ if (!dev) ++ return; ++ if (test_bit(RTL8150_UNPLUG, &dev->flags)) ++ return; ++ netdev = dev->netdev; ++ if (!netif_device_present(netdev)) ++ return; ++ ++ switch (status) { ++ case 0: ++ break; ++ case -ENOENT: ++ return; /* the urb is in unlink state */ ++ case -ETIME: ++ if (printk_ratelimit()) ++ dev_warn(&urb->dev->dev, "may be reset is needed?..\n"); ++ goto goon; ++ default: ++ if (printk_ratelimit()) ++ dev_warn(&urb->dev->dev, "Rx status %d\n", status); ++ goto goon; ++ } ++ ++ if (!dev->rx_skb) ++ goto resched; ++ /* protect against short packets (tell me why we got some?!?) */ ++ if (urb->actual_length < 4) ++ goto goon; ++ ++ res = urb->actual_length; ++ rx_stat = le16_to_cpu(*(__le16 *)(urb->transfer_buffer + res - 4)); ++ pkt_len = res - 4; ++ ++ skb_put(dev->rx_skb, pkt_len); ++ dev->rx_skb->protocol = eth_type_trans(dev->rx_skb, netdev); ++ netif_rx(dev->rx_skb); ++ netdev->stats.rx_packets++; ++ netdev->stats.rx_bytes += pkt_len; ++ ++ spin_lock(&dev->rx_pool_lock); ++ skb = pull_skb(dev); ++ spin_unlock(&dev->rx_pool_lock); ++ if (!skb) ++ goto resched; ++ ++ dev->rx_skb = skb; ++goon: ++ usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), ++ dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev); ++ result = usb_submit_urb(dev->rx_urb, GFP_ATOMIC); ++ if (result == -ENODEV) ++ netif_device_detach(dev->netdev); ++ else if (result) { ++ set_bit(RX_URB_FAIL, &dev->flags); ++ goto resched; ++ } else { ++ clear_bit(RX_URB_FAIL, &dev->flags); ++ } ++ ++ return; ++resched: ++ tasklet_schedule(&dev->tl); ++} ++ ++static void write_bulk_callback(struct urb *urb) ++{ ++ rtl8150_t *dev; ++ int status = urb->status; ++ ++ dev = urb->context; ++ if (!dev) ++ return; ++ dev_kfree_skb_irq(dev->tx_skb); ++ if (!netif_device_present(dev->netdev)) ++ return; ++ if (status) ++ dev_info(&urb->dev->dev, "%s: Tx status %d\n", ++ dev->netdev->name, status); ++ dev->netdev->trans_start = jiffies; ++ netif_wake_queue(dev->netdev); ++} ++ ++static void intr_callback(struct urb *urb) ++{ ++ rtl8150_t *dev; ++ __u8 *d; ++ int status = urb->status; ++ int res; ++ ++ dev = urb->context; ++ if (!dev) ++ return; ++ switch (status) { ++ case 0: /* success */ ++ break; ++ case -ECONNRESET: /* unlink */ ++ case -ENOENT: ++ case -ESHUTDOWN: ++ return; ++ /* -EPIPE: should clear the halt */ ++ default: ++ dev_info(&urb->dev->dev, "%s: intr status %d\n", ++ dev->netdev->name, status); ++ goto resubmit; ++ } ++ ++ d = urb->transfer_buffer; ++ if (d[0] & TSR_ERRORS) { ++ dev->netdev->stats.tx_errors++; ++ if (d[INT_TSR] & (TSR_ECOL | TSR_JBR)) ++ dev->netdev->stats.tx_aborted_errors++; ++ if (d[INT_TSR] & TSR_LCOL) ++ dev->netdev->stats.tx_window_errors++; ++ if (d[INT_TSR] & TSR_LOSS_CRS) ++ dev->netdev->stats.tx_carrier_errors++; ++ } ++ /* Report link status changes to the network stack */ ++ if ((d[INT_MSR] & MSR_LINK) == 0) { ++ if (netif_carrier_ok(dev->netdev)) { ++ netif_carrier_off(dev->netdev); ++ netdev_dbg(dev->netdev, "%s: LINK LOST\n", __func__); ++ } ++ } else { ++ if (!netif_carrier_ok(dev->netdev)) { ++ netif_carrier_on(dev->netdev); ++ netdev_dbg(dev->netdev, "%s: LINK CAME BACK\n", __func__); ++ } ++ } ++ ++resubmit: ++ res = usb_submit_urb (urb, GFP_ATOMIC); ++ if (res == -ENODEV) ++ netif_device_detach(dev->netdev); ++ else if (res) ++ dev_err(&dev->udev->dev, ++ "can't resubmit intr, %s-%s/input0, status %d\n", ++ dev->udev->bus->bus_name, dev->udev->devpath, res); ++} ++ ++static int rtl8150_suspend(struct usb_interface *intf, pm_message_t message) ++{ ++ rtl8150_t *dev = usb_get_intfdata(intf); ++ ++ netif_device_detach(dev->netdev); ++ ++ if (netif_running(dev->netdev)) { ++ usb_kill_urb(dev->rx_urb); ++ usb_kill_urb(dev->intr_urb); ++ } ++ return 0; ++} ++ ++static int rtl8150_resume(struct usb_interface *intf) ++{ ++ rtl8150_t *dev = usb_get_intfdata(intf); ++ ++ netif_device_attach(dev->netdev); ++ if (netif_running(dev->netdev)) { ++ dev->rx_urb->status = 0; ++ dev->rx_urb->actual_length = 0; ++ read_bulk_callback(dev->rx_urb); ++ ++ dev->intr_urb->status = 0; ++ dev->intr_urb->actual_length = 0; ++ intr_callback(dev->intr_urb); ++ } ++ return 0; ++} ++ ++/* ++** ++** network related part of the code ++** ++*/ ++ ++static void fill_skb_pool(rtl8150_t *dev) ++{ ++ struct sk_buff *skb; ++ int i; ++ ++ for (i = 0; i < RX_SKB_POOL_SIZE; i++) { ++ if (dev->rx_skb_pool[i]) ++ continue; ++ skb = dev_alloc_skb(RTL8150_MTU + 2); ++ if (!skb) { ++ return; ++ } ++ skb_reserve(skb, 2); ++ dev->rx_skb_pool[i] = skb; ++ } ++} ++ ++static void free_skb_pool(rtl8150_t *dev) ++{ ++ int i; ++ ++ for (i = 0; i < RX_SKB_POOL_SIZE; i++) ++ if (dev->rx_skb_pool[i]) ++ dev_kfree_skb(dev->rx_skb_pool[i]); ++} ++ ++static void rx_fixup(unsigned long data) ++{ ++ struct rtl8150 *dev = (struct rtl8150 *)data; ++ struct sk_buff *skb; ++ int status; ++ ++ spin_lock_irq(&dev->rx_pool_lock); ++ fill_skb_pool(dev); ++ spin_unlock_irq(&dev->rx_pool_lock); ++ if (test_bit(RX_URB_FAIL, &dev->flags)) ++ if (dev->rx_skb) ++ goto try_again; ++ spin_lock_irq(&dev->rx_pool_lock); ++ skb = pull_skb(dev); ++ spin_unlock_irq(&dev->rx_pool_lock); ++ if (skb == NULL) ++ goto tlsched; ++ dev->rx_skb = skb; ++ usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), ++ dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev); ++try_again: ++ status = usb_submit_urb(dev->rx_urb, GFP_ATOMIC); ++ if (status == -ENODEV) { ++ netif_device_detach(dev->netdev); ++ } else if (status) { ++ set_bit(RX_URB_FAIL, &dev->flags); ++ goto tlsched; ++ } else { ++ clear_bit(RX_URB_FAIL, &dev->flags); ++ } ++ ++ return; ++tlsched: ++ tasklet_schedule(&dev->tl); ++} ++ ++static int enable_net_traffic(rtl8150_t * dev) ++{ ++ u8 cr, tcr, rcr, msr; ++ ++ if (!rtl8150_reset(dev)) { ++ dev_warn(&dev->udev->dev, "device reset failed\n"); ++ } ++ /* RCR bit7=1 attach Rx info at the end; =0 HW CRC (which is broken) */ ++ rcr = 0x9e; ++ tcr = 0xd8; ++ cr = 0x0c; ++ if (!(rcr & 0x80)) ++ set_bit(RTL8150_HW_CRC, &dev->flags); ++ set_registers(dev, RCR, 1, &rcr); ++ set_registers(dev, TCR, 1, &tcr); ++ set_registers(dev, CR, 1, &cr); ++ get_registers(dev, MSR, 1, &msr); ++ ++ return 0; ++} ++ ++static void disable_net_traffic(rtl8150_t * dev) ++{ ++ u8 cr; ++ ++ get_registers(dev, CR, 1, &cr); ++ cr &= 0xf3; ++ set_registers(dev, CR, 1, &cr); ++} ++ ++static void rtl8150_tx_timeout(struct net_device *netdev) ++{ ++ rtl8150_t *dev = netdev_priv(netdev); ++ dev_warn(&netdev->dev, "Tx timeout.\n"); ++ usb_unlink_urb(dev->tx_urb); ++ netdev->stats.tx_errors++; ++} ++ ++static void rtl8150_set_multicast(struct net_device *netdev) ++{ ++ rtl8150_t *dev = netdev_priv(netdev); ++ u16 rx_creg = 0x9e; ++ ++ netif_stop_queue(netdev); ++ if (netdev->flags & IFF_PROMISC) { ++ rx_creg |= 0x0001; ++ dev_info(&netdev->dev, "%s: promiscuous mode\n", netdev->name); ++ } else if (!netdev_mc_empty(netdev) || ++ (netdev->flags & IFF_ALLMULTI)) { ++ rx_creg &= 0xfffe; ++ rx_creg |= 0x0002; ++ dev_info(&netdev->dev, "%s: allmulti set\n", netdev->name); ++ } else { ++ /* ~RX_MULTICAST, ~RX_PROMISCUOUS */ ++ rx_creg &= 0x00fc; ++ } ++ async_set_registers(dev, RCR, sizeof(rx_creg), rx_creg); ++ netif_wake_queue(netdev); ++} ++ ++static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb, ++ struct net_device *netdev) ++{ ++ rtl8150_t *dev = netdev_priv(netdev); ++ int count, res; ++ ++ netif_stop_queue(netdev); ++ count = (skb->len < 60) ? 60 : skb->len; ++ count = (count & 0x3f) ? count : count + 1; ++ dev->tx_skb = skb; ++ usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), ++ skb->data, count, write_bulk_callback, dev); ++ if ((res = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))) { ++ /* Can we get/handle EPIPE here? */ ++ if (res == -ENODEV) ++ netif_device_detach(dev->netdev); ++ else { ++ dev_warn(&netdev->dev, "failed tx_urb %d\n", res); ++ netdev->stats.tx_errors++; ++ netif_start_queue(netdev); ++ } ++ } else { ++ netdev->stats.tx_packets++; ++ netdev->stats.tx_bytes += skb->len; ++ netdev->trans_start = jiffies; ++ } ++ ++ return NETDEV_TX_OK; ++} ++ ++ ++static void set_carrier(struct net_device *netdev) ++{ ++ rtl8150_t *dev = netdev_priv(netdev); ++ short tmp; ++ ++ get_registers(dev, CSCR, 2, &tmp); ++ if (tmp & CSCR_LINK_STATUS) ++ netif_carrier_on(netdev); ++ else ++ netif_carrier_off(netdev); ++} ++ ++static int rtl8150_open(struct net_device *netdev) ++{ ++ rtl8150_t *dev = netdev_priv(netdev); ++ int res; ++ ++ if (dev->rx_skb == NULL) ++ dev->rx_skb = pull_skb(dev); ++ if (!dev->rx_skb) ++ return -ENOMEM; ++ ++ set_registers(dev, IDR, 6, netdev->dev_addr); ++ ++ usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), ++ dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev); ++ if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL))) { ++ if (res == -ENODEV) ++ netif_device_detach(dev->netdev); ++ dev_warn(&netdev->dev, "rx_urb submit failed: %d\n", res); ++ return res; ++ } ++ usb_fill_int_urb(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev, 3), ++ dev->intr_buff, INTBUFSIZE, intr_callback, ++ dev, dev->intr_interval); ++ if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL))) { ++ if (res == -ENODEV) ++ netif_device_detach(dev->netdev); ++ dev_warn(&netdev->dev, "intr_urb submit failed: %d\n", res); ++ usb_kill_urb(dev->rx_urb); ++ return res; ++ } ++ enable_net_traffic(dev); ++ set_carrier(netdev); ++ netif_start_queue(netdev); ++ ++ return res; ++} ++ ++static int rtl8150_close(struct net_device *netdev) ++{ ++ rtl8150_t *dev = netdev_priv(netdev); ++ int res = 0; ++ ++ netif_stop_queue(netdev); ++ if (!test_bit(RTL8150_UNPLUG, &dev->flags)) ++ disable_net_traffic(dev); ++ unlink_all_urbs(dev); ++ ++ return res; ++} ++ ++static void rtl8150_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) ++{ ++ rtl8150_t *dev = netdev_priv(netdev); ++ ++ strlcpy(info->driver, driver_name, sizeof(info->driver)); ++ strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); ++ usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info)); ++} ++ ++static int rtl8150_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ++{ ++ rtl8150_t *dev = netdev_priv(netdev); ++ short lpa, bmcr; ++ ++ ecmd->supported = (SUPPORTED_10baseT_Half | ++ SUPPORTED_10baseT_Full | ++ SUPPORTED_100baseT_Half | ++ SUPPORTED_100baseT_Full | ++ SUPPORTED_Autoneg | ++ SUPPORTED_TP | SUPPORTED_MII); ++ ecmd->port = PORT_TP; ++ ecmd->transceiver = XCVR_INTERNAL; ++ ecmd->phy_address = dev->phy; ++ get_registers(dev, BMCR, 2, &bmcr); ++ get_registers(dev, ANLP, 2, &lpa); ++ if (bmcr & BMCR_ANENABLE) { ++ u32 speed = ((lpa & (LPA_100HALF | LPA_100FULL)) ? ++ SPEED_100 : SPEED_10); ++ ethtool_cmd_speed_set(ecmd, speed); ++ ecmd->autoneg = AUTONEG_ENABLE; ++ if (speed == SPEED_100) ++ ecmd->duplex = (lpa & LPA_100FULL) ? ++ DUPLEX_FULL : DUPLEX_HALF; ++ else ++ ecmd->duplex = (lpa & LPA_10FULL) ? ++ DUPLEX_FULL : DUPLEX_HALF; ++ } else { ++ ecmd->autoneg = AUTONEG_DISABLE; ++ ethtool_cmd_speed_set(ecmd, ((bmcr & BMCR_SPEED100) ? ++ SPEED_100 : SPEED_10)); ++ ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? ++ DUPLEX_FULL : DUPLEX_HALF; ++ } ++ return 0; ++} ++ ++static const struct ethtool_ops ops = { ++ .get_drvinfo = rtl8150_get_drvinfo, ++ .get_settings = rtl8150_get_settings, ++ .get_link = ethtool_op_get_link ++}; ++ ++static int rtl8150_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) ++{ ++ rtl8150_t *dev = netdev_priv(netdev); ++ u16 *data = (u16 *) & rq->ifr_ifru; ++ int res = 0; ++ ++ switch (cmd) { ++ case SIOCDEVPRIVATE: ++ data[0] = dev->phy; ++ case SIOCDEVPRIVATE + 1: ++ read_mii_word(dev, dev->phy, (data[1] & 0x1f), &data[3]); ++ break; ++ case SIOCDEVPRIVATE + 2: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ write_mii_word(dev, dev->phy, (data[1] & 0x1f), data[2]); ++ break; ++ default: ++ res = -EOPNOTSUPP; ++ } ++ ++ return res; ++} ++ ++static const struct net_device_ops rtl8150_netdev_ops = { ++ .ndo_open = rtl8150_open, ++ .ndo_stop = rtl8150_close, ++ .ndo_do_ioctl = rtl8150_ioctl, ++ .ndo_start_xmit = rtl8150_start_xmit, ++ .ndo_tx_timeout = rtl8150_tx_timeout, ++ .ndo_set_rx_mode = rtl8150_set_multicast, ++ .ndo_set_mac_address = rtl8150_set_mac_address, ++ ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++}; ++ ++static int rtl8150_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ struct usb_device *udev = interface_to_usbdev(intf); ++ rtl8150_t *dev; ++ struct net_device *netdev; ++ ++ netdev = alloc_etherdev(sizeof(rtl8150_t)); ++ if (!netdev) ++ return -ENOMEM; ++ ++ dev = netdev_priv(netdev); ++ ++ dev->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL); ++ if (!dev->intr_buff) { ++ free_netdev(netdev); ++ return -ENOMEM; ++ } ++ ++ tasklet_init(&dev->tl, rx_fixup, (unsigned long)dev); ++ spin_lock_init(&dev->rx_pool_lock); ++ ++ dev->udev = udev; ++ dev->netdev = netdev; ++ netdev->netdev_ops = &rtl8150_netdev_ops; ++ netdev->watchdog_timeo = RTL8150_TX_TIMEOUT; ++ netdev->ethtool_ops = &ops; ++ dev->intr_interval = 100; /* 100ms */ ++ ++ if (!alloc_all_urbs(dev)) { ++ dev_err(&intf->dev, "out of memory\n"); ++ goto out; ++ } ++ if (!rtl8150_reset(dev)) { ++ dev_err(&intf->dev, "couldn't reset the device\n"); ++ goto out1; ++ } ++ fill_skb_pool(dev); ++ set_ethernet_addr(dev); ++ ++ usb_set_intfdata(intf, dev); ++ SET_NETDEV_DEV(netdev, &intf->dev); ++ if (register_netdev(netdev) != 0) { ++ dev_err(&intf->dev, "couldn't register the device\n"); ++ goto out2; ++ } ++ ++ dev_info(&intf->dev, "%s: rtl8150 is detected\n", netdev->name); ++ ++ return 0; ++ ++out2: ++ usb_set_intfdata(intf, NULL); ++ free_skb_pool(dev); ++out1: ++ free_all_urbs(dev); ++out: ++ kfree(dev->intr_buff); ++ free_netdev(netdev); ++ return -EIO; ++} ++ ++static void rtl8150_disconnect(struct usb_interface *intf) ++{ ++ rtl8150_t *dev = usb_get_intfdata(intf); ++ ++ usb_set_intfdata(intf, NULL); ++ if (dev) { ++ set_bit(RTL8150_UNPLUG, &dev->flags); ++ tasklet_kill(&dev->tl); ++ unregister_netdev(dev->netdev); ++ unlink_all_urbs(dev); ++ free_all_urbs(dev); ++ free_skb_pool(dev); ++ if (dev->rx_skb) ++ dev_kfree_skb(dev->rx_skb); ++ kfree(dev->intr_buff); ++ free_netdev(dev->netdev); ++ } ++} ++ ++static struct usb_driver rtl8150_driver = { ++ .name = driver_name, ++ .probe = rtl8150_probe, ++ .disconnect = rtl8150_disconnect, ++ .id_table = rtl8150_table, ++ .suspend = rtl8150_suspend, ++ .resume = rtl8150_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(rtl8150_driver); ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/smsc75xx.c backports-3.18.1-1/drivers/net/usb/smsc75xx.c +--- backports-3.18.1-1.org/drivers/net/usb/smsc75xx.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/smsc75xx.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,2286 @@ ++ /*************************************************************************** ++ * ++ * Copyright (C) 2007-2010 SMSC ++ * ++ * 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, see . ++ * ++ *****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "smsc75xx.h" ++ ++#define SMSC_CHIPNAME "smsc75xx" ++#define SMSC_DRIVER_VERSION "1.0.0" ++#define HS_USB_PKT_SIZE (512) ++#define FS_USB_PKT_SIZE (64) ++#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) ++#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE) ++#define DEFAULT_BULK_IN_DELAY (0x00002000) ++#define MAX_SINGLE_PACKET_SIZE (9000) ++#define LAN75XX_EEPROM_MAGIC (0x7500) ++#define EEPROM_MAC_OFFSET (0x01) ++#define DEFAULT_TX_CSUM_ENABLE (true) ++#define DEFAULT_RX_CSUM_ENABLE (true) ++#define SMSC75XX_INTERNAL_PHY_ID (1) ++#define SMSC75XX_TX_OVERHEAD (8) ++#define MAX_RX_FIFO_SIZE (20 * 1024) ++#define MAX_TX_FIFO_SIZE (12 * 1024) ++#define USB_VENDOR_ID_SMSC (0x0424) ++#define USB_PRODUCT_ID_LAN7500 (0x7500) ++#define USB_PRODUCT_ID_LAN7505 (0x7505) ++#define RXW_PADDING 2 ++#define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \ ++ WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) ++ ++#define SUSPEND_SUSPEND0 (0x01) ++#define SUSPEND_SUSPEND1 (0x02) ++#define SUSPEND_SUSPEND2 (0x04) ++#define SUSPEND_SUSPEND3 (0x08) ++#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ ++ SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) ++ ++struct smsc75xx_priv { ++ struct usbnet *dev; ++ u32 rfe_ctl; ++ u32 wolopts; ++ u32 multicast_hash_table[DP_SEL_VHF_HASH_LEN]; ++ struct mutex dataport_mutex; ++ spinlock_t rfe_ctl_lock; ++ struct work_struct set_multicast; ++ u8 suspend_flags; ++}; ++ ++struct usb_context { ++ struct usb_ctrlrequest req; ++ struct usbnet *dev; ++}; ++ ++static bool turbo_mode = true; ++module_param(turbo_mode, bool, 0644); ++MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); ++ ++static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index, ++ u32 *data, int in_pm) ++{ ++ u32 buf; ++ int ret; ++ int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); ++ ++ BUG_ON(!dev); ++ ++ if (!in_pm) ++ fn = usbnet_read_cmd; ++ else ++ fn = usbnet_read_cmd_nopm; ++ ++ ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN ++ | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ 0, index, &buf, 4); ++ if (unlikely(ret < 0)) ++ netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n", ++ index, ret); ++ ++ le32_to_cpus(&buf); ++ *data = buf; ++ ++ return ret; ++} ++ ++static int __must_check __smsc75xx_write_reg(struct usbnet *dev, u32 index, ++ u32 data, int in_pm) ++{ ++ u32 buf; ++ int ret; ++ int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); ++ ++ BUG_ON(!dev); ++ ++ if (!in_pm) ++ fn = usbnet_write_cmd; ++ else ++ fn = usbnet_write_cmd_nopm; ++ ++ buf = data; ++ cpu_to_le32s(&buf); ++ ++ ret = fn(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT ++ | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ 0, index, &buf, 4); ++ if (unlikely(ret < 0)) ++ netdev_warn(dev->net, "Failed to write reg index 0x%08x: %d\n", ++ index, ret); ++ ++ return ret; ++} ++ ++static int __must_check smsc75xx_read_reg_nopm(struct usbnet *dev, u32 index, ++ u32 *data) ++{ ++ return __smsc75xx_read_reg(dev, index, data, 1); ++} ++ ++static int __must_check smsc75xx_write_reg_nopm(struct usbnet *dev, u32 index, ++ u32 data) ++{ ++ return __smsc75xx_write_reg(dev, index, data, 1); ++} ++ ++static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index, ++ u32 *data) ++{ ++ return __smsc75xx_read_reg(dev, index, data, 0); ++} ++ ++static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index, ++ u32 data) ++{ ++ return __smsc75xx_write_reg(dev, index, data, 0); ++} ++ ++/* Loop until the read is completed with timeout ++ * called with phy_mutex held */ ++static __must_check int __smsc75xx_phy_wait_not_busy(struct usbnet *dev, ++ int in_pm) ++{ ++ unsigned long start_time = jiffies; ++ u32 val; ++ int ret; ++ ++ do { ++ ret = __smsc75xx_read_reg(dev, MII_ACCESS, &val, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading MII_ACCESS\n"); ++ return ret; ++ } ++ ++ if (!(val & MII_ACCESS_BUSY)) ++ return 0; ++ } while (!time_after(jiffies, start_time + HZ)); ++ ++ return -EIO; ++} ++ ++static int __smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx, ++ int in_pm) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ u32 val, addr; ++ int ret; ++ ++ mutex_lock(&dev->phy_mutex); ++ ++ /* confirm MII not busy */ ++ ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_read\n"); ++ goto done; ++ } ++ ++ /* set the address, index & direction (read from PHY) */ ++ phy_id &= dev->mii.phy_id_mask; ++ idx &= dev->mii.reg_num_mask; ++ addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) ++ | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) ++ | MII_ACCESS_READ | MII_ACCESS_BUSY; ++ ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing MII_ACCESS\n"); ++ goto done; ++ } ++ ++ ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx); ++ goto done; ++ } ++ ++ ret = __smsc75xx_read_reg(dev, MII_DATA, &val, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading MII_DATA\n"); ++ goto done; ++ } ++ ++ ret = (u16)(val & 0xFFFF); ++ ++done: ++ mutex_unlock(&dev->phy_mutex); ++ return ret; ++} ++ ++static void __smsc75xx_mdio_write(struct net_device *netdev, int phy_id, ++ int idx, int regval, int in_pm) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ u32 val, addr; ++ int ret; ++ ++ mutex_lock(&dev->phy_mutex); ++ ++ /* confirm MII not busy */ ++ ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_write\n"); ++ goto done; ++ } ++ ++ val = regval; ++ ret = __smsc75xx_write_reg(dev, MII_DATA, val, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing MII_DATA\n"); ++ goto done; ++ } ++ ++ /* set the address, index & direction (write to PHY) */ ++ phy_id &= dev->mii.phy_id_mask; ++ idx &= dev->mii.reg_num_mask; ++ addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) ++ | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) ++ | MII_ACCESS_WRITE | MII_ACCESS_BUSY; ++ ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing MII_ACCESS\n"); ++ goto done; ++ } ++ ++ ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Timed out writing MII reg %02X\n", idx); ++ goto done; ++ } ++ ++done: ++ mutex_unlock(&dev->phy_mutex); ++} ++ ++static int smsc75xx_mdio_read_nopm(struct net_device *netdev, int phy_id, ++ int idx) ++{ ++ return __smsc75xx_mdio_read(netdev, phy_id, idx, 1); ++} ++ ++static void smsc75xx_mdio_write_nopm(struct net_device *netdev, int phy_id, ++ int idx, int regval) ++{ ++ __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 1); ++} ++ ++static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) ++{ ++ return __smsc75xx_mdio_read(netdev, phy_id, idx, 0); ++} ++ ++static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, ++ int regval) ++{ ++ __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 0); ++} ++ ++static int smsc75xx_wait_eeprom(struct usbnet *dev) ++{ ++ unsigned long start_time = jiffies; ++ u32 val; ++ int ret; ++ ++ do { ++ ret = smsc75xx_read_reg(dev, E2P_CMD, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading E2P_CMD\n"); ++ return ret; ++ } ++ ++ if (!(val & E2P_CMD_BUSY) || (val & E2P_CMD_TIMEOUT)) ++ break; ++ udelay(40); ++ } while (!time_after(jiffies, start_time + HZ)); ++ ++ if (val & (E2P_CMD_TIMEOUT | E2P_CMD_BUSY)) { ++ netdev_warn(dev->net, "EEPROM read operation timeout\n"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int smsc75xx_eeprom_confirm_not_busy(struct usbnet *dev) ++{ ++ unsigned long start_time = jiffies; ++ u32 val; ++ int ret; ++ ++ do { ++ ret = smsc75xx_read_reg(dev, E2P_CMD, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading E2P_CMD\n"); ++ return ret; ++ } ++ ++ if (!(val & E2P_CMD_BUSY)) ++ return 0; ++ ++ udelay(40); ++ } while (!time_after(jiffies, start_time + HZ)); ++ ++ netdev_warn(dev->net, "EEPROM is busy\n"); ++ return -EIO; ++} ++ ++static int smsc75xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length, ++ u8 *data) ++{ ++ u32 val; ++ int i, ret; ++ ++ BUG_ON(!dev); ++ BUG_ON(!data); ++ ++ ret = smsc75xx_eeprom_confirm_not_busy(dev); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < length; i++) { ++ val = E2P_CMD_BUSY | E2P_CMD_READ | (offset & E2P_CMD_ADDR); ++ ret = smsc75xx_write_reg(dev, E2P_CMD, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing E2P_CMD\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_wait_eeprom(dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc75xx_read_reg(dev, E2P_DATA, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading E2P_DATA\n"); ++ return ret; ++ } ++ ++ data[i] = val & 0xFF; ++ offset++; ++ } ++ ++ return 0; ++} ++ ++static int smsc75xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length, ++ u8 *data) ++{ ++ u32 val; ++ int i, ret; ++ ++ BUG_ON(!dev); ++ BUG_ON(!data); ++ ++ ret = smsc75xx_eeprom_confirm_not_busy(dev); ++ if (ret) ++ return ret; ++ ++ /* Issue write/erase enable command */ ++ val = E2P_CMD_BUSY | E2P_CMD_EWEN; ++ ret = smsc75xx_write_reg(dev, E2P_CMD, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing E2P_CMD\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_wait_eeprom(dev); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < length; i++) { ++ ++ /* Fill data register */ ++ val = data[i]; ++ ret = smsc75xx_write_reg(dev, E2P_DATA, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing E2P_DATA\n"); ++ return ret; ++ } ++ ++ /* Send "write" command */ ++ val = E2P_CMD_BUSY | E2P_CMD_WRITE | (offset & E2P_CMD_ADDR); ++ ret = smsc75xx_write_reg(dev, E2P_CMD, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing E2P_CMD\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_wait_eeprom(dev); ++ if (ret < 0) ++ return ret; ++ ++ offset++; ++ } ++ ++ return 0; ++} ++ ++static int smsc75xx_dataport_wait_not_busy(struct usbnet *dev) ++{ ++ int i, ret; ++ ++ for (i = 0; i < 100; i++) { ++ u32 dp_sel; ++ ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading DP_SEL\n"); ++ return ret; ++ } ++ ++ if (dp_sel & DP_SEL_DPRDY) ++ return 0; ++ ++ udelay(40); ++ } ++ ++ netdev_warn(dev->net, "smsc75xx_dataport_wait_not_busy timed out\n"); ++ ++ return -EIO; ++} ++ ++static int smsc75xx_dataport_write(struct usbnet *dev, u32 ram_select, u32 addr, ++ u32 length, u32 *buf) ++{ ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ u32 dp_sel; ++ int i, ret; ++ ++ mutex_lock(&pdata->dataport_mutex); ++ ++ ret = smsc75xx_dataport_wait_not_busy(dev); ++ if (ret < 0) { ++ netdev_warn(dev->net, "smsc75xx_dataport_write busy on entry\n"); ++ goto done; ++ } ++ ++ ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading DP_SEL\n"); ++ goto done; ++ } ++ ++ dp_sel &= ~DP_SEL_RSEL; ++ dp_sel |= ram_select; ++ ret = smsc75xx_write_reg(dev, DP_SEL, dp_sel); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing DP_SEL\n"); ++ goto done; ++ } ++ ++ for (i = 0; i < length; i++) { ++ ret = smsc75xx_write_reg(dev, DP_ADDR, addr + i); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing DP_ADDR\n"); ++ goto done; ++ } ++ ++ ret = smsc75xx_write_reg(dev, DP_DATA, buf[i]); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing DP_DATA\n"); ++ goto done; ++ } ++ ++ ret = smsc75xx_write_reg(dev, DP_CMD, DP_CMD_WRITE); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing DP_CMD\n"); ++ goto done; ++ } ++ ++ ret = smsc75xx_dataport_wait_not_busy(dev); ++ if (ret < 0) { ++ netdev_warn(dev->net, "smsc75xx_dataport_write timeout\n"); ++ goto done; ++ } ++ } ++ ++done: ++ mutex_unlock(&pdata->dataport_mutex); ++ return ret; ++} ++ ++/* returns hash bit number for given MAC address */ ++static u32 smsc75xx_hash(char addr[ETH_ALEN]) ++{ ++ return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff; ++} ++ ++static void smsc75xx_deferred_multicast_write(struct work_struct *param) ++{ ++ struct smsc75xx_priv *pdata = ++ container_of(param, struct smsc75xx_priv, set_multicast); ++ struct usbnet *dev = pdata->dev; ++ int ret; ++ ++ netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x\n", ++ pdata->rfe_ctl); ++ ++ smsc75xx_dataport_write(dev, DP_SEL_VHF, DP_SEL_VHF_VLAN_LEN, ++ DP_SEL_VHF_HASH_LEN, pdata->multicast_hash_table); ++ ++ ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); ++ if (ret < 0) ++ netdev_warn(dev->net, "Error writing RFE_CRL\n"); ++} ++ ++static void smsc75xx_set_multicast(struct net_device *netdev) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); ++ ++ pdata->rfe_ctl &= ++ ~(RFE_CTL_AU | RFE_CTL_AM | RFE_CTL_DPF | RFE_CTL_MHF); ++ pdata->rfe_ctl |= RFE_CTL_AB; ++ ++ for (i = 0; i < DP_SEL_VHF_HASH_LEN; i++) ++ pdata->multicast_hash_table[i] = 0; ++ ++ if (dev->net->flags & IFF_PROMISC) { ++ netif_dbg(dev, drv, dev->net, "promiscuous mode enabled\n"); ++ pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_AU; ++ } else if (dev->net->flags & IFF_ALLMULTI) { ++ netif_dbg(dev, drv, dev->net, "receive all multicast enabled\n"); ++ pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_DPF; ++ } else if (!netdev_mc_empty(dev->net)) { ++ struct netdev_hw_addr *ha; ++ ++ netif_dbg(dev, drv, dev->net, "receive multicast hash filter\n"); ++ ++ pdata->rfe_ctl |= RFE_CTL_MHF | RFE_CTL_DPF; ++ ++ netdev_for_each_mc_addr(ha, netdev) { ++ u32 bitnum = smsc75xx_hash(ha->addr); ++ pdata->multicast_hash_table[bitnum / 32] |= ++ (1 << (bitnum % 32)); ++ } ++ } else { ++ netif_dbg(dev, drv, dev->net, "receive own packets only\n"); ++ pdata->rfe_ctl |= RFE_CTL_DPF; ++ } ++ ++ spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); ++ ++ /* defer register writes to a sleepable context */ ++ schedule_work(&pdata->set_multicast); ++} ++ ++static int smsc75xx_update_flowcontrol(struct usbnet *dev, u8 duplex, ++ u16 lcladv, u16 rmtadv) ++{ ++ u32 flow = 0, fct_flow = 0; ++ int ret; ++ ++ if (duplex == DUPLEX_FULL) { ++ u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); ++ ++ if (cap & FLOW_CTRL_TX) { ++ flow = (FLOW_TX_FCEN | 0xFFFF); ++ /* set fct_flow thresholds to 20% and 80% */ ++ fct_flow = (8 << 8) | 32; ++ } ++ ++ if (cap & FLOW_CTRL_RX) ++ flow |= FLOW_RX_FCEN; ++ ++ netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s\n", ++ (cap & FLOW_CTRL_RX ? "enabled" : "disabled"), ++ (cap & FLOW_CTRL_TX ? "enabled" : "disabled")); ++ } else { ++ netif_dbg(dev, link, dev->net, "half duplex\n"); ++ } ++ ++ ret = smsc75xx_write_reg(dev, FLOW, flow); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing FLOW\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_write_reg(dev, FCT_FLOW, fct_flow); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing FCT_FLOW\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int smsc75xx_link_reset(struct usbnet *dev) ++{ ++ struct mii_if_info *mii = &dev->mii; ++ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; ++ u16 lcladv, rmtadv; ++ int ret; ++ ++ /* write to clear phy interrupt status */ ++ smsc75xx_mdio_write(dev->net, mii->phy_id, PHY_INT_SRC, ++ PHY_INT_SRC_CLEAR_ALL); ++ ++ ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing INT_STS\n"); ++ return ret; ++ } ++ ++ mii_check_media(mii, 1, 1); ++ mii_ethtool_gset(&dev->mii, &ecmd); ++ lcladv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE); ++ rmtadv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_LPA); ++ ++ netif_dbg(dev, link, dev->net, "speed: %u duplex: %d lcladv: %04x rmtadv: %04x\n", ++ ethtool_cmd_speed(&ecmd), ecmd.duplex, lcladv, rmtadv); ++ ++ return smsc75xx_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv); ++} ++ ++static void smsc75xx_status(struct usbnet *dev, struct urb *urb) ++{ ++ u32 intdata; ++ ++ if (urb->actual_length != 4) { ++ netdev_warn(dev->net, "unexpected urb length %d\n", ++ urb->actual_length); ++ return; ++ } ++ ++ memcpy(&intdata, urb->transfer_buffer, 4); ++ le32_to_cpus(&intdata); ++ ++ netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata); ++ ++ if (intdata & INT_ENP_PHY_INT) ++ usbnet_defer_kevent(dev, EVENT_LINK_RESET); ++ else ++ netdev_warn(dev->net, "unexpected interrupt, intdata=0x%08X\n", ++ intdata); ++} ++ ++static int smsc75xx_ethtool_get_eeprom_len(struct net_device *net) ++{ ++ return MAX_EEPROM_SIZE; ++} ++ ++static int smsc75xx_ethtool_get_eeprom(struct net_device *netdev, ++ struct ethtool_eeprom *ee, u8 *data) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ ++ ee->magic = LAN75XX_EEPROM_MAGIC; ++ ++ return smsc75xx_read_eeprom(dev, ee->offset, ee->len, data); ++} ++ ++static int smsc75xx_ethtool_set_eeprom(struct net_device *netdev, ++ struct ethtool_eeprom *ee, u8 *data) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ ++ if (ee->magic != LAN75XX_EEPROM_MAGIC) { ++ netdev_warn(dev->net, "EEPROM: magic value mismatch: 0x%x\n", ++ ee->magic); ++ return -EINVAL; ++ } ++ ++ return smsc75xx_write_eeprom(dev, ee->offset, ee->len, data); ++} ++ ++static void smsc75xx_ethtool_get_wol(struct net_device *net, ++ struct ethtool_wolinfo *wolinfo) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ ++ wolinfo->supported = SUPPORTED_WAKE; ++ wolinfo->wolopts = pdata->wolopts; ++} ++ ++static int smsc75xx_ethtool_set_wol(struct net_device *net, ++ struct ethtool_wolinfo *wolinfo) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ int ret; ++ ++ pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; ++ ++ ret = device_set_wakeup_enable(&dev->udev->dev, pdata->wolopts); ++ if (ret < 0) ++ netdev_warn(dev->net, "device_set_wakeup_enable error %d\n", ret); ++ ++ return ret; ++} ++ ++static const struct ethtool_ops smsc75xx_ethtool_ops = { ++ .get_link = usbnet_get_link, ++ .nway_reset = usbnet_nway_reset, ++ .get_drvinfo = usbnet_get_drvinfo, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_settings = usbnet_get_settings, ++ .set_settings = usbnet_set_settings, ++ .get_eeprom_len = smsc75xx_ethtool_get_eeprom_len, ++ .get_eeprom = smsc75xx_ethtool_get_eeprom, ++ .set_eeprom = smsc75xx_ethtool_set_eeprom, ++ .get_wol = smsc75xx_ethtool_get_wol, ++ .set_wol = smsc75xx_ethtool_set_wol, ++}; ++ ++static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ ++ if (!netif_running(netdev)) ++ return -EINVAL; ++ ++ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); ++} ++ ++static void smsc75xx_init_mac_address(struct usbnet *dev) ++{ ++ /* try reading mac address from EEPROM */ ++ if (smsc75xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, ++ dev->net->dev_addr) == 0) { ++ if (is_valid_ether_addr(dev->net->dev_addr)) { ++ /* eeprom values are valid so use them */ ++ netif_dbg(dev, ifup, dev->net, ++ "MAC address read from EEPROM\n"); ++ return; ++ } ++ } ++ ++ /* no eeprom, or eeprom values are invalid. generate random MAC */ ++ eth_hw_addr_random(dev->net); ++ netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); ++} ++ ++static int smsc75xx_set_mac_address(struct usbnet *dev) ++{ ++ u32 addr_lo = dev->net->dev_addr[0] | dev->net->dev_addr[1] << 8 | ++ dev->net->dev_addr[2] << 16 | dev->net->dev_addr[3] << 24; ++ u32 addr_hi = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8; ++ ++ int ret = smsc75xx_write_reg(dev, RX_ADDRH, addr_hi); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write RX_ADDRH: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_write_reg(dev, RX_ADDRL, addr_lo); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write RX_ADDRL: %d\n", ret); ++ return ret; ++ } ++ ++ addr_hi |= ADDR_FILTX_FB_VALID; ++ ret = smsc75xx_write_reg(dev, ADDR_FILTX, addr_hi); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write ADDR_FILTX: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_write_reg(dev, ADDR_FILTX + 4, addr_lo); ++ if (ret < 0) ++ netdev_warn(dev->net, "Failed to write ADDR_FILTX+4: %d\n", ret); ++ ++ return ret; ++} ++ ++static int smsc75xx_phy_initialize(struct usbnet *dev) ++{ ++ int bmcr, ret, timeout = 0; ++ ++ /* Initialize MII structure */ ++ dev->mii.dev = dev->net; ++ dev->mii.mdio_read = smsc75xx_mdio_read; ++ dev->mii.mdio_write = smsc75xx_mdio_write; ++ dev->mii.phy_id_mask = 0x1f; ++ dev->mii.reg_num_mask = 0x1f; ++ dev->mii.supports_gmii = 1; ++ dev->mii.phy_id = SMSC75XX_INTERNAL_PHY_ID; ++ ++ /* reset phy and wait for reset to complete */ ++ smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); ++ ++ do { ++ msleep(10); ++ bmcr = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); ++ if (bmcr < 0) { ++ netdev_warn(dev->net, "Error reading MII_BMCR\n"); ++ return bmcr; ++ } ++ timeout++; ++ } while ((bmcr & BMCR_RESET) && (timeout < 100)); ++ ++ if (timeout >= 100) { ++ netdev_warn(dev->net, "timeout on PHY Reset\n"); ++ return -EIO; ++ } ++ ++ smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ++ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ++ ADVERTISE_PAUSE_ASYM); ++ smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000, ++ ADVERTISE_1000FULL); ++ ++ /* read and write to clear phy interrupt status */ ++ ret = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PHY_INT_SRC\n"); ++ return ret; ++ } ++ ++ smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_SRC, 0xffff); ++ ++ smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, ++ PHY_INT_MASK_DEFAULT); ++ mii_nway_restart(&dev->mii); ++ ++ netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n"); ++ return 0; ++} ++ ++static int smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size) ++{ ++ int ret = 0; ++ u32 buf; ++ bool rxenabled; ++ ++ ret = smsc75xx_read_reg(dev, MAC_RX, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); ++ return ret; ++ } ++ ++ rxenabled = ((buf & MAC_RX_RXEN) != 0); ++ ++ if (rxenabled) { ++ buf &= ~MAC_RX_RXEN; ++ ret = smsc75xx_write_reg(dev, MAC_RX, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ /* add 4 to size for FCS */ ++ buf &= ~MAC_RX_MAX_SIZE; ++ buf |= (((size + 4) << MAC_RX_MAX_SIZE_SHIFT) & MAC_RX_MAX_SIZE); ++ ++ ret = smsc75xx_write_reg(dev, MAC_RX, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); ++ return ret; ++ } ++ ++ if (rxenabled) { ++ buf |= MAC_RX_RXEN; ++ ret = smsc75xx_write_reg(dev, MAC_RX, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int smsc75xx_change_mtu(struct net_device *netdev, int new_mtu) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ int ret; ++ ++ if (new_mtu > MAX_SINGLE_PACKET_SIZE) ++ return -EINVAL; ++ ++ ret = smsc75xx_set_rx_max_frame_length(dev, new_mtu + ETH_HLEN); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to set mac rx frame length\n"); ++ return ret; ++ } ++ ++ return usbnet_change_mtu(netdev, new_mtu); ++} ++ ++/* Enable or disable Rx checksum offload engine */ ++static int smsc75xx_set_features(struct net_device *netdev, ++ netdev_features_t features) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ unsigned long flags; ++ int ret; ++ ++ spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); ++ ++ if (features & NETIF_F_RXCSUM) ++ pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM; ++ else ++ pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM); ++ ++ spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); ++ /* it's racing here! */ ++ ++ ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); ++ if (ret < 0) ++ netdev_warn(dev->net, "Error writing RFE_CTL\n"); ++ ++ return ret; ++} ++ ++static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm) ++{ ++ int timeout = 0; ++ ++ do { ++ u32 buf; ++ int ret; ++ ++ ret = __smsc75xx_read_reg(dev, PMT_CTL, &buf, in_pm); ++ ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ if (buf & PMT_CTL_DEV_RDY) ++ return 0; ++ ++ msleep(10); ++ timeout++; ++ } while (timeout < 100); ++ ++ netdev_warn(dev->net, "timeout waiting for device ready\n"); ++ return -EIO; ++} ++ ++static int smsc75xx_reset(struct usbnet *dev) ++{ ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ u32 buf; ++ int ret = 0, timeout; ++ ++ netif_dbg(dev, ifup, dev->net, "entering smsc75xx_reset\n"); ++ ++ ret = smsc75xx_wait_ready(dev, 0); ++ if (ret < 0) { ++ netdev_warn(dev->net, "device not ready in smsc75xx_reset\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ buf |= HW_CFG_LRST; ++ ++ ret = smsc75xx_write_reg(dev, HW_CFG, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ timeout = 0; ++ do { ++ msleep(10); ++ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); ++ return ret; ++ } ++ timeout++; ++ } while ((buf & HW_CFG_LRST) && (timeout < 100)); ++ ++ if (timeout >= 100) { ++ netdev_warn(dev->net, "timeout on completion of Lite Reset\n"); ++ return -EIO; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "Lite reset complete, resetting PHY\n"); ++ ++ ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ buf |= PMT_CTL_PHY_RST; ++ ++ ret = smsc75xx_write_reg(dev, PMT_CTL, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ timeout = 0; ++ do { ++ msleep(10); ++ ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); ++ return ret; ++ } ++ timeout++; ++ } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100)); ++ ++ if (timeout >= 100) { ++ netdev_warn(dev->net, "timeout waiting for PHY Reset\n"); ++ return -EIO; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "PHY reset complete\n"); ++ ++ ret = smsc75xx_set_mac_address(dev); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to set mac address\n"); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "MAC Address: %pM\n", ++ dev->net->dev_addr); ++ ++ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG : 0x%08x\n", ++ buf); ++ ++ buf |= HW_CFG_BIR; ++ ++ ret = smsc75xx_write_reg(dev, HW_CFG, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG after writing HW_CFG_BIR: 0x%08x\n", ++ buf); ++ ++ if (!turbo_mode) { ++ buf = 0; ++ dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE; ++ } else if (dev->udev->speed == USB_SPEED_HIGH) { ++ buf = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE; ++ dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE; ++ } else { ++ buf = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE; ++ dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n", ++ (ulong)dev->rx_urb_size); ++ ++ ret = smsc75xx_write_reg(dev, BURST_CAP, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write BURST_CAP: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_read_reg(dev, BURST_CAP, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read BURST_CAP: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, ++ "Read Value from BURST_CAP after writing: 0x%08x\n", buf); ++ ++ ret = smsc75xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write BULK_IN_DLY: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_read_reg(dev, BULK_IN_DLY, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read BULK_IN_DLY: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, ++ "Read Value from BULK_IN_DLY after writing: 0x%08x\n", buf); ++ ++ if (turbo_mode) { ++ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x\n", buf); ++ ++ buf |= (HW_CFG_MEF | HW_CFG_BCE); ++ ++ ret = smsc75xx_write_reg(dev, HW_CFG, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_read_reg(dev, HW_CFG, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x\n", buf); ++ } ++ ++ /* set FIFO sizes */ ++ buf = (MAX_RX_FIFO_SIZE - 512) / 512; ++ ret = smsc75xx_write_reg(dev, FCT_RX_FIFO_END, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write FCT_RX_FIFO_END: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "FCT_RX_FIFO_END set to 0x%08x\n", buf); ++ ++ buf = (MAX_TX_FIFO_SIZE - 512) / 512; ++ ret = smsc75xx_write_reg(dev, FCT_TX_FIFO_END, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write FCT_TX_FIFO_END: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "FCT_TX_FIFO_END set to 0x%08x\n", buf); ++ ++ ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write INT_STS: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_read_reg(dev, ID_REV, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read ID_REV: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x\n", buf); ++ ++ ret = smsc75xx_read_reg(dev, E2P_CMD, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read E2P_CMD: %d\n", ret); ++ return ret; ++ } ++ ++ /* only set default GPIO/LED settings if no EEPROM is detected */ ++ if (!(buf & E2P_CMD_LOADED)) { ++ ret = smsc75xx_read_reg(dev, LED_GPIO_CFG, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read LED_GPIO_CFG: %d\n", ret); ++ return ret; ++ } ++ ++ buf &= ~(LED_GPIO_CFG_LED2_FUN_SEL | LED_GPIO_CFG_LED10_FUN_SEL); ++ buf |= LED_GPIO_CFG_LEDGPIO_EN | LED_GPIO_CFG_LED2_FUN_SEL; ++ ++ ret = smsc75xx_write_reg(dev, LED_GPIO_CFG, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write LED_GPIO_CFG: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ ret = smsc75xx_write_reg(dev, FLOW, 0); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write FLOW: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_write_reg(dev, FCT_FLOW, 0); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write FCT_FLOW: %d\n", ret); ++ return ret; ++ } ++ ++ /* Don't need rfe_ctl_lock during initialisation */ ++ ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read RFE_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ pdata->rfe_ctl |= RFE_CTL_AB | RFE_CTL_DPF; ++ ++ ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write RFE_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read RFE_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "RFE_CTL set to 0x%08x\n", ++ pdata->rfe_ctl); ++ ++ /* Enable or disable checksum offload engines */ ++ smsc75xx_set_features(dev->net, dev->net->features); ++ ++ smsc75xx_set_multicast(dev->net); ++ ++ ret = smsc75xx_phy_initialize(dev); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to initialize PHY: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_read_reg(dev, INT_EP_CTL, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read INT_EP_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ /* enable PHY interrupts */ ++ buf |= INT_ENP_PHY_INT; ++ ++ ret = smsc75xx_write_reg(dev, INT_EP_CTL, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write INT_EP_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ /* allow mac to detect speed and duplex from phy */ ++ ret = smsc75xx_read_reg(dev, MAC_CR, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read MAC_CR: %d\n", ret); ++ return ret; ++ } ++ ++ buf |= (MAC_CR_ADD | MAC_CR_ASD); ++ ret = smsc75xx_write_reg(dev, MAC_CR, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write MAC_CR: %d\n", ret); ++ return ret; ++ } ++ ++ ret = smsc75xx_read_reg(dev, MAC_TX, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read MAC_TX: %d\n", ret); ++ return ret; ++ } ++ ++ buf |= MAC_TX_TXEN; ++ ++ ret = smsc75xx_write_reg(dev, MAC_TX, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write MAC_TX: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "MAC_TX set to 0x%08x\n", buf); ++ ++ ret = smsc75xx_read_reg(dev, FCT_TX_CTL, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read FCT_TX_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ buf |= FCT_TX_CTL_EN; ++ ++ ret = smsc75xx_write_reg(dev, FCT_TX_CTL, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write FCT_TX_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "FCT_TX_CTL set to 0x%08x\n", buf); ++ ++ ret = smsc75xx_set_rx_max_frame_length(dev, dev->net->mtu + ETH_HLEN); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to set max rx frame length\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_read_reg(dev, MAC_RX, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); ++ return ret; ++ } ++ ++ buf |= MAC_RX_RXEN; ++ ++ ret = smsc75xx_write_reg(dev, MAC_RX, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "MAC_RX set to 0x%08x\n", buf); ++ ++ ret = smsc75xx_read_reg(dev, FCT_RX_CTL, &buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read FCT_RX_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ buf |= FCT_RX_CTL_EN; ++ ++ ret = smsc75xx_write_reg(dev, FCT_RX_CTL, buf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write FCT_RX_CTL: %d\n", ret); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "FCT_RX_CTL set to 0x%08x\n", buf); ++ ++ netif_dbg(dev, ifup, dev->net, "smsc75xx_reset, return 0\n"); ++ return 0; ++} ++ ++static const struct net_device_ops smsc75xx_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = smsc75xx_change_mtu, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_do_ioctl = smsc75xx_ioctl, ++ .ndo_set_rx_mode = smsc75xx_set_multicast, ++ .ndo_set_features = smsc75xx_set_features, ++}; ++ ++static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ struct smsc75xx_priv *pdata = NULL; ++ int ret; ++ ++ printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); ++ ++ ret = usbnet_get_endpoints(dev, intf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "usbnet_get_endpoints failed: %d\n", ret); ++ return ret; ++ } ++ ++ dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc75xx_priv), ++ GFP_KERNEL); ++ ++ pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ if (!pdata) ++ return -ENOMEM; ++ ++ pdata->dev = dev; ++ ++ spin_lock_init(&pdata->rfe_ctl_lock); ++ mutex_init(&pdata->dataport_mutex); ++ ++ INIT_WORK(&pdata->set_multicast, smsc75xx_deferred_multicast_write); ++ ++ if (DEFAULT_TX_CSUM_ENABLE) ++ dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; ++ ++ if (DEFAULT_RX_CSUM_ENABLE) ++ dev->net->features |= NETIF_F_RXCSUM; ++ ++ dev->net->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_RXCSUM; ++ ++ ret = smsc75xx_wait_ready(dev, 0); ++ if (ret < 0) { ++ netdev_warn(dev->net, "device not ready in smsc75xx_bind\n"); ++ return ret; ++ } ++ ++ smsc75xx_init_mac_address(dev); ++ ++ /* Init all registers */ ++ ret = smsc75xx_reset(dev); ++ if (ret < 0) { ++ netdev_warn(dev->net, "smsc75xx_reset error %d\n", ret); ++ return ret; ++ } ++ ++ dev->net->netdev_ops = &smsc75xx_netdev_ops; ++ dev->net->ethtool_ops = &smsc75xx_ethtool_ops; ++ dev->net->flags |= IFF_MULTICAST; ++ dev->net->hard_header_len += SMSC75XX_TX_OVERHEAD; ++ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; ++ return 0; ++} ++ ++static void smsc75xx_unbind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ if (pdata) { ++ netif_dbg(dev, ifdown, dev->net, "free pdata\n"); ++ kfree(pdata); ++ pdata = NULL; ++ dev->data[0] = 0; ++ } ++} ++ ++static u16 smsc_crc(const u8 *buffer, size_t len) ++{ ++ return bitrev16(crc16(0xFFFF, buffer, len)); ++} ++ ++static int smsc75xx_write_wuff(struct usbnet *dev, int filter, u32 wuf_cfg, ++ u32 wuf_mask1) ++{ ++ int cfg_base = WUF_CFGX + filter * 4; ++ int mask_base = WUF_MASKX + filter * 16; ++ int ret; ++ ++ ret = smsc75xx_write_reg(dev, cfg_base, wuf_cfg); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUF_CFGX\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_write_reg(dev, mask_base, wuf_mask1); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUF_MASKX\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_write_reg(dev, mask_base + 4, 0); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUF_MASKX\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_write_reg(dev, mask_base + 8, 0); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUF_MASKX\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_write_reg(dev, mask_base + 12, 0); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUF_MASKX\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int smsc75xx_enter_suspend0(struct usbnet *dev) ++{ ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ u32 val; ++ int ret; ++ ++ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PMT_CTL\n"); ++ return ret; ++ } ++ ++ val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST)); ++ val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS; ++ ++ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing PMT_CTL\n"); ++ return ret; ++ } ++ ++ pdata->suspend_flags |= SUSPEND_SUSPEND0; ++ ++ return 0; ++} ++ ++static int smsc75xx_enter_suspend1(struct usbnet *dev) ++{ ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ u32 val; ++ int ret; ++ ++ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PMT_CTL\n"); ++ return ret; ++ } ++ ++ val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); ++ val |= PMT_CTL_SUS_MODE_1; ++ ++ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing PMT_CTL\n"); ++ return ret; ++ } ++ ++ /* clear wol status, enable energy detection */ ++ val &= ~PMT_CTL_WUPS; ++ val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN); ++ ++ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing PMT_CTL\n"); ++ return ret; ++ } ++ ++ pdata->suspend_flags |= SUSPEND_SUSPEND1; ++ ++ return 0; ++} ++ ++static int smsc75xx_enter_suspend2(struct usbnet *dev) ++{ ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ u32 val; ++ int ret; ++ ++ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PMT_CTL\n"); ++ return ret; ++ } ++ ++ val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); ++ val |= PMT_CTL_SUS_MODE_2; ++ ++ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing PMT_CTL\n"); ++ return ret; ++ } ++ ++ pdata->suspend_flags |= SUSPEND_SUSPEND2; ++ ++ return 0; ++} ++ ++static int smsc75xx_enter_suspend3(struct usbnet *dev) ++{ ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ u32 val; ++ int ret; ++ ++ ret = smsc75xx_read_reg_nopm(dev, FCT_RX_CTL, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading FCT_RX_CTL\n"); ++ return ret; ++ } ++ ++ if (val & FCT_RX_CTL_RXUSED) { ++ netdev_dbg(dev->net, "rx fifo not empty in autosuspend\n"); ++ return -EBUSY; ++ } ++ ++ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PMT_CTL\n"); ++ return ret; ++ } ++ ++ val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); ++ val |= PMT_CTL_SUS_MODE_3 | PMT_CTL_RES_CLR_WKP_EN; ++ ++ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing PMT_CTL\n"); ++ return ret; ++ } ++ ++ /* clear wol status */ ++ val &= ~PMT_CTL_WUPS; ++ val |= PMT_CTL_WUPS_WOL; ++ ++ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing PMT_CTL\n"); ++ return ret; ++ } ++ ++ pdata->suspend_flags |= SUSPEND_SUSPEND3; ++ ++ return 0; ++} ++ ++static int smsc75xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) ++{ ++ struct mii_if_info *mii = &dev->mii; ++ int ret; ++ ++ netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n"); ++ ++ /* read to clear */ ++ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PHY_INT_SRC\n"); ++ return ret; ++ } ++ ++ /* enable interrupt source */ ++ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PHY_INT_MASK\n"); ++ return ret; ++ } ++ ++ ret |= mask; ++ ++ smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret); ++ ++ return 0; ++} ++ ++static int smsc75xx_link_ok_nopm(struct usbnet *dev) ++{ ++ struct mii_if_info *mii = &dev->mii; ++ int ret; ++ ++ /* first, a dummy read, needed to latch some MII phys */ ++ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading MII_BMSR\n"); ++ return ret; ++ } ++ ++ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading MII_BMSR\n"); ++ return ret; ++ } ++ ++ return !!(ret & BMSR_LSTATUS); ++} ++ ++static int smsc75xx_autosuspend(struct usbnet *dev, u32 link_up) ++{ ++ int ret; ++ ++ if (!netif_running(dev->net)) { ++ /* interface is ifconfig down so fully power down hw */ ++ netdev_dbg(dev->net, "autosuspend entering SUSPEND2\n"); ++ return smsc75xx_enter_suspend2(dev); ++ } ++ ++ if (!link_up) { ++ /* link is down so enter EDPD mode */ ++ netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n"); ++ ++ /* enable PHY wakeup events for if cable is attached */ ++ ret = smsc75xx_enable_phy_wakeup_interrupts(dev, ++ PHY_INT_MASK_ANEG_COMP); ++ if (ret < 0) { ++ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); ++ return ret; ++ } ++ ++ netdev_info(dev->net, "entering SUSPEND1 mode\n"); ++ return smsc75xx_enter_suspend1(dev); ++ } ++ ++ /* enable PHY wakeup events so we remote wakeup if cable is pulled */ ++ ret = smsc75xx_enable_phy_wakeup_interrupts(dev, ++ PHY_INT_MASK_LINK_DOWN); ++ if (ret < 0) { ++ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); ++ return ret; ++ } ++ ++ netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n"); ++ return smsc75xx_enter_suspend3(dev); ++} ++ ++static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) ++{ ++ struct usbnet *dev = usb_get_intfdata(intf); ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ u32 val, link_up; ++ int ret; ++ ++ ret = usbnet_suspend(intf, message); ++ if (ret < 0) { ++ netdev_warn(dev->net, "usbnet_suspend error\n"); ++ return ret; ++ } ++ ++ if (pdata->suspend_flags) { ++ netdev_warn(dev->net, "error during last resume\n"); ++ pdata->suspend_flags = 0; ++ } ++ ++ /* determine if link is up using only _nopm functions */ ++ link_up = smsc75xx_link_ok_nopm(dev); ++ ++ if (message.event == PM_EVENT_AUTO_SUSPEND) { ++ ret = smsc75xx_autosuspend(dev, link_up); ++ goto done; ++ } ++ ++ /* if we get this far we're not autosuspending */ ++ /* if no wol options set, or if link is down and we're not waking on ++ * PHY activity, enter lowest power SUSPEND2 mode ++ */ ++ if (!(pdata->wolopts & SUPPORTED_WAKE) || ++ !(link_up || (pdata->wolopts & WAKE_PHY))) { ++ netdev_info(dev->net, "entering SUSPEND2 mode\n"); ++ ++ /* disable energy detect (link up) & wake up events */ ++ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading WUCSR\n"); ++ goto done; ++ } ++ ++ val &= ~(WUCSR_MPEN | WUCSR_WUEN); ++ ++ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUCSR\n"); ++ goto done; ++ } ++ ++ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PMT_CTL\n"); ++ goto done; ++ } ++ ++ val &= ~(PMT_CTL_ED_EN | PMT_CTL_WOL_EN); ++ ++ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing PMT_CTL\n"); ++ goto done; ++ } ++ ++ ret = smsc75xx_enter_suspend2(dev); ++ goto done; ++ } ++ ++ if (pdata->wolopts & WAKE_PHY) { ++ ret = smsc75xx_enable_phy_wakeup_interrupts(dev, ++ (PHY_INT_MASK_ANEG_COMP | PHY_INT_MASK_LINK_DOWN)); ++ if (ret < 0) { ++ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); ++ goto done; ++ } ++ ++ /* if link is down then configure EDPD and enter SUSPEND1, ++ * otherwise enter SUSPEND0 below ++ */ ++ if (!link_up) { ++ struct mii_if_info *mii = &dev->mii; ++ netdev_info(dev->net, "entering SUSPEND1 mode\n"); ++ ++ /* enable energy detect power-down mode */ ++ ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, ++ PHY_MODE_CTRL_STS); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PHY_MODE_CTRL_STS\n"); ++ goto done; ++ } ++ ++ ret |= MODE_CTRL_STS_EDPWRDOWN; ++ ++ smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, ++ PHY_MODE_CTRL_STS, ret); ++ ++ /* enter SUSPEND1 mode */ ++ ret = smsc75xx_enter_suspend1(dev); ++ goto done; ++ } ++ } ++ ++ if (pdata->wolopts & (WAKE_MCAST | WAKE_ARP)) { ++ int i, filter = 0; ++ ++ /* disable all filters */ ++ for (i = 0; i < WUF_NUM; i++) { ++ ret = smsc75xx_write_reg_nopm(dev, WUF_CFGX + i * 4, 0); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUF_CFGX\n"); ++ goto done; ++ } ++ } ++ ++ if (pdata->wolopts & WAKE_MCAST) { ++ const u8 mcast[] = {0x01, 0x00, 0x5E}; ++ netdev_info(dev->net, "enabling multicast detection\n"); ++ ++ val = WUF_CFGX_EN | WUF_CFGX_ATYPE_MULTICAST ++ | smsc_crc(mcast, 3); ++ ret = smsc75xx_write_wuff(dev, filter++, val, 0x0007); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing wakeup filter\n"); ++ goto done; ++ } ++ } ++ ++ if (pdata->wolopts & WAKE_ARP) { ++ const u8 arp[] = {0x08, 0x06}; ++ netdev_info(dev->net, "enabling ARP detection\n"); ++ ++ val = WUF_CFGX_EN | WUF_CFGX_ATYPE_ALL | (0x0C << 16) ++ | smsc_crc(arp, 2); ++ ret = smsc75xx_write_wuff(dev, filter++, val, 0x0003); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing wakeup filter\n"); ++ goto done; ++ } ++ } ++ ++ /* clear any pending pattern match packet status */ ++ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading WUCSR\n"); ++ goto done; ++ } ++ ++ val |= WUCSR_WUFR; ++ ++ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUCSR\n"); ++ goto done; ++ } ++ ++ netdev_info(dev->net, "enabling packet match detection\n"); ++ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading WUCSR\n"); ++ goto done; ++ } ++ ++ val |= WUCSR_WUEN; ++ ++ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUCSR\n"); ++ goto done; ++ } ++ } else { ++ netdev_info(dev->net, "disabling packet match detection\n"); ++ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading WUCSR\n"); ++ goto done; ++ } ++ ++ val &= ~WUCSR_WUEN; ++ ++ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUCSR\n"); ++ goto done; ++ } ++ } ++ ++ /* disable magic, bcast & unicast wakeup sources */ ++ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading WUCSR\n"); ++ goto done; ++ } ++ ++ val &= ~(WUCSR_MPEN | WUCSR_BCST_EN | WUCSR_PFDA_EN); ++ ++ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUCSR\n"); ++ goto done; ++ } ++ ++ if (pdata->wolopts & WAKE_PHY) { ++ netdev_info(dev->net, "enabling PHY wakeup\n"); ++ ++ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PMT_CTL\n"); ++ goto done; ++ } ++ ++ /* clear wol status, enable energy detection */ ++ val &= ~PMT_CTL_WUPS; ++ val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN); ++ ++ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing PMT_CTL\n"); ++ goto done; ++ } ++ } ++ ++ if (pdata->wolopts & WAKE_MAGIC) { ++ netdev_info(dev->net, "enabling magic packet wakeup\n"); ++ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading WUCSR\n"); ++ goto done; ++ } ++ ++ /* clear any pending magic packet status */ ++ val |= WUCSR_MPR | WUCSR_MPEN; ++ ++ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUCSR\n"); ++ goto done; ++ } ++ } ++ ++ if (pdata->wolopts & WAKE_BCAST) { ++ netdev_info(dev->net, "enabling broadcast detection\n"); ++ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading WUCSR\n"); ++ goto done; ++ } ++ ++ val |= WUCSR_BCAST_FR | WUCSR_BCST_EN; ++ ++ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUCSR\n"); ++ goto done; ++ } ++ } ++ ++ if (pdata->wolopts & WAKE_UCAST) { ++ netdev_info(dev->net, "enabling unicast detection\n"); ++ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading WUCSR\n"); ++ goto done; ++ } ++ ++ val |= WUCSR_WUFR | WUCSR_PFDA_EN; ++ ++ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUCSR\n"); ++ goto done; ++ } ++ } ++ ++ /* enable receiver to enable frame reception */ ++ ret = smsc75xx_read_reg_nopm(dev, MAC_RX, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); ++ goto done; ++ } ++ ++ val |= MAC_RX_RXEN; ++ ++ ret = smsc75xx_write_reg_nopm(dev, MAC_RX, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); ++ goto done; ++ } ++ ++ /* some wol options are enabled, so enter SUSPEND0 */ ++ netdev_info(dev->net, "entering SUSPEND0 mode\n"); ++ ret = smsc75xx_enter_suspend0(dev); ++ ++done: ++ /* ++ * TODO: resume() might need to handle the suspend failure ++ * in system sleep ++ */ ++ if (ret && PMSG_IS_AUTO(message)) ++ usbnet_resume(intf); ++ return ret; ++} ++ ++static int smsc75xx_resume(struct usb_interface *intf) ++{ ++ struct usbnet *dev = usb_get_intfdata(intf); ++ struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); ++ u8 suspend_flags = pdata->suspend_flags; ++ int ret; ++ u32 val; ++ ++ netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags); ++ ++ /* do this first to ensure it's cleared even in error case */ ++ pdata->suspend_flags = 0; ++ ++ if (suspend_flags & SUSPEND_ALLMODES) { ++ /* Disable wakeup sources */ ++ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading WUCSR\n"); ++ return ret; ++ } ++ ++ val &= ~(WUCSR_WUEN | WUCSR_MPEN | WUCSR_PFDA_EN ++ | WUCSR_BCST_EN); ++ ++ ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing WUCSR\n"); ++ return ret; ++ } ++ ++ /* clear wake-up status */ ++ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PMT_CTL\n"); ++ return ret; ++ } ++ ++ val &= ~PMT_CTL_WOL_EN; ++ val |= PMT_CTL_WUPS; ++ ++ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing PMT_CTL\n"); ++ return ret; ++ } ++ } ++ ++ if (suspend_flags & SUSPEND_SUSPEND2) { ++ netdev_info(dev->net, "resuming from SUSPEND2\n"); ++ ++ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading PMT_CTL\n"); ++ return ret; ++ } ++ ++ val |= PMT_CTL_PHY_PWRUP; ++ ++ ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing PMT_CTL\n"); ++ return ret; ++ } ++ } ++ ++ ret = smsc75xx_wait_ready(dev, 1); ++ if (ret < 0) { ++ netdev_warn(dev->net, "device not ready in smsc75xx_resume\n"); ++ return ret; ++ } ++ ++ return usbnet_resume(intf); ++} ++ ++static void smsc75xx_rx_csum_offload(struct usbnet *dev, struct sk_buff *skb, ++ u32 rx_cmd_a, u32 rx_cmd_b) ++{ ++ if (!(dev->net->features & NETIF_F_RXCSUM) || ++ unlikely(rx_cmd_a & RX_CMD_A_LCSM)) { ++ skb->ip_summed = CHECKSUM_NONE; ++ } else { ++ skb->csum = ntohs((u16)(rx_cmd_b >> RX_CMD_B_CSUM_SHIFT)); ++ skb->ip_summed = CHECKSUM_COMPLETE; ++ } ++} ++ ++static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ /* This check is no longer done by usbnet */ ++ if (skb->len < dev->net->hard_header_len) ++ return 0; ++ ++ while (skb->len > 0) { ++ u32 rx_cmd_a, rx_cmd_b, align_count, size; ++ struct sk_buff *ax_skb; ++ unsigned char *packet; ++ ++ memcpy(&rx_cmd_a, skb->data, sizeof(rx_cmd_a)); ++ le32_to_cpus(&rx_cmd_a); ++ skb_pull(skb, 4); ++ ++ memcpy(&rx_cmd_b, skb->data, sizeof(rx_cmd_b)); ++ le32_to_cpus(&rx_cmd_b); ++ skb_pull(skb, 4 + RXW_PADDING); ++ ++ packet = skb->data; ++ ++ /* get the packet length */ ++ size = (rx_cmd_a & RX_CMD_A_LEN) - RXW_PADDING; ++ align_count = (4 - ((size + RXW_PADDING) % 4)) % 4; ++ ++ if (unlikely(rx_cmd_a & RX_CMD_A_RED)) { ++ netif_dbg(dev, rx_err, dev->net, ++ "Error rx_cmd_a=0x%08x\n", rx_cmd_a); ++ dev->net->stats.rx_errors++; ++ dev->net->stats.rx_dropped++; ++ ++ if (rx_cmd_a & RX_CMD_A_FCS) ++ dev->net->stats.rx_crc_errors++; ++ else if (rx_cmd_a & (RX_CMD_A_LONG | RX_CMD_A_RUNT)) ++ dev->net->stats.rx_frame_errors++; ++ } else { ++ /* MAX_SINGLE_PACKET_SIZE + 4(CRC) + 2(COE) + 4(Vlan) */ ++ if (unlikely(size > (MAX_SINGLE_PACKET_SIZE + ETH_HLEN + 12))) { ++ netif_dbg(dev, rx_err, dev->net, ++ "size err rx_cmd_a=0x%08x\n", ++ rx_cmd_a); ++ return 0; ++ } ++ ++ /* last frame in this batch */ ++ if (skb->len == size) { ++ smsc75xx_rx_csum_offload(dev, skb, rx_cmd_a, ++ rx_cmd_b); ++ ++ skb_trim(skb, skb->len - 4); /* remove fcs */ ++ skb->truesize = size + sizeof(struct sk_buff); ++ ++ return 1; ++ } ++ ++ ax_skb = skb_clone(skb, GFP_ATOMIC); ++ if (unlikely(!ax_skb)) { ++ netdev_warn(dev->net, "Error allocating skb\n"); ++ return 0; ++ } ++ ++ ax_skb->len = size; ++ ax_skb->data = packet; ++ skb_set_tail_pointer(ax_skb, size); ++ ++ smsc75xx_rx_csum_offload(dev, ax_skb, rx_cmd_a, ++ rx_cmd_b); ++ ++ skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */ ++ ax_skb->truesize = size + sizeof(struct sk_buff); ++ ++ usbnet_skb_return(dev, ax_skb); ++ } ++ ++ skb_pull(skb, size); ++ ++ /* padding bytes before the next frame starts */ ++ if (skb->len) ++ skb_pull(skb, align_count); ++ } ++ ++ if (unlikely(skb->len < 0)) { ++ netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev, ++ struct sk_buff *skb, gfp_t flags) ++{ ++ u32 tx_cmd_a, tx_cmd_b; ++ ++ if (skb_headroom(skb) < SMSC75XX_TX_OVERHEAD) { ++ struct sk_buff *skb2 = ++ skb_copy_expand(skb, SMSC75XX_TX_OVERHEAD, 0, flags); ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ if (!skb) ++ return NULL; ++ } ++ ++ tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN) | TX_CMD_A_FCS; ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ tx_cmd_a |= TX_CMD_A_IPE | TX_CMD_A_TPE; ++ ++ if (skb_is_gso(skb)) { ++ u16 mss = max(skb_shinfo(skb)->gso_size, TX_MSS_MIN); ++ tx_cmd_b = (mss << TX_CMD_B_MSS_SHIFT) & TX_CMD_B_MSS; ++ ++ tx_cmd_a |= TX_CMD_A_LSO; ++ } else { ++ tx_cmd_b = 0; ++ } ++ ++ skb_push(skb, 4); ++ cpu_to_le32s(&tx_cmd_b); ++ memcpy(skb->data, &tx_cmd_b, 4); ++ ++ skb_push(skb, 4); ++ cpu_to_le32s(&tx_cmd_a); ++ memcpy(skb->data, &tx_cmd_a, 4); ++ ++ return skb; ++} ++ ++static int smsc75xx_manage_power(struct usbnet *dev, int on) ++{ ++ dev->intf->needs_remote_wakeup = on; ++ return 0; ++} ++ ++static const struct driver_info smsc75xx_info = { ++ .description = "smsc75xx USB 2.0 Gigabit Ethernet", ++ .bind = smsc75xx_bind, ++ .unbind = smsc75xx_unbind, ++ .link_reset = smsc75xx_link_reset, ++ .reset = smsc75xx_reset, ++ .rx_fixup = smsc75xx_rx_fixup, ++ .tx_fixup = smsc75xx_tx_fixup, ++ .status = smsc75xx_status, ++ .manage_power = smsc75xx_manage_power, ++ .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR, ++}; ++ ++static const struct usb_device_id products[] = { ++ { ++ /* SMSC7500 USB Gigabit Ethernet Device */ ++ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_PRODUCT_ID_LAN7500), ++ .driver_info = (unsigned long) &smsc75xx_info, ++ }, ++ { ++ /* SMSC7500 USB Gigabit Ethernet Device */ ++ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_PRODUCT_ID_LAN7505), ++ .driver_info = (unsigned long) &smsc75xx_info, ++ }, ++ { }, /* END */ ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver smsc75xx_driver = { ++ .name = SMSC_CHIPNAME, ++ .id_table = products, ++ .probe = usbnet_probe, ++ .suspend = smsc75xx_suspend, ++ .resume = smsc75xx_resume, ++ .reset_resume = smsc75xx_resume, ++ .disconnect = usbnet_disconnect, ++ .disable_hub_initiated_lpm = 1, ++ .supports_autosuspend = 1, ++}; ++ ++module_usb_driver(smsc75xx_driver); ++ ++MODULE_AUTHOR("Nancy Lin"); ++MODULE_AUTHOR("Steve Glendinning "); ++MODULE_DESCRIPTION("SMSC75XX USB 2.0 Gigabit Ethernet Devices"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/smsc75xx.h backports-3.18.1-1/drivers/net/usb/smsc75xx.h +--- backports-3.18.1-1.org/drivers/net/usb/smsc75xx.h 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/smsc75xx.h 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,421 @@ ++ /*************************************************************************** ++ * ++ * Copyright (C) 2007-2010 SMSC ++ * ++ * 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, see . ++ * ++ *****************************************************************************/ ++ ++#ifndef _SMSC75XX_H ++#define _SMSC75XX_H ++ ++/* Tx command words */ ++#define TX_CMD_A_LSO (0x08000000) ++#define TX_CMD_A_IPE (0x04000000) ++#define TX_CMD_A_TPE (0x02000000) ++#define TX_CMD_A_IVTG (0x01000000) ++#define TX_CMD_A_RVTG (0x00800000) ++#define TX_CMD_A_FCS (0x00400000) ++#define TX_CMD_A_LEN (0x000FFFFF) ++ ++#define TX_CMD_B_MSS (0x3FFF0000) ++#define TX_CMD_B_MSS_SHIFT (16) ++#define TX_MSS_MIN ((u16)8) ++#define TX_CMD_B_VTAG (0x0000FFFF) ++ ++/* Rx command words */ ++#define RX_CMD_A_ICE (0x80000000) ++#define RX_CMD_A_TCE (0x40000000) ++#define RX_CMD_A_IPV (0x20000000) ++#define RX_CMD_A_PID (0x18000000) ++#define RX_CMD_A_PID_NIP (0x00000000) ++#define RX_CMD_A_PID_TCP (0x08000000) ++#define RX_CMD_A_PID_UDP (0x10000000) ++#define RX_CMD_A_PID_PP (0x18000000) ++#define RX_CMD_A_PFF (0x04000000) ++#define RX_CMD_A_BAM (0x02000000) ++#define RX_CMD_A_MAM (0x01000000) ++#define RX_CMD_A_FVTG (0x00800000) ++#define RX_CMD_A_RED (0x00400000) ++#define RX_CMD_A_RWT (0x00200000) ++#define RX_CMD_A_RUNT (0x00100000) ++#define RX_CMD_A_LONG (0x00080000) ++#define RX_CMD_A_RXE (0x00040000) ++#define RX_CMD_A_DRB (0x00020000) ++#define RX_CMD_A_FCS (0x00010000) ++#define RX_CMD_A_UAM (0x00008000) ++#define RX_CMD_A_LCSM (0x00004000) ++#define RX_CMD_A_LEN (0x00003FFF) ++ ++#define RX_CMD_B_CSUM (0xFFFF0000) ++#define RX_CMD_B_CSUM_SHIFT (16) ++#define RX_CMD_B_VTAG (0x0000FFFF) ++ ++/* SCSRs */ ++#define ID_REV (0x0000) ++ ++#define FPGA_REV (0x0004) ++ ++#define BOND_CTL (0x0008) ++ ++#define INT_STS (0x000C) ++#define INT_STS_RDFO_INT (0x00400000) ++#define INT_STS_TXE_INT (0x00200000) ++#define INT_STS_MACRTO_INT (0x00100000) ++#define INT_STS_TX_DIS_INT (0x00080000) ++#define INT_STS_RX_DIS_INT (0x00040000) ++#define INT_STS_PHY_INT_ (0x00020000) ++#define INT_STS_MAC_ERR_INT (0x00008000) ++#define INT_STS_TDFU (0x00004000) ++#define INT_STS_TDFO (0x00002000) ++#define INT_STS_GPIOS (0x00000FFF) ++#define INT_STS_CLEAR_ALL (0xFFFFFFFF) ++ ++#define HW_CFG (0x0010) ++#define HW_CFG_SMDET_STS (0x00008000) ++#define HW_CFG_SMDET_EN (0x00004000) ++#define HW_CFG_EEM (0x00002000) ++#define HW_CFG_RST_PROTECT (0x00001000) ++#define HW_CFG_PORT_SWAP (0x00000800) ++#define HW_CFG_PHY_BOOST (0x00000600) ++#define HW_CFG_PHY_BOOST_NORMAL (0x00000000) ++#define HW_CFG_PHY_BOOST_4 (0x00002000) ++#define HW_CFG_PHY_BOOST_8 (0x00004000) ++#define HW_CFG_PHY_BOOST_12 (0x00006000) ++#define HW_CFG_LEDB (0x00000100) ++#define HW_CFG_BIR (0x00000080) ++#define HW_CFG_SBP (0x00000040) ++#define HW_CFG_IME (0x00000020) ++#define HW_CFG_MEF (0x00000010) ++#define HW_CFG_ETC (0x00000008) ++#define HW_CFG_BCE (0x00000004) ++#define HW_CFG_LRST (0x00000002) ++#define HW_CFG_SRST (0x00000001) ++ ++#define PMT_CTL (0x0014) ++#define PMT_CTL_PHY_PWRUP (0x00000400) ++#define PMT_CTL_RES_CLR_WKP_EN (0x00000100) ++#define PMT_CTL_DEV_RDY (0x00000080) ++#define PMT_CTL_SUS_MODE (0x00000060) ++#define PMT_CTL_SUS_MODE_0 (0x00000000) ++#define PMT_CTL_SUS_MODE_1 (0x00000020) ++#define PMT_CTL_SUS_MODE_2 (0x00000040) ++#define PMT_CTL_SUS_MODE_3 (0x00000060) ++#define PMT_CTL_PHY_RST (0x00000010) ++#define PMT_CTL_WOL_EN (0x00000008) ++#define PMT_CTL_ED_EN (0x00000004) ++#define PMT_CTL_WUPS (0x00000003) ++#define PMT_CTL_WUPS_NO (0x00000000) ++#define PMT_CTL_WUPS_ED (0x00000001) ++#define PMT_CTL_WUPS_WOL (0x00000002) ++#define PMT_CTL_WUPS_MULTI (0x00000003) ++ ++#define LED_GPIO_CFG (0x0018) ++#define LED_GPIO_CFG_LED2_FUN_SEL (0x80000000) ++#define LED_GPIO_CFG_LED10_FUN_SEL (0x40000000) ++#define LED_GPIO_CFG_LEDGPIO_EN (0x0000F000) ++#define LED_GPIO_CFG_LEDGPIO_EN_0 (0x00001000) ++#define LED_GPIO_CFG_LEDGPIO_EN_1 (0x00002000) ++#define LED_GPIO_CFG_LEDGPIO_EN_2 (0x00004000) ++#define LED_GPIO_CFG_LEDGPIO_EN_3 (0x00008000) ++#define LED_GPIO_CFG_GPBUF (0x00000F00) ++#define LED_GPIO_CFG_GPBUF_0 (0x00000100) ++#define LED_GPIO_CFG_GPBUF_1 (0x00000200) ++#define LED_GPIO_CFG_GPBUF_2 (0x00000400) ++#define LED_GPIO_CFG_GPBUF_3 (0x00000800) ++#define LED_GPIO_CFG_GPDIR (0x000000F0) ++#define LED_GPIO_CFG_GPDIR_0 (0x00000010) ++#define LED_GPIO_CFG_GPDIR_1 (0x00000020) ++#define LED_GPIO_CFG_GPDIR_2 (0x00000040) ++#define LED_GPIO_CFG_GPDIR_3 (0x00000080) ++#define LED_GPIO_CFG_GPDATA (0x0000000F) ++#define LED_GPIO_CFG_GPDATA_0 (0x00000001) ++#define LED_GPIO_CFG_GPDATA_1 (0x00000002) ++#define LED_GPIO_CFG_GPDATA_2 (0x00000004) ++#define LED_GPIO_CFG_GPDATA_3 (0x00000008) ++ ++#define GPIO_CFG (0x001C) ++#define GPIO_CFG_SHIFT (24) ++#define GPIO_CFG_GPEN (0xFF000000) ++#define GPIO_CFG_GPBUF (0x00FF0000) ++#define GPIO_CFG_GPDIR (0x0000FF00) ++#define GPIO_CFG_GPDATA (0x000000FF) ++ ++#define GPIO_WAKE (0x0020) ++#define GPIO_WAKE_PHY_LINKUP_EN (0x80000000) ++#define GPIO_WAKE_POL (0x0FFF0000) ++#define GPIO_WAKE_POL_SHIFT (16) ++#define GPIO_WAKE_WK (0x00000FFF) ++ ++#define DP_SEL (0x0024) ++#define DP_SEL_DPRDY (0x80000000) ++#define DP_SEL_RSEL (0x0000000F) ++#define DP_SEL_URX (0x00000000) ++#define DP_SEL_VHF (0x00000001) ++#define DP_SEL_VHF_HASH_LEN (16) ++#define DP_SEL_VHF_VLAN_LEN (128) ++#define DP_SEL_LSO_HEAD (0x00000002) ++#define DP_SEL_FCT_RX (0x00000003) ++#define DP_SEL_FCT_TX (0x00000004) ++#define DP_SEL_DESCRIPTOR (0x00000005) ++#define DP_SEL_WOL (0x00000006) ++ ++#define DP_CMD (0x0028) ++#define DP_CMD_WRITE (0x01) ++#define DP_CMD_READ (0x00) ++ ++#define DP_ADDR (0x002C) ++ ++#define DP_DATA (0x0030) ++ ++#define BURST_CAP (0x0034) ++#define BURST_CAP_MASK (0x0000000F) ++ ++#define INT_EP_CTL (0x0038) ++#define INT_EP_CTL_INTEP_ON (0x80000000) ++#define INT_EP_CTL_RDFO_EN (0x00400000) ++#define INT_EP_CTL_TXE_EN (0x00200000) ++#define INT_EP_CTL_MACROTO_EN (0x00100000) ++#define INT_EP_CTL_TX_DIS_EN (0x00080000) ++#define INT_EP_CTL_RX_DIS_EN (0x00040000) ++#define INT_EP_CTL_PHY_EN_ (0x00020000) ++#define INT_EP_CTL_MAC_ERR_EN (0x00008000) ++#define INT_EP_CTL_TDFU_EN (0x00004000) ++#define INT_EP_CTL_TDFO_EN (0x00002000) ++#define INT_EP_CTL_RX_FIFO_EN (0x00001000) ++#define INT_EP_CTL_GPIOX_EN (0x00000FFF) ++ ++#define BULK_IN_DLY (0x003C) ++#define BULK_IN_DLY_MASK (0xFFFF) ++ ++#define E2P_CMD (0x0040) ++#define E2P_CMD_BUSY (0x80000000) ++#define E2P_CMD_MASK (0x70000000) ++#define E2P_CMD_READ (0x00000000) ++#define E2P_CMD_EWDS (0x10000000) ++#define E2P_CMD_EWEN (0x20000000) ++#define E2P_CMD_WRITE (0x30000000) ++#define E2P_CMD_WRAL (0x40000000) ++#define E2P_CMD_ERASE (0x50000000) ++#define E2P_CMD_ERAL (0x60000000) ++#define E2P_CMD_RELOAD (0x70000000) ++#define E2P_CMD_TIMEOUT (0x00000400) ++#define E2P_CMD_LOADED (0x00000200) ++#define E2P_CMD_ADDR (0x000001FF) ++ ++#define MAX_EEPROM_SIZE (512) ++ ++#define E2P_DATA (0x0044) ++#define E2P_DATA_MASK_ (0x000000FF) ++ ++#define RFE_CTL (0x0060) ++#define RFE_CTL_TCPUDP_CKM (0x00001000) ++#define RFE_CTL_IP_CKM (0x00000800) ++#define RFE_CTL_AB (0x00000400) ++#define RFE_CTL_AM (0x00000200) ++#define RFE_CTL_AU (0x00000100) ++#define RFE_CTL_VS (0x00000080) ++#define RFE_CTL_UF (0x00000040) ++#define RFE_CTL_VF (0x00000020) ++#define RFE_CTL_SPF (0x00000010) ++#define RFE_CTL_MHF (0x00000008) ++#define RFE_CTL_DHF (0x00000004) ++#define RFE_CTL_DPF (0x00000002) ++#define RFE_CTL_RST_RF (0x00000001) ++ ++#define VLAN_TYPE (0x0064) ++#define VLAN_TYPE_MASK (0x0000FFFF) ++ ++#define FCT_RX_CTL (0x0090) ++#define FCT_RX_CTL_EN (0x80000000) ++#define FCT_RX_CTL_RST (0x40000000) ++#define FCT_RX_CTL_SBF (0x02000000) ++#define FCT_RX_CTL_OVERFLOW (0x01000000) ++#define FCT_RX_CTL_FRM_DROP (0x00800000) ++#define FCT_RX_CTL_RX_NOT_EMPTY (0x00400000) ++#define FCT_RX_CTL_RX_EMPTY (0x00200000) ++#define FCT_RX_CTL_RX_DISABLED (0x00100000) ++#define FCT_RX_CTL_RXUSED (0x0000FFFF) ++ ++#define FCT_TX_CTL (0x0094) ++#define FCT_TX_CTL_EN (0x80000000) ++#define FCT_TX_CTL_RST (0x40000000) ++#define FCT_TX_CTL_TX_NOT_EMPTY (0x00400000) ++#define FCT_TX_CTL_TX_EMPTY (0x00200000) ++#define FCT_TX_CTL_TX_DISABLED (0x00100000) ++#define FCT_TX_CTL_TXUSED (0x0000FFFF) ++ ++#define FCT_RX_FIFO_END (0x0098) ++#define FCT_RX_FIFO_END_MASK (0x0000007F) ++ ++#define FCT_TX_FIFO_END (0x009C) ++#define FCT_TX_FIFO_END_MASK (0x0000003F) ++ ++#define FCT_FLOW (0x00A0) ++#define FCT_FLOW_THRESHOLD_OFF (0x00007F00) ++#define FCT_FLOW_THRESHOLD_OFF_SHIFT (8) ++#define FCT_FLOW_THRESHOLD_ON (0x0000007F) ++ ++/* MAC CSRs */ ++#define MAC_CR (0x100) ++#define MAC_CR_ADP (0x00002000) ++#define MAC_CR_ADD (0x00001000) ++#define MAC_CR_ASD (0x00000800) ++#define MAC_CR_INT_LOOP (0x00000400) ++#define MAC_CR_BOLMT (0x000000C0) ++#define MAC_CR_FDPX (0x00000008) ++#define MAC_CR_CFG (0x00000006) ++#define MAC_CR_CFG_10 (0x00000000) ++#define MAC_CR_CFG_100 (0x00000002) ++#define MAC_CR_CFG_1000 (0x00000004) ++#define MAC_CR_RST (0x00000001) ++ ++#define MAC_RX (0x104) ++#define MAC_RX_MAX_SIZE (0x3FFF0000) ++#define MAC_RX_MAX_SIZE_SHIFT (16) ++#define MAC_RX_FCS_STRIP (0x00000010) ++#define MAC_RX_FSE (0x00000004) ++#define MAC_RX_RXD (0x00000002) ++#define MAC_RX_RXEN (0x00000001) ++ ++#define MAC_TX (0x108) ++#define MAC_TX_BFCS (0x00000004) ++#define MAC_TX_TXD (0x00000002) ++#define MAC_TX_TXEN (0x00000001) ++ ++#define FLOW (0x10C) ++#define FLOW_FORCE_FC (0x80000000) ++#define FLOW_TX_FCEN (0x40000000) ++#define FLOW_RX_FCEN (0x20000000) ++#define FLOW_FPF (0x10000000) ++#define FLOW_PAUSE_TIME (0x0000FFFF) ++ ++#define RAND_SEED (0x110) ++#define RAND_SEED_MASK (0x0000FFFF) ++ ++#define ERR_STS (0x114) ++#define ERR_STS_FCS_ERR (0x00000100) ++#define ERR_STS_LFRM_ERR (0x00000080) ++#define ERR_STS_RUNT_ERR (0x00000040) ++#define ERR_STS_COLLISION_ERR (0x00000010) ++#define ERR_STS_ALIGN_ERR (0x00000008) ++#define ERR_STS_URUN_ERR (0x00000004) ++ ++#define RX_ADDRH (0x118) ++#define RX_ADDRH_MASK (0x0000FFFF) ++ ++#define RX_ADDRL (0x11C) ++ ++#define MII_ACCESS (0x120) ++#define MII_ACCESS_PHY_ADDR (0x0000F800) ++#define MII_ACCESS_PHY_ADDR_SHIFT (11) ++#define MII_ACCESS_REG_ADDR (0x000007C0) ++#define MII_ACCESS_REG_ADDR_SHIFT (6) ++#define MII_ACCESS_READ (0x00000000) ++#define MII_ACCESS_WRITE (0x00000002) ++#define MII_ACCESS_BUSY (0x00000001) ++ ++#define MII_DATA (0x124) ++#define MII_DATA_MASK (0x0000FFFF) ++ ++#define WUCSR (0x140) ++#define WUCSR_PFDA_FR (0x00000080) ++#define WUCSR_WUFR (0x00000040) ++#define WUCSR_MPR (0x00000020) ++#define WUCSR_BCAST_FR (0x00000010) ++#define WUCSR_PFDA_EN (0x00000008) ++#define WUCSR_WUEN (0x00000004) ++#define WUCSR_MPEN (0x00000002) ++#define WUCSR_BCST_EN (0x00000001) ++ ++#define WUF_CFGX (0x144) ++#define WUF_CFGX_EN (0x80000000) ++#define WUF_CFGX_ATYPE (0x03000000) ++#define WUF_CFGX_ATYPE_UNICAST (0x00000000) ++#define WUF_CFGX_ATYPE_MULTICAST (0x02000000) ++#define WUF_CFGX_ATYPE_ALL (0x03000000) ++#define WUF_CFGX_PATTERN_OFFSET (0x007F0000) ++#define WUF_CFGX_PATTERN_OFFSET_SHIFT (16) ++#define WUF_CFGX_CRC16 (0x0000FFFF) ++#define WUF_NUM (8) ++ ++#define WUF_MASKX (0x170) ++#define WUF_MASKX_AVALID (0x80000000) ++#define WUF_MASKX_ATYPE (0x40000000) ++ ++#define ADDR_FILTX (0x300) ++#define ADDR_FILTX_FB_VALID (0x80000000) ++#define ADDR_FILTX_FB_TYPE (0x40000000) ++#define ADDR_FILTX_FB_ADDRHI (0x0000FFFF) ++#define ADDR_FILTX_SB_ADDRLO (0xFFFFFFFF) ++ ++#define WUCSR2 (0x500) ++#define WUCSR2_NS_RCD (0x00000040) ++#define WUCSR2_ARP_RCD (0x00000020) ++#define WUCSR2_TCPSYN_RCD (0x00000010) ++#define WUCSR2_NS_OFFLOAD (0x00000004) ++#define WUCSR2_ARP_OFFLOAD (0x00000002) ++#define WUCSR2_TCPSYN_OFFLOAD (0x00000001) ++ ++#define WOL_FIFO_STS (0x504) ++ ++#define IPV6_ADDRX (0x510) ++ ++#define IPV4_ADDRX (0x590) ++ ++ ++/* Vendor-specific PHY Definitions */ ++ ++/* Mode Control/Status Register */ ++#define PHY_MODE_CTRL_STS (17) ++#define MODE_CTRL_STS_EDPWRDOWN ((u16)0x2000) ++#define MODE_CTRL_STS_ENERGYON ((u16)0x0002) ++ ++#define PHY_INT_SRC (29) ++#define PHY_INT_SRC_ENERGY_ON ((u16)0x0080) ++#define PHY_INT_SRC_ANEG_COMP ((u16)0x0040) ++#define PHY_INT_SRC_REMOTE_FAULT ((u16)0x0020) ++#define PHY_INT_SRC_LINK_DOWN ((u16)0x0010) ++#define PHY_INT_SRC_CLEAR_ALL ((u16)0xffff) ++ ++#define PHY_INT_MASK (30) ++#define PHY_INT_MASK_ENERGY_ON ((u16)0x0080) ++#define PHY_INT_MASK_ANEG_COMP ((u16)0x0040) ++#define PHY_INT_MASK_REMOTE_FAULT ((u16)0x0020) ++#define PHY_INT_MASK_LINK_DOWN ((u16)0x0010) ++#define PHY_INT_MASK_DEFAULT (PHY_INT_MASK_ANEG_COMP | \ ++ PHY_INT_MASK_LINK_DOWN) ++ ++#define PHY_SPECIAL (31) ++#define PHY_SPECIAL_SPD ((u16)0x001C) ++#define PHY_SPECIAL_SPD_10HALF ((u16)0x0004) ++#define PHY_SPECIAL_SPD_10FULL ((u16)0x0014) ++#define PHY_SPECIAL_SPD_100HALF ((u16)0x0008) ++#define PHY_SPECIAL_SPD_100FULL ((u16)0x0018) ++ ++/* USB Vendor Requests */ ++#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 ++#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 ++#define USB_VENDOR_REQUEST_GET_STATS 0xA2 ++ ++/* Interrupt Endpoint status word bitfields */ ++#define INT_ENP_RDFO_INT ((u32)BIT(22)) ++#define INT_ENP_TXE_INT ((u32)BIT(21)) ++#define INT_ENP_TX_DIS_INT ((u32)BIT(19)) ++#define INT_ENP_RX_DIS_INT ((u32)BIT(18)) ++#define INT_ENP_PHY_INT ((u32)BIT(17)) ++#define INT_ENP_MAC_ERR_INT ((u32)BIT(15)) ++#define INT_ENP_RX_FIFO_DATA_INT ((u32)BIT(12)) ++ ++#endif /* _SMSC75XX_H */ +diff -Naur backports-3.18.1-1.org/drivers/net/usb/smsc95xx.c backports-3.18.1-1/drivers/net/usb/smsc95xx.c +--- backports-3.18.1-1.org/drivers/net/usb/smsc95xx.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/smsc95xx.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,2030 @@ ++ /*************************************************************************** ++ * ++ * Copyright (C) 2007-2008 SMSC ++ * ++ * 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, see . ++ * ++ *****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "smsc95xx.h" ++ ++#define SMSC_CHIPNAME "smsc95xx" ++#define SMSC_DRIVER_VERSION "1.0.4" ++#define HS_USB_PKT_SIZE (512) ++#define FS_USB_PKT_SIZE (64) ++#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) ++#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE) ++#define DEFAULT_BULK_IN_DELAY (0x00002000) ++#define MAX_SINGLE_PACKET_SIZE (2048) ++#define LAN95XX_EEPROM_MAGIC (0x9500) ++#define EEPROM_MAC_OFFSET (0x01) ++#define DEFAULT_TX_CSUM_ENABLE (true) ++#define DEFAULT_RX_CSUM_ENABLE (true) ++#define SMSC95XX_INTERNAL_PHY_ID (1) ++#define SMSC95XX_TX_OVERHEAD (8) ++#define SMSC95XX_TX_OVERHEAD_CSUM (12) ++#define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \ ++ WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) ++ ++#define FEATURE_8_WAKEUP_FILTERS (0x01) ++#define FEATURE_PHY_NLP_CROSSOVER (0x02) ++#define FEATURE_REMOTE_WAKEUP (0x04) ++ ++#define SUSPEND_SUSPEND0 (0x01) ++#define SUSPEND_SUSPEND1 (0x02) ++#define SUSPEND_SUSPEND2 (0x04) ++#define SUSPEND_SUSPEND3 (0x08) ++#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ ++ SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) ++ ++struct smsc95xx_priv { ++ u32 mac_cr; ++ u32 hash_hi; ++ u32 hash_lo; ++ u32 wolopts; ++ spinlock_t mac_cr_lock; ++ u8 features; ++ u8 suspend_flags; ++}; ++ ++static bool turbo_mode = true; ++module_param(turbo_mode, bool, 0644); ++MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); ++ ++static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index, ++ u32 *data, int in_pm) ++{ ++ u32 buf; ++ int ret; ++ int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16); ++ ++ BUG_ON(!dev); ++ ++ if (!in_pm) ++ fn = usbnet_read_cmd; ++ else ++ fn = usbnet_read_cmd_nopm; ++ ++ ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN ++ | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ 0, index, &buf, 4); ++ if (unlikely(ret < 0)) ++ netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n", ++ index, ret); ++ ++ le32_to_cpus(&buf); ++ *data = buf; ++ ++ return ret; ++} ++ ++static int __must_check __smsc95xx_write_reg(struct usbnet *dev, u32 index, ++ u32 data, int in_pm) ++{ ++ u32 buf; ++ int ret; ++ int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16); ++ ++ BUG_ON(!dev); ++ ++ if (!in_pm) ++ fn = usbnet_write_cmd; ++ else ++ fn = usbnet_write_cmd_nopm; ++ ++ buf = data; ++ cpu_to_le32s(&buf); ++ ++ ret = fn(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT ++ | USB_TYPE_VENDOR | USB_RECIP_DEVICE, ++ 0, index, &buf, 4); ++ if (unlikely(ret < 0)) ++ netdev_warn(dev->net, "Failed to write reg index 0x%08x: %d\n", ++ index, ret); ++ ++ return ret; ++} ++ ++static int __must_check smsc95xx_read_reg_nopm(struct usbnet *dev, u32 index, ++ u32 *data) ++{ ++ return __smsc95xx_read_reg(dev, index, data, 1); ++} ++ ++static int __must_check smsc95xx_write_reg_nopm(struct usbnet *dev, u32 index, ++ u32 data) ++{ ++ return __smsc95xx_write_reg(dev, index, data, 1); ++} ++ ++static int __must_check smsc95xx_read_reg(struct usbnet *dev, u32 index, ++ u32 *data) ++{ ++ return __smsc95xx_read_reg(dev, index, data, 0); ++} ++ ++static int __must_check smsc95xx_write_reg(struct usbnet *dev, u32 index, ++ u32 data) ++{ ++ return __smsc95xx_write_reg(dev, index, data, 0); ++} ++ ++/* Loop until the read is completed with timeout ++ * called with phy_mutex held */ ++static int __must_check __smsc95xx_phy_wait_not_busy(struct usbnet *dev, ++ int in_pm) ++{ ++ unsigned long start_time = jiffies; ++ u32 val; ++ int ret; ++ ++ do { ++ ret = __smsc95xx_read_reg(dev, MII_ADDR, &val, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading MII_ACCESS\n"); ++ return ret; ++ } ++ ++ if (!(val & MII_BUSY_)) ++ return 0; ++ } while (!time_after(jiffies, start_time + HZ)); ++ ++ return -EIO; ++} ++ ++static int __smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx, ++ int in_pm) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ u32 val, addr; ++ int ret; ++ ++ mutex_lock(&dev->phy_mutex); ++ ++ /* confirm MII not busy */ ++ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_read\n"); ++ goto done; ++ } ++ ++ /* set the address, index & direction (read from PHY) */ ++ phy_id &= dev->mii.phy_id_mask; ++ idx &= dev->mii.reg_num_mask; ++ addr = (phy_id << 11) | (idx << 6) | MII_READ_ | MII_BUSY_; ++ ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing MII_ADDR\n"); ++ goto done; ++ } ++ ++ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx); ++ goto done; ++ } ++ ++ ret = __smsc95xx_read_reg(dev, MII_DATA, &val, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading MII_DATA\n"); ++ goto done; ++ } ++ ++ ret = (u16)(val & 0xFFFF); ++ ++done: ++ mutex_unlock(&dev->phy_mutex); ++ return ret; ++} ++ ++static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id, ++ int idx, int regval, int in_pm) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ u32 val, addr; ++ int ret; ++ ++ mutex_lock(&dev->phy_mutex); ++ ++ /* confirm MII not busy */ ++ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_write\n"); ++ goto done; ++ } ++ ++ val = regval; ++ ret = __smsc95xx_write_reg(dev, MII_DATA, val, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing MII_DATA\n"); ++ goto done; ++ } ++ ++ /* set the address, index & direction (write to PHY) */ ++ phy_id &= dev->mii.phy_id_mask; ++ idx &= dev->mii.reg_num_mask; ++ addr = (phy_id << 11) | (idx << 6) | MII_WRITE_ | MII_BUSY_; ++ ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing MII_ADDR\n"); ++ goto done; ++ } ++ ++ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Timed out writing MII reg %02X\n", idx); ++ goto done; ++ } ++ ++done: ++ mutex_unlock(&dev->phy_mutex); ++} ++ ++static int smsc95xx_mdio_read_nopm(struct net_device *netdev, int phy_id, ++ int idx) ++{ ++ return __smsc95xx_mdio_read(netdev, phy_id, idx, 1); ++} ++ ++static void smsc95xx_mdio_write_nopm(struct net_device *netdev, int phy_id, ++ int idx, int regval) ++{ ++ __smsc95xx_mdio_write(netdev, phy_id, idx, regval, 1); ++} ++ ++static int smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx) ++{ ++ return __smsc95xx_mdio_read(netdev, phy_id, idx, 0); ++} ++ ++static void smsc95xx_mdio_write(struct net_device *netdev, int phy_id, int idx, ++ int regval) ++{ ++ __smsc95xx_mdio_write(netdev, phy_id, idx, regval, 0); ++} ++ ++static int __must_check smsc95xx_wait_eeprom(struct usbnet *dev) ++{ ++ unsigned long start_time = jiffies; ++ u32 val; ++ int ret; ++ ++ do { ++ ret = smsc95xx_read_reg(dev, E2P_CMD, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading E2P_CMD\n"); ++ return ret; ++ } ++ ++ if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_)) ++ break; ++ udelay(40); ++ } while (!time_after(jiffies, start_time + HZ)); ++ ++ if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) { ++ netdev_warn(dev->net, "EEPROM read operation timeout\n"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int __must_check smsc95xx_eeprom_confirm_not_busy(struct usbnet *dev) ++{ ++ unsigned long start_time = jiffies; ++ u32 val; ++ int ret; ++ ++ do { ++ ret = smsc95xx_read_reg(dev, E2P_CMD, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading E2P_CMD\n"); ++ return ret; ++ } ++ ++ if (!(val & E2P_CMD_BUSY_)) ++ return 0; ++ ++ udelay(40); ++ } while (!time_after(jiffies, start_time + HZ)); ++ ++ netdev_warn(dev->net, "EEPROM is busy\n"); ++ return -EIO; ++} ++ ++static int smsc95xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length, ++ u8 *data) ++{ ++ u32 val; ++ int i, ret; ++ ++ BUG_ON(!dev); ++ BUG_ON(!data); ++ ++ ret = smsc95xx_eeprom_confirm_not_busy(dev); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < length; i++) { ++ val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_); ++ ret = smsc95xx_write_reg(dev, E2P_CMD, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing E2P_CMD\n"); ++ return ret; ++ } ++ ++ ret = smsc95xx_wait_eeprom(dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_read_reg(dev, E2P_DATA, &val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error reading E2P_DATA\n"); ++ return ret; ++ } ++ ++ data[i] = val & 0xFF; ++ offset++; ++ } ++ ++ return 0; ++} ++ ++static int smsc95xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length, ++ u8 *data) ++{ ++ u32 val; ++ int i, ret; ++ ++ BUG_ON(!dev); ++ BUG_ON(!data); ++ ++ ret = smsc95xx_eeprom_confirm_not_busy(dev); ++ if (ret) ++ return ret; ++ ++ /* Issue write/erase enable command */ ++ val = E2P_CMD_BUSY_ | E2P_CMD_EWEN_; ++ ret = smsc95xx_write_reg(dev, E2P_CMD, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing E2P_DATA\n"); ++ return ret; ++ } ++ ++ ret = smsc95xx_wait_eeprom(dev); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < length; i++) { ++ ++ /* Fill data register */ ++ val = data[i]; ++ ret = smsc95xx_write_reg(dev, E2P_DATA, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing E2P_DATA\n"); ++ return ret; ++ } ++ ++ /* Send "write" command */ ++ val = E2P_CMD_BUSY_ | E2P_CMD_WRITE_ | (offset & E2P_CMD_ADDR_); ++ ret = smsc95xx_write_reg(dev, E2P_CMD, val); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Error writing E2P_CMD\n"); ++ return ret; ++ } ++ ++ ret = smsc95xx_wait_eeprom(dev); ++ if (ret < 0) ++ return ret; ++ ++ offset++; ++ } ++ ++ return 0; ++} ++ ++static int __must_check smsc95xx_write_reg_async(struct usbnet *dev, u16 index, ++ u32 data) ++{ ++ const u16 size = 4; ++ u32 buf; ++ int ret; ++ ++ buf = data; ++ cpu_to_le32s(&buf); ++ ++ ret = usbnet_write_cmd_async(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_DEVICE, ++ 0, index, &buf, size); ++ if (ret < 0) ++ netdev_warn(dev->net, "Error write async cmd, sts=%d\n", ++ ret); ++ return ret; ++} ++ ++/* returns hash bit number for given MAC address ++ * example: ++ * 01 00 5E 00 00 01 -> returns bit number 31 */ ++static unsigned int smsc95xx_hash(char addr[ETH_ALEN]) ++{ ++ return (ether_crc(ETH_ALEN, addr) >> 26) & 0x3f; ++} ++ ++static void smsc95xx_set_multicast(struct net_device *netdev) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ unsigned long flags; ++ int ret; ++ ++ pdata->hash_hi = 0; ++ pdata->hash_lo = 0; ++ ++ spin_lock_irqsave(&pdata->mac_cr_lock, flags); ++ ++ if (dev->net->flags & IFF_PROMISC) { ++ netif_dbg(dev, drv, dev->net, "promiscuous mode enabled\n"); ++ pdata->mac_cr |= MAC_CR_PRMS_; ++ pdata->mac_cr &= ~(MAC_CR_MCPAS_ | MAC_CR_HPFILT_); ++ } else if (dev->net->flags & IFF_ALLMULTI) { ++ netif_dbg(dev, drv, dev->net, "receive all multicast enabled\n"); ++ pdata->mac_cr |= MAC_CR_MCPAS_; ++ pdata->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_HPFILT_); ++ } else if (!netdev_mc_empty(dev->net)) { ++ struct netdev_hw_addr *ha; ++ ++ pdata->mac_cr |= MAC_CR_HPFILT_; ++ pdata->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_); ++ ++ netdev_for_each_mc_addr(ha, netdev) { ++ u32 bitnum = smsc95xx_hash(ha->addr); ++ u32 mask = 0x01 << (bitnum & 0x1F); ++ if (bitnum & 0x20) ++ pdata->hash_hi |= mask; ++ else ++ pdata->hash_lo |= mask; ++ } ++ ++ netif_dbg(dev, drv, dev->net, "HASHH=0x%08X, HASHL=0x%08X\n", ++ pdata->hash_hi, pdata->hash_lo); ++ } else { ++ netif_dbg(dev, drv, dev->net, "receive own packets only\n"); ++ pdata->mac_cr &= ++ ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_); ++ } ++ ++ spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); ++ ++ /* Initiate async writes, as we can't wait for completion here */ ++ ret = smsc95xx_write_reg_async(dev, HASHH, pdata->hash_hi); ++ if (ret < 0) ++ netdev_warn(dev->net, "failed to initiate async write to HASHH\n"); ++ ++ ret = smsc95xx_write_reg_async(dev, HASHL, pdata->hash_lo); ++ if (ret < 0) ++ netdev_warn(dev->net, "failed to initiate async write to HASHL\n"); ++ ++ ret = smsc95xx_write_reg_async(dev, MAC_CR, pdata->mac_cr); ++ if (ret < 0) ++ netdev_warn(dev->net, "failed to initiate async write to MAC_CR\n"); ++} ++ ++static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, ++ u16 lcladv, u16 rmtadv) ++{ ++ u32 flow, afc_cfg = 0; ++ ++ int ret = smsc95xx_read_reg(dev, AFC_CFG, &afc_cfg); ++ if (ret < 0) ++ return ret; ++ ++ if (duplex == DUPLEX_FULL) { ++ u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); ++ ++ if (cap & FLOW_CTRL_RX) ++ flow = 0xFFFF0002; ++ else ++ flow = 0; ++ ++ if (cap & FLOW_CTRL_TX) ++ afc_cfg |= 0xF; ++ else ++ afc_cfg &= ~0xF; ++ ++ netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s\n", ++ cap & FLOW_CTRL_RX ? "enabled" : "disabled", ++ cap & FLOW_CTRL_TX ? "enabled" : "disabled"); ++ } else { ++ netif_dbg(dev, link, dev->net, "half duplex\n"); ++ flow = 0; ++ afc_cfg |= 0xF; ++ } ++ ++ ret = smsc95xx_write_reg(dev, FLOW, flow); ++ if (ret < 0) ++ return ret; ++ ++ return smsc95xx_write_reg(dev, AFC_CFG, afc_cfg); ++} ++ ++static int smsc95xx_link_reset(struct usbnet *dev) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ struct mii_if_info *mii = &dev->mii; ++ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; ++ unsigned long flags; ++ u16 lcladv, rmtadv; ++ int ret; ++ ++ /* clear interrupt status */ ++ ret = smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); ++ if (ret < 0) ++ return ret; ++ ++ mii_check_media(mii, 1, 1); ++ mii_ethtool_gset(&dev->mii, &ecmd); ++ lcladv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE); ++ rmtadv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_LPA); ++ ++ netif_dbg(dev, link, dev->net, ++ "speed: %u duplex: %d lcladv: %04x rmtadv: %04x\n", ++ ethtool_cmd_speed(&ecmd), ecmd.duplex, lcladv, rmtadv); ++ ++ spin_lock_irqsave(&pdata->mac_cr_lock, flags); ++ if (ecmd.duplex != DUPLEX_FULL) { ++ pdata->mac_cr &= ~MAC_CR_FDPX_; ++ pdata->mac_cr |= MAC_CR_RCVOWN_; ++ } else { ++ pdata->mac_cr &= ~MAC_CR_RCVOWN_; ++ pdata->mac_cr |= MAC_CR_FDPX_; ++ } ++ spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); ++ ++ ret = smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_phy_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv); ++ if (ret < 0) ++ netdev_warn(dev->net, "Error updating PHY flow control\n"); ++ ++ return ret; ++} ++ ++static void smsc95xx_status(struct usbnet *dev, struct urb *urb) ++{ ++ u32 intdata; ++ ++ if (urb->actual_length != 4) { ++ netdev_warn(dev->net, "unexpected urb length %d\n", ++ urb->actual_length); ++ return; ++ } ++ ++ memcpy(&intdata, urb->transfer_buffer, 4); ++ le32_to_cpus(&intdata); ++ ++ netif_dbg(dev, link, dev->net, "intdata: 0x%08X\n", intdata); ++ ++ if (intdata & INT_ENP_PHY_INT_) ++ usbnet_defer_kevent(dev, EVENT_LINK_RESET); ++ else ++ netdev_warn(dev->net, "unexpected interrupt, intdata=0x%08X\n", ++ intdata); ++} ++ ++/* Enable or disable Tx & Rx checksum offload engines */ ++static int smsc95xx_set_features(struct net_device *netdev, ++ netdev_features_t features) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ u32 read_buf; ++ int ret; ++ ++ ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); ++ if (ret < 0) ++ return ret; ++ ++ if (features & NETIF_F_HW_CSUM) ++ read_buf |= Tx_COE_EN_; ++ else ++ read_buf &= ~Tx_COE_EN_; ++ ++ if (features & NETIF_F_RXCSUM) ++ read_buf |= Rx_COE_EN_; ++ else ++ read_buf &= ~Rx_COE_EN_; ++ ++ ret = smsc95xx_write_reg(dev, COE_CR, read_buf); ++ if (ret < 0) ++ return ret; ++ ++ netif_dbg(dev, hw, dev->net, "COE_CR = 0x%08x\n", read_buf); ++ return 0; ++} ++ ++static int smsc95xx_ethtool_get_eeprom_len(struct net_device *net) ++{ ++ return MAX_EEPROM_SIZE; ++} ++ ++static int smsc95xx_ethtool_get_eeprom(struct net_device *netdev, ++ struct ethtool_eeprom *ee, u8 *data) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ ++ ee->magic = LAN95XX_EEPROM_MAGIC; ++ ++ return smsc95xx_read_eeprom(dev, ee->offset, ee->len, data); ++} ++ ++static int smsc95xx_ethtool_set_eeprom(struct net_device *netdev, ++ struct ethtool_eeprom *ee, u8 *data) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ ++ if (ee->magic != LAN95XX_EEPROM_MAGIC) { ++ netdev_warn(dev->net, "EEPROM: magic value mismatch, magic = 0x%x\n", ++ ee->magic); ++ return -EINVAL; ++ } ++ ++ return smsc95xx_write_eeprom(dev, ee->offset, ee->len, data); ++} ++ ++static int smsc95xx_ethtool_getregslen(struct net_device *netdev) ++{ ++ /* all smsc95xx registers */ ++ return COE_CR - ID_REV + sizeof(u32); ++} ++ ++static void ++smsc95xx_ethtool_getregs(struct net_device *netdev, struct ethtool_regs *regs, ++ void *buf) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ unsigned int i, j; ++ int retval; ++ u32 *data = buf; ++ ++ retval = smsc95xx_read_reg(dev, ID_REV, ®s->version); ++ if (retval < 0) { ++ netdev_warn(netdev, "REGS: cannot read ID_REV\n"); ++ return; ++ } ++ ++ for (i = ID_REV, j = 0; i <= COE_CR; i += (sizeof(u32)), j++) { ++ retval = smsc95xx_read_reg(dev, i, &data[j]); ++ if (retval < 0) { ++ netdev_warn(netdev, "REGS: cannot read reg[%x]\n", i); ++ return; ++ } ++ } ++} ++ ++static void smsc95xx_ethtool_get_wol(struct net_device *net, ++ struct ethtool_wolinfo *wolinfo) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ ++ wolinfo->supported = SUPPORTED_WAKE; ++ wolinfo->wolopts = pdata->wolopts; ++} ++ ++static int smsc95xx_ethtool_set_wol(struct net_device *net, ++ struct ethtool_wolinfo *wolinfo) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ int ret; ++ ++ pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; ++ ++ ret = device_set_wakeup_enable(&dev->udev->dev, pdata->wolopts); ++ if (ret < 0) ++ netdev_warn(dev->net, "device_set_wakeup_enable error %d\n", ret); ++ ++ return ret; ++} ++ ++static const struct ethtool_ops smsc95xx_ethtool_ops = { ++ .get_link = usbnet_get_link, ++ .nway_reset = usbnet_nway_reset, ++ .get_drvinfo = usbnet_get_drvinfo, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_settings = usbnet_get_settings, ++ .set_settings = usbnet_set_settings, ++ .get_eeprom_len = smsc95xx_ethtool_get_eeprom_len, ++ .get_eeprom = smsc95xx_ethtool_get_eeprom, ++ .set_eeprom = smsc95xx_ethtool_set_eeprom, ++ .get_regs_len = smsc95xx_ethtool_getregslen, ++ .get_regs = smsc95xx_ethtool_getregs, ++ .get_wol = smsc95xx_ethtool_get_wol, ++ .set_wol = smsc95xx_ethtool_set_wol, ++}; ++ ++static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ ++ if (!netif_running(netdev)) ++ return -EINVAL; ++ ++ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); ++} ++ ++static void smsc95xx_init_mac_address(struct usbnet *dev) ++{ ++ /* try reading mac address from EEPROM */ ++ if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, ++ dev->net->dev_addr) == 0) { ++ if (is_valid_ether_addr(dev->net->dev_addr)) { ++ /* eeprom values are valid so use them */ ++ netif_dbg(dev, ifup, dev->net, "MAC address read from EEPROM\n"); ++ return; ++ } ++ } ++ ++ /* no eeprom, or eeprom values are invalid. generate random MAC */ ++ eth_hw_addr_random(dev->net); ++ netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); ++} ++ ++static int smsc95xx_set_mac_address(struct usbnet *dev) ++{ ++ u32 addr_lo = dev->net->dev_addr[0] | dev->net->dev_addr[1] << 8 | ++ dev->net->dev_addr[2] << 16 | dev->net->dev_addr[3] << 24; ++ u32 addr_hi = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8; ++ int ret; ++ ++ ret = smsc95xx_write_reg(dev, ADDRL, addr_lo); ++ if (ret < 0) ++ return ret; ++ ++ return smsc95xx_write_reg(dev, ADDRH, addr_hi); ++} ++ ++/* starts the TX path */ ++static int smsc95xx_start_tx_path(struct usbnet *dev) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ unsigned long flags; ++ int ret; ++ ++ /* Enable Tx at MAC */ ++ spin_lock_irqsave(&pdata->mac_cr_lock, flags); ++ pdata->mac_cr |= MAC_CR_TXEN_; ++ spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); ++ ++ ret = smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); ++ if (ret < 0) ++ return ret; ++ ++ /* Enable Tx at SCSRs */ ++ return smsc95xx_write_reg(dev, TX_CFG, TX_CFG_ON_); ++} ++ ++/* Starts the Receive path */ ++static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pdata->mac_cr_lock, flags); ++ pdata->mac_cr |= MAC_CR_RXEN_; ++ spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); ++ ++ return __smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr, in_pm); ++} ++ ++static int smsc95xx_phy_initialize(struct usbnet *dev) ++{ ++ int bmcr, ret, timeout = 0; ++ ++ /* Initialize MII structure */ ++ dev->mii.dev = dev->net; ++ dev->mii.mdio_read = smsc95xx_mdio_read; ++ dev->mii.mdio_write = smsc95xx_mdio_write; ++ dev->mii.phy_id_mask = 0x1f; ++ dev->mii.reg_num_mask = 0x1f; ++ dev->mii.phy_id = SMSC95XX_INTERNAL_PHY_ID; ++ ++ /* reset phy and wait for reset to complete */ ++ smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); ++ ++ do { ++ msleep(10); ++ bmcr = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); ++ timeout++; ++ } while ((bmcr & BMCR_RESET) && (timeout < 100)); ++ ++ if (timeout >= 100) { ++ netdev_warn(dev->net, "timeout on PHY Reset"); ++ return -EIO; ++ } ++ ++ smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ++ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ++ ADVERTISE_PAUSE_ASYM); ++ ++ /* read to clear */ ++ ret = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to read PHY_INT_SRC during init\n"); ++ return ret; ++ } ++ ++ smsc95xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, ++ PHY_INT_MASK_DEFAULT_); ++ mii_nway_restart(&dev->mii); ++ ++ netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n"); ++ return 0; ++} ++ ++static int smsc95xx_reset(struct usbnet *dev) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ u32 read_buf, write_buf, burst_cap; ++ int ret = 0, timeout; ++ ++ netif_dbg(dev, ifup, dev->net, "entering smsc95xx_reset\n"); ++ ++ ret = smsc95xx_write_reg(dev, HW_CFG, HW_CFG_LRST_); ++ if (ret < 0) ++ return ret; ++ ++ timeout = 0; ++ do { ++ msleep(10); ++ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); ++ if (ret < 0) ++ return ret; ++ timeout++; ++ } while ((read_buf & HW_CFG_LRST_) && (timeout < 100)); ++ ++ if (timeout >= 100) { ++ netdev_warn(dev->net, "timeout waiting for completion of Lite Reset\n"); ++ return ret; ++ } ++ ++ ret = smsc95xx_write_reg(dev, PM_CTRL, PM_CTL_PHY_RST_); ++ if (ret < 0) ++ return ret; ++ ++ timeout = 0; ++ do { ++ msleep(10); ++ ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf); ++ if (ret < 0) ++ return ret; ++ timeout++; ++ } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100)); ++ ++ if (timeout >= 100) { ++ netdev_warn(dev->net, "timeout waiting for PHY Reset\n"); ++ return ret; ++ } ++ ++ ret = smsc95xx_set_mac_address(dev); ++ if (ret < 0) ++ return ret; ++ ++ netif_dbg(dev, ifup, dev->net, "MAC Address: %pM\n", ++ dev->net->dev_addr); ++ ++ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); ++ if (ret < 0) ++ return ret; ++ ++ netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG : 0x%08x\n", ++ read_buf); ++ ++ read_buf |= HW_CFG_BIR_; ++ ++ ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); ++ if (ret < 0) ++ return ret; ++ ++ netif_dbg(dev, ifup, dev->net, ++ "Read Value from HW_CFG after writing HW_CFG_BIR_: 0x%08x\n", ++ read_buf); ++ ++ if (!turbo_mode) { ++ burst_cap = 0; ++ dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE; ++ } else if (dev->udev->speed == USB_SPEED_HIGH) { ++ burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE; ++ dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE; ++ } else { ++ burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE; ++ dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n", ++ (ulong)dev->rx_urb_size); ++ ++ ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf); ++ if (ret < 0) ++ return ret; ++ ++ netif_dbg(dev, ifup, dev->net, ++ "Read Value from BURST_CAP after writing: 0x%08x\n", ++ read_buf); ++ ++ ret = smsc95xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf); ++ if (ret < 0) ++ return ret; ++ ++ netif_dbg(dev, ifup, dev->net, ++ "Read Value from BULK_IN_DLY after writing: 0x%08x\n", ++ read_buf); ++ ++ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); ++ if (ret < 0) ++ return ret; ++ ++ netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG: 0x%08x\n", ++ read_buf); ++ ++ if (turbo_mode) ++ read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_); ++ ++ read_buf &= ~HW_CFG_RXDOFF_; ++ ++ /* set Rx data offset=2, Make IP header aligns on word boundary. */ ++ read_buf |= NET_IP_ALIGN << 9; ++ ++ ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); ++ if (ret < 0) ++ return ret; ++ ++ netif_dbg(dev, ifup, dev->net, ++ "Read Value from HW_CFG after writing: 0x%08x\n", read_buf); ++ ++ ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_read_reg(dev, ID_REV, &read_buf); ++ if (ret < 0) ++ return ret; ++ netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x\n", read_buf); ++ ++ /* Configure GPIO pins as LED outputs */ ++ write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED | ++ LED_GPIO_CFG_FDX_LED; ++ ret = smsc95xx_write_reg(dev, LED_GPIO_CFG, write_buf); ++ if (ret < 0) ++ return ret; ++ ++ /* Init Tx */ ++ ret = smsc95xx_write_reg(dev, FLOW, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_write_reg(dev, AFC_CFG, AFC_CFG_DEFAULT); ++ if (ret < 0) ++ return ret; ++ ++ /* Don't need mac_cr_lock during initialisation */ ++ ret = smsc95xx_read_reg(dev, MAC_CR, &pdata->mac_cr); ++ if (ret < 0) ++ return ret; ++ ++ /* Init Rx */ ++ /* Set Vlan */ ++ ret = smsc95xx_write_reg(dev, VLAN1, (u32)ETH_P_8021Q); ++ if (ret < 0) ++ return ret; ++ ++ /* Enable or disable checksum offload engines */ ++ ret = smsc95xx_set_features(dev->net, dev->net->features); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to set checksum offload features\n"); ++ return ret; ++ } ++ ++ smsc95xx_set_multicast(dev->net); ++ ++ ret = smsc95xx_phy_initialize(dev); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to init PHY\n"); ++ return ret; ++ } ++ ++ ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf); ++ if (ret < 0) ++ return ret; ++ ++ /* enable PHY interrupts */ ++ read_buf |= INT_EP_CTL_PHY_INT_; ++ ++ ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_start_tx_path(dev); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to start TX path\n"); ++ return ret; ++ } ++ ++ ret = smsc95xx_start_rx_path(dev, 0); ++ if (ret < 0) { ++ netdev_warn(dev->net, "Failed to start RX path\n"); ++ return ret; ++ } ++ ++ netif_dbg(dev, ifup, dev->net, "smsc95xx_reset, return 0\n"); ++ return 0; ++} ++ ++static const struct net_device_ops smsc95xx_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = usbnet_change_mtu, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_do_ioctl = smsc95xx_ioctl, ++ .ndo_set_rx_mode = smsc95xx_set_multicast, ++ .ndo_set_features = smsc95xx_set_features, ++}; ++ ++static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ struct smsc95xx_priv *pdata = NULL; ++ u32 val; ++ int ret; ++ ++ printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); ++ ++ ret = usbnet_get_endpoints(dev, intf); ++ if (ret < 0) { ++ netdev_warn(dev->net, "usbnet_get_endpoints failed: %d\n", ret); ++ return ret; ++ } ++ ++ dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc95xx_priv), ++ GFP_KERNEL); ++ ++ pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ if (!pdata) ++ return -ENOMEM; ++ ++ spin_lock_init(&pdata->mac_cr_lock); ++ ++ if (DEFAULT_TX_CSUM_ENABLE) ++ dev->net->features |= NETIF_F_HW_CSUM; ++ if (DEFAULT_RX_CSUM_ENABLE) ++ dev->net->features |= NETIF_F_RXCSUM; ++ ++ dev->net->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM; ++ ++ smsc95xx_init_mac_address(dev); ++ ++ /* Init all registers */ ++ ret = smsc95xx_reset(dev); ++ ++ /* detect device revision as different features may be available */ ++ ret = smsc95xx_read_reg(dev, ID_REV, &val); ++ if (ret < 0) ++ return ret; ++ val >>= 16; ++ ++ if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9530_) || ++ (val == ID_REV_CHIP_ID_89530_) || (val == ID_REV_CHIP_ID_9730_)) ++ pdata->features = (FEATURE_8_WAKEUP_FILTERS | ++ FEATURE_PHY_NLP_CROSSOVER | ++ FEATURE_REMOTE_WAKEUP); ++ else if (val == ID_REV_CHIP_ID_9512_) ++ pdata->features = FEATURE_8_WAKEUP_FILTERS; ++ ++ dev->net->netdev_ops = &smsc95xx_netdev_ops; ++ dev->net->ethtool_ops = &smsc95xx_ethtool_ops; ++ dev->net->flags |= IFF_MULTICAST; ++ dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM; ++ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; ++ return 0; ++} ++ ++static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ if (pdata) { ++ netif_dbg(dev, ifdown, dev->net, "free pdata\n"); ++ kfree(pdata); ++ pdata = NULL; ++ dev->data[0] = 0; ++ } ++} ++ ++static u32 smsc_crc(const u8 *buffer, size_t len, int filter) ++{ ++ u32 crc = bitrev16(crc16(0xFFFF, buffer, len)); ++ return crc << ((filter % 2) * 16); ++} ++ ++static int smsc95xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) ++{ ++ struct mii_if_info *mii = &dev->mii; ++ int ret; ++ ++ netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n"); ++ ++ /* read to clear */ ++ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC); ++ if (ret < 0) ++ return ret; ++ ++ /* enable interrupt source */ ++ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK); ++ if (ret < 0) ++ return ret; ++ ++ ret |= mask; ++ ++ smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret); ++ ++ return 0; ++} ++ ++static int smsc95xx_link_ok_nopm(struct usbnet *dev) ++{ ++ struct mii_if_info *mii = &dev->mii; ++ int ret; ++ ++ /* first, a dummy read, needed to latch some MII phys */ ++ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); ++ if (ret < 0) ++ return ret; ++ ++ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); ++ if (ret < 0) ++ return ret; ++ ++ return !!(ret & BMSR_LSTATUS); ++} ++ ++static int smsc95xx_enter_suspend0(struct usbnet *dev) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ u32 val; ++ int ret; ++ ++ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); ++ if (ret < 0) ++ return ret; ++ ++ val &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_)); ++ val |= PM_CTL_SUS_MODE_0; ++ ++ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); ++ if (ret < 0) ++ return ret; ++ ++ /* clear wol status */ ++ val &= ~PM_CTL_WUPS_; ++ val |= PM_CTL_WUPS_WOL_; ++ ++ /* enable energy detection */ ++ if (pdata->wolopts & WAKE_PHY) ++ val |= PM_CTL_WUPS_ED_; ++ ++ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); ++ if (ret < 0) ++ return ret; ++ ++ /* read back PM_CTRL */ ++ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); ++ if (ret < 0) ++ return ret; ++ ++ pdata->suspend_flags |= SUSPEND_SUSPEND0; ++ ++ return 0; ++} ++ ++static int smsc95xx_enter_suspend1(struct usbnet *dev) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ struct mii_if_info *mii = &dev->mii; ++ u32 val; ++ int ret; ++ ++ /* reconfigure link pulse detection timing for ++ * compatibility with non-standard link partners ++ */ ++ if (pdata->features & FEATURE_PHY_NLP_CROSSOVER) ++ smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_EDPD_CONFIG, ++ PHY_EDPD_CONFIG_DEFAULT); ++ ++ /* enable energy detect power-down mode */ ++ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_MODE_CTRL_STS); ++ if (ret < 0) ++ return ret; ++ ++ ret |= MODE_CTRL_STS_EDPWRDOWN_; ++ ++ smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_MODE_CTRL_STS, ret); ++ ++ /* enter SUSPEND1 mode */ ++ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); ++ if (ret < 0) ++ return ret; ++ ++ val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); ++ val |= PM_CTL_SUS_MODE_1; ++ ++ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); ++ if (ret < 0) ++ return ret; ++ ++ /* clear wol status, enable energy detection */ ++ val &= ~PM_CTL_WUPS_; ++ val |= (PM_CTL_WUPS_ED_ | PM_CTL_ED_EN_); ++ ++ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); ++ if (ret < 0) ++ return ret; ++ ++ pdata->suspend_flags |= SUSPEND_SUSPEND1; ++ ++ return 0; ++} ++ ++static int smsc95xx_enter_suspend2(struct usbnet *dev) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ u32 val; ++ int ret; ++ ++ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); ++ if (ret < 0) ++ return ret; ++ ++ val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); ++ val |= PM_CTL_SUS_MODE_2; ++ ++ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); ++ if (ret < 0) ++ return ret; ++ ++ pdata->suspend_flags |= SUSPEND_SUSPEND2; ++ ++ return 0; ++} ++ ++static int smsc95xx_enter_suspend3(struct usbnet *dev) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ u32 val; ++ int ret; ++ ++ ret = smsc95xx_read_reg_nopm(dev, RX_FIFO_INF, &val); ++ if (ret < 0) ++ return ret; ++ ++ if (val & 0xFFFF) { ++ netdev_info(dev->net, "rx fifo not empty in autosuspend\n"); ++ return -EBUSY; ++ } ++ ++ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); ++ if (ret < 0) ++ return ret; ++ ++ val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); ++ val |= PM_CTL_SUS_MODE_3 | PM_CTL_RES_CLR_WKP_STS; ++ ++ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); ++ if (ret < 0) ++ return ret; ++ ++ /* clear wol status */ ++ val &= ~PM_CTL_WUPS_; ++ val |= PM_CTL_WUPS_WOL_; ++ ++ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); ++ if (ret < 0) ++ return ret; ++ ++ pdata->suspend_flags |= SUSPEND_SUSPEND3; ++ ++ return 0; ++} ++ ++static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ int ret; ++ ++ if (!netif_running(dev->net)) { ++ /* interface is ifconfig down so fully power down hw */ ++ netdev_dbg(dev->net, "autosuspend entering SUSPEND2\n"); ++ return smsc95xx_enter_suspend2(dev); ++ } ++ ++ if (!link_up) { ++ /* link is down so enter EDPD mode, but only if device can ++ * reliably resume from it. This check should be redundant ++ * as current FEATURE_REMOTE_WAKEUP parts also support ++ * FEATURE_PHY_NLP_CROSSOVER but it's included for clarity */ ++ if (!(pdata->features & FEATURE_PHY_NLP_CROSSOVER)) { ++ netdev_warn(dev->net, "EDPD not supported\n"); ++ return -EBUSY; ++ } ++ ++ netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n"); ++ ++ /* enable PHY wakeup events for if cable is attached */ ++ ret = smsc95xx_enable_phy_wakeup_interrupts(dev, ++ PHY_INT_MASK_ANEG_COMP_); ++ if (ret < 0) { ++ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); ++ return ret; ++ } ++ ++ netdev_info(dev->net, "entering SUSPEND1 mode\n"); ++ return smsc95xx_enter_suspend1(dev); ++ } ++ ++ /* enable PHY wakeup events so we remote wakeup if cable is pulled */ ++ ret = smsc95xx_enable_phy_wakeup_interrupts(dev, ++ PHY_INT_MASK_LINK_DOWN_); ++ if (ret < 0) { ++ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); ++ return ret; ++ } ++ ++ netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n"); ++ return smsc95xx_enter_suspend3(dev); ++} ++ ++static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) ++{ ++ struct usbnet *dev = usb_get_intfdata(intf); ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ u32 val, link_up; ++ int ret; ++ ++ ret = usbnet_suspend(intf, message); ++ if (ret < 0) { ++ netdev_warn(dev->net, "usbnet_suspend error\n"); ++ return ret; ++ } ++ ++ if (pdata->suspend_flags) { ++ netdev_warn(dev->net, "error during last resume\n"); ++ pdata->suspend_flags = 0; ++ } ++ ++ /* determine if link is up using only _nopm functions */ ++ link_up = smsc95xx_link_ok_nopm(dev); ++ ++ if (message.event == PM_EVENT_AUTO_SUSPEND && ++ (pdata->features & FEATURE_REMOTE_WAKEUP)) { ++ ret = smsc95xx_autosuspend(dev, link_up); ++ goto done; ++ } ++ ++ /* if we get this far we're not autosuspending */ ++ /* if no wol options set, or if link is down and we're not waking on ++ * PHY activity, enter lowest power SUSPEND2 mode ++ */ ++ if (!(pdata->wolopts & SUPPORTED_WAKE) || ++ !(link_up || (pdata->wolopts & WAKE_PHY))) { ++ netdev_info(dev->net, "entering SUSPEND2 mode\n"); ++ ++ /* disable energy detect (link up) & wake up events */ ++ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) ++ goto done; ++ ++ val &= ~(WUCSR_MPEN_ | WUCSR_WAKE_EN_); ++ ++ ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) ++ goto done; ++ ++ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); ++ if (ret < 0) ++ goto done; ++ ++ val &= ~(PM_CTL_ED_EN_ | PM_CTL_WOL_EN_); ++ ++ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); ++ if (ret < 0) ++ goto done; ++ ++ ret = smsc95xx_enter_suspend2(dev); ++ goto done; ++ } ++ ++ if (pdata->wolopts & WAKE_PHY) { ++ ret = smsc95xx_enable_phy_wakeup_interrupts(dev, ++ (PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_LINK_DOWN_)); ++ if (ret < 0) { ++ netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); ++ goto done; ++ } ++ ++ /* if link is down then configure EDPD and enter SUSPEND1, ++ * otherwise enter SUSPEND0 below ++ */ ++ if (!link_up) { ++ netdev_info(dev->net, "entering SUSPEND1 mode\n"); ++ ret = smsc95xx_enter_suspend1(dev); ++ goto done; ++ } ++ } ++ ++ if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) { ++ u32 *filter_mask = kzalloc(sizeof(u32) * 32, GFP_KERNEL); ++ u32 command[2]; ++ u32 offset[2]; ++ u32 crc[4]; ++ int wuff_filter_count = ++ (pdata->features & FEATURE_8_WAKEUP_FILTERS) ? ++ LAN9500A_WUFF_NUM : LAN9500_WUFF_NUM; ++ int i, filter = 0; ++ ++ if (!filter_mask) { ++ netdev_warn(dev->net, "Unable to allocate filter_mask\n"); ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ memset(command, 0, sizeof(command)); ++ memset(offset, 0, sizeof(offset)); ++ memset(crc, 0, sizeof(crc)); ++ ++ if (pdata->wolopts & WAKE_BCAST) { ++ const u8 bcast[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; ++ netdev_info(dev->net, "enabling broadcast detection\n"); ++ filter_mask[filter * 4] = 0x003F; ++ filter_mask[filter * 4 + 1] = 0x00; ++ filter_mask[filter * 4 + 2] = 0x00; ++ filter_mask[filter * 4 + 3] = 0x00; ++ command[filter/4] |= 0x05UL << ((filter % 4) * 8); ++ offset[filter/4] |= 0x00 << ((filter % 4) * 8); ++ crc[filter/2] |= smsc_crc(bcast, 6, filter); ++ filter++; ++ } ++ ++ if (pdata->wolopts & WAKE_MCAST) { ++ const u8 mcast[] = {0x01, 0x00, 0x5E}; ++ netdev_info(dev->net, "enabling multicast detection\n"); ++ filter_mask[filter * 4] = 0x0007; ++ filter_mask[filter * 4 + 1] = 0x00; ++ filter_mask[filter * 4 + 2] = 0x00; ++ filter_mask[filter * 4 + 3] = 0x00; ++ command[filter/4] |= 0x09UL << ((filter % 4) * 8); ++ offset[filter/4] |= 0x00 << ((filter % 4) * 8); ++ crc[filter/2] |= smsc_crc(mcast, 3, filter); ++ filter++; ++ } ++ ++ if (pdata->wolopts & WAKE_ARP) { ++ const u8 arp[] = {0x08, 0x06}; ++ netdev_info(dev->net, "enabling ARP detection\n"); ++ filter_mask[filter * 4] = 0x0003; ++ filter_mask[filter * 4 + 1] = 0x00; ++ filter_mask[filter * 4 + 2] = 0x00; ++ filter_mask[filter * 4 + 3] = 0x00; ++ command[filter/4] |= 0x05UL << ((filter % 4) * 8); ++ offset[filter/4] |= 0x0C << ((filter % 4) * 8); ++ crc[filter/2] |= smsc_crc(arp, 2, filter); ++ filter++; ++ } ++ ++ if (pdata->wolopts & WAKE_UCAST) { ++ netdev_info(dev->net, "enabling unicast detection\n"); ++ filter_mask[filter * 4] = 0x003F; ++ filter_mask[filter * 4 + 1] = 0x00; ++ filter_mask[filter * 4 + 2] = 0x00; ++ filter_mask[filter * 4 + 3] = 0x00; ++ command[filter/4] |= 0x01UL << ((filter % 4) * 8); ++ offset[filter/4] |= 0x00 << ((filter % 4) * 8); ++ crc[filter/2] |= smsc_crc(dev->net->dev_addr, ETH_ALEN, filter); ++ filter++; ++ } ++ ++ for (i = 0; i < (wuff_filter_count * 4); i++) { ++ ret = smsc95xx_write_reg_nopm(dev, WUFF, filter_mask[i]); ++ if (ret < 0) { ++ kfree(filter_mask); ++ goto done; ++ } ++ } ++ kfree(filter_mask); ++ ++ for (i = 0; i < (wuff_filter_count / 4); i++) { ++ ret = smsc95xx_write_reg_nopm(dev, WUFF, command[i]); ++ if (ret < 0) ++ goto done; ++ } ++ ++ for (i = 0; i < (wuff_filter_count / 4); i++) { ++ ret = smsc95xx_write_reg_nopm(dev, WUFF, offset[i]); ++ if (ret < 0) ++ goto done; ++ } ++ ++ for (i = 0; i < (wuff_filter_count / 2); i++) { ++ ret = smsc95xx_write_reg_nopm(dev, WUFF, crc[i]); ++ if (ret < 0) ++ goto done; ++ } ++ ++ /* clear any pending pattern match packet status */ ++ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) ++ goto done; ++ ++ val |= WUCSR_WUFR_; ++ ++ ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) ++ goto done; ++ } ++ ++ if (pdata->wolopts & WAKE_MAGIC) { ++ /* clear any pending magic packet status */ ++ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) ++ goto done; ++ ++ val |= WUCSR_MPR_; ++ ++ ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) ++ goto done; ++ } ++ ++ /* enable/disable wakeup sources */ ++ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) ++ goto done; ++ ++ if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) { ++ netdev_info(dev->net, "enabling pattern match wakeup\n"); ++ val |= WUCSR_WAKE_EN_; ++ } else { ++ netdev_info(dev->net, "disabling pattern match wakeup\n"); ++ val &= ~WUCSR_WAKE_EN_; ++ } ++ ++ if (pdata->wolopts & WAKE_MAGIC) { ++ netdev_info(dev->net, "enabling magic packet wakeup\n"); ++ val |= WUCSR_MPEN_; ++ } else { ++ netdev_info(dev->net, "disabling magic packet wakeup\n"); ++ val &= ~WUCSR_MPEN_; ++ } ++ ++ ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) ++ goto done; ++ ++ /* enable wol wakeup source */ ++ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); ++ if (ret < 0) ++ goto done; ++ ++ val |= PM_CTL_WOL_EN_; ++ ++ /* phy energy detect wakeup source */ ++ if (pdata->wolopts & WAKE_PHY) ++ val |= PM_CTL_ED_EN_; ++ ++ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); ++ if (ret < 0) ++ goto done; ++ ++ /* enable receiver to enable frame reception */ ++ smsc95xx_start_rx_path(dev, 1); ++ ++ /* some wol options are enabled, so enter SUSPEND0 */ ++ netdev_info(dev->net, "entering SUSPEND0 mode\n"); ++ ret = smsc95xx_enter_suspend0(dev); ++ ++done: ++ /* ++ * TODO: resume() might need to handle the suspend failure ++ * in system sleep ++ */ ++ if (ret && PMSG_IS_AUTO(message)) ++ usbnet_resume(intf); ++ return ret; ++} ++ ++static int smsc95xx_resume(struct usb_interface *intf) ++{ ++ struct usbnet *dev = usb_get_intfdata(intf); ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ u8 suspend_flags = pdata->suspend_flags; ++ int ret; ++ u32 val; ++ ++ BUG_ON(!dev); ++ ++ netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags); ++ ++ /* do this first to ensure it's cleared even in error case */ ++ pdata->suspend_flags = 0; ++ ++ if (suspend_flags & SUSPEND_ALLMODES) { ++ /* clear wake-up sources */ ++ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); ++ if (ret < 0) ++ return ret; ++ ++ val &= ~(WUCSR_WAKE_EN_ | WUCSR_MPEN_); ++ ++ ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); ++ if (ret < 0) ++ return ret; ++ ++ /* clear wake-up status */ ++ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); ++ if (ret < 0) ++ return ret; ++ ++ val &= ~PM_CTL_WOL_EN_; ++ val |= PM_CTL_WUPS_; ++ ++ ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); ++ if (ret < 0) ++ return ret; ++ } ++ ++ ret = usbnet_resume(intf); ++ if (ret < 0) ++ netdev_warn(dev->net, "usbnet_resume error\n"); ++ ++ return ret; ++} ++ ++static int smsc95xx_reset_resume(struct usb_interface *intf) ++{ ++ struct usbnet *dev = usb_get_intfdata(intf); ++ int ret; ++ ++ ret = smsc95xx_reset(dev); ++ if (ret < 0) ++ return ret; ++ ++ return smsc95xx_resume(intf); ++} ++ ++static void smsc95xx_rx_csum_offload(struct sk_buff *skb) ++{ ++ skb->csum = *(u16 *)(skb_tail_pointer(skb) - 2); ++ skb->ip_summed = CHECKSUM_COMPLETE; ++ skb_trim(skb, skb->len - 2); ++} ++ ++static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ /* This check is no longer done by usbnet */ ++ if (skb->len < dev->net->hard_header_len) ++ return 0; ++ ++ while (skb->len > 0) { ++ u32 header, align_count; ++ struct sk_buff *ax_skb; ++ unsigned char *packet; ++ u16 size; ++ ++ memcpy(&header, skb->data, sizeof(header)); ++ le32_to_cpus(&header); ++ skb_pull(skb, 4 + NET_IP_ALIGN); ++ packet = skb->data; ++ ++ /* get the packet length */ ++ size = (u16)((header & RX_STS_FL_) >> 16); ++ align_count = (4 - ((size + NET_IP_ALIGN) % 4)) % 4; ++ ++ if (unlikely(header & RX_STS_ES_)) { ++ netif_dbg(dev, rx_err, dev->net, ++ "Error header=0x%08x\n", header); ++ dev->net->stats.rx_errors++; ++ dev->net->stats.rx_dropped++; ++ ++ if (header & RX_STS_CRC_) { ++ dev->net->stats.rx_crc_errors++; ++ } else { ++ if (header & (RX_STS_TL_ | RX_STS_RF_)) ++ dev->net->stats.rx_frame_errors++; ++ ++ if ((header & RX_STS_LE_) && ++ (!(header & RX_STS_FT_))) ++ dev->net->stats.rx_length_errors++; ++ } ++ } else { ++ /* ETH_FRAME_LEN + 4(CRC) + 2(COE) + 4(Vlan) */ ++ if (unlikely(size > (ETH_FRAME_LEN + 12))) { ++ netif_dbg(dev, rx_err, dev->net, ++ "size err header=0x%08x\n", header); ++ return 0; ++ } ++ ++ /* last frame in this batch */ ++ if (skb->len == size) { ++ if (dev->net->features & NETIF_F_RXCSUM) ++ smsc95xx_rx_csum_offload(skb); ++ skb_trim(skb, skb->len - 4); /* remove fcs */ ++ skb->truesize = size + sizeof(struct sk_buff); ++ ++ return 1; ++ } ++ ++ ax_skb = skb_clone(skb, GFP_ATOMIC); ++ if (unlikely(!ax_skb)) { ++ netdev_warn(dev->net, "Error allocating skb\n"); ++ return 0; ++ } ++ ++ ax_skb->len = size; ++ ax_skb->data = packet; ++ skb_set_tail_pointer(ax_skb, size); ++ ++ if (dev->net->features & NETIF_F_RXCSUM) ++ smsc95xx_rx_csum_offload(ax_skb); ++ skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */ ++ ax_skb->truesize = size + sizeof(struct sk_buff); ++ ++ usbnet_skb_return(dev, ax_skb); ++ } ++ ++ skb_pull(skb, size); ++ ++ /* padding bytes before the next frame starts */ ++ if (skb->len) ++ skb_pull(skb, align_count); ++ } ++ ++ if (unlikely(skb->len < 0)) { ++ netdev_warn(dev->net, "invalid rx length<0 %d\n", skb->len); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb) ++{ ++ u16 low_16 = (u16)skb_checksum_start_offset(skb); ++ u16 high_16 = low_16 + skb->csum_offset; ++ return (high_16 << 16) | low_16; ++} ++ ++static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev, ++ struct sk_buff *skb, gfp_t flags) ++{ ++ bool csum = skb->ip_summed == CHECKSUM_PARTIAL; ++ int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD; ++ u32 tx_cmd_a, tx_cmd_b; ++ ++ /* We do not advertise SG, so skbs should be already linearized */ ++ BUG_ON(skb_shinfo(skb)->nr_frags); ++ ++ if (skb_headroom(skb) < overhead) { ++ struct sk_buff *skb2 = skb_copy_expand(skb, ++ overhead, 0, flags); ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ if (!skb) ++ return NULL; ++ } ++ ++ if (csum) { ++ if (skb->len <= 45) { ++ /* workaround - hardware tx checksum does not work ++ * properly with extremely small packets */ ++ long csstart = skb_checksum_start_offset(skb); ++ __wsum calc = csum_partial(skb->data + csstart, ++ skb->len - csstart, 0); ++ *((__sum16 *)(skb->data + csstart ++ + skb->csum_offset)) = csum_fold(calc); ++ ++ csum = false; ++ } else { ++ u32 csum_preamble = smsc95xx_calc_csum_preamble(skb); ++ skb_push(skb, 4); ++ cpu_to_le32s(&csum_preamble); ++ memcpy(skb->data, &csum_preamble, 4); ++ } ++ } ++ ++ skb_push(skb, 4); ++ tx_cmd_b = (u32)(skb->len - 4); ++ if (csum) ++ tx_cmd_b |= TX_CMD_B_CSUM_ENABLE; ++ cpu_to_le32s(&tx_cmd_b); ++ memcpy(skb->data, &tx_cmd_b, 4); ++ ++ skb_push(skb, 4); ++ tx_cmd_a = (u32)(skb->len - 8) | TX_CMD_A_FIRST_SEG_ | ++ TX_CMD_A_LAST_SEG_; ++ cpu_to_le32s(&tx_cmd_a); ++ memcpy(skb->data, &tx_cmd_a, 4); ++ ++ return skb; ++} ++ ++static int smsc95xx_manage_power(struct usbnet *dev, int on) ++{ ++ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); ++ ++ dev->intf->needs_remote_wakeup = on; ++ ++ if (pdata->features & FEATURE_REMOTE_WAKEUP) ++ return 0; ++ ++ /* this chip revision isn't capable of remote wakeup */ ++ netdev_info(dev->net, "hardware isn't capable of remote wakeup\n"); ++ ++ if (on) ++ usb_autopm_get_interface_no_resume(dev->intf); ++ else ++ usb_autopm_put_interface(dev->intf); ++ ++ return 0; ++} ++ ++static const struct driver_info smsc95xx_info = { ++ .description = "smsc95xx USB 2.0 Ethernet", ++ .bind = smsc95xx_bind, ++ .unbind = smsc95xx_unbind, ++ .link_reset = smsc95xx_link_reset, ++ .reset = smsc95xx_reset, ++ .rx_fixup = smsc95xx_rx_fixup, ++ .tx_fixup = smsc95xx_tx_fixup, ++ .status = smsc95xx_status, ++ .manage_power = smsc95xx_manage_power, ++ .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR, ++}; ++ ++static const struct usb_device_id products[] = { ++ { ++ /* SMSC9500 USB Ethernet Device */ ++ USB_DEVICE(0x0424, 0x9500), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9505 USB Ethernet Device */ ++ USB_DEVICE(0x0424, 0x9505), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9500A USB Ethernet Device */ ++ USB_DEVICE(0x0424, 0x9E00), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9505A USB Ethernet Device */ ++ USB_DEVICE(0x0424, 0x9E01), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9512/9514 USB Hub & Ethernet Device */ ++ USB_DEVICE(0x0424, 0xec00), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9500 USB Ethernet Device (SAL10) */ ++ USB_DEVICE(0x0424, 0x9900), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9505 USB Ethernet Device (SAL10) */ ++ USB_DEVICE(0x0424, 0x9901), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9500A USB Ethernet Device (SAL10) */ ++ USB_DEVICE(0x0424, 0x9902), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9505A USB Ethernet Device (SAL10) */ ++ USB_DEVICE(0x0424, 0x9903), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9512/9514 USB Hub & Ethernet Device (SAL10) */ ++ USB_DEVICE(0x0424, 0x9904), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9500A USB Ethernet Device (HAL) */ ++ USB_DEVICE(0x0424, 0x9905), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9505A USB Ethernet Device (HAL) */ ++ USB_DEVICE(0x0424, 0x9906), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9500 USB Ethernet Device (Alternate ID) */ ++ USB_DEVICE(0x0424, 0x9907), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9500A USB Ethernet Device (Alternate ID) */ ++ USB_DEVICE(0x0424, 0x9908), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC9512/9514 USB Hub & Ethernet Device (Alternate ID) */ ++ USB_DEVICE(0x0424, 0x9909), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC LAN9530 USB Ethernet Device */ ++ USB_DEVICE(0x0424, 0x9530), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC LAN9730 USB Ethernet Device */ ++ USB_DEVICE(0x0424, 0x9730), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { ++ /* SMSC LAN89530 USB Ethernet Device */ ++ USB_DEVICE(0x0424, 0x9E08), ++ .driver_info = (unsigned long) &smsc95xx_info, ++ }, ++ { }, /* END */ ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver smsc95xx_driver = { ++ .name = "smsc95xx", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .suspend = smsc95xx_suspend, ++ .resume = smsc95xx_resume, ++ .reset_resume = smsc95xx_reset_resume, ++ .disconnect = usbnet_disconnect, ++ .disable_hub_initiated_lpm = 1, ++ .supports_autosuspend = 1, ++}; ++ ++module_usb_driver(smsc95xx_driver); ++ ++MODULE_AUTHOR("Nancy Lin"); ++MODULE_AUTHOR("Steve Glendinning "); ++MODULE_DESCRIPTION("SMSC95XX USB 2.0 Ethernet Devices"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/smsc95xx.h backports-3.18.1-1/drivers/net/usb/smsc95xx.h +--- backports-3.18.1-1.org/drivers/net/usb/smsc95xx.h 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/smsc95xx.h 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,290 @@ ++ /*************************************************************************** ++ * ++ * Copyright (C) 2007-2008 SMSC ++ * ++ * 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, see . ++ * ++ *****************************************************************************/ ++ ++#ifndef _SMSC95XX_H ++#define _SMSC95XX_H ++ ++/* Tx command words */ ++#define TX_CMD_A_DATA_OFFSET_ (0x001F0000) ++#define TX_CMD_A_FIRST_SEG_ (0x00002000) ++#define TX_CMD_A_LAST_SEG_ (0x00001000) ++#define TX_CMD_A_BUF_SIZE_ (0x000007FF) ++ ++#define TX_CMD_B_CSUM_ENABLE (0x00004000) ++#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000) ++#define TX_CMD_B_DISABLE_PADDING_ (0x00001000) ++#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FF) ++ ++/* Rx status word */ ++#define RX_STS_FF_ (0x40000000) /* Filter Fail */ ++#define RX_STS_FL_ (0x3FFF0000) /* Frame Length */ ++#define RX_STS_ES_ (0x00008000) /* Error Summary */ ++#define RX_STS_BF_ (0x00002000) /* Broadcast Frame */ ++#define RX_STS_LE_ (0x00001000) /* Length Error */ ++#define RX_STS_RF_ (0x00000800) /* Runt Frame */ ++#define RX_STS_MF_ (0x00000400) /* Multicast Frame */ ++#define RX_STS_TL_ (0x00000080) /* Frame too long */ ++#define RX_STS_CS_ (0x00000040) /* Collision Seen */ ++#define RX_STS_FT_ (0x00000020) /* Frame Type */ ++#define RX_STS_RW_ (0x00000010) /* Receive Watchdog */ ++#define RX_STS_ME_ (0x00000008) /* Mii Error */ ++#define RX_STS_DB_ (0x00000004) /* Dribbling */ ++#define RX_STS_CRC_ (0x00000002) /* CRC Error */ ++ ++/* SCSRs */ ++#define ID_REV (0x00) ++#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000) ++#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) ++#define ID_REV_CHIP_ID_9500_ (0x9500) ++#define ID_REV_CHIP_ID_9500A_ (0x9E00) ++#define ID_REV_CHIP_ID_9512_ (0xEC00) ++#define ID_REV_CHIP_ID_9530_ (0x9530) ++#define ID_REV_CHIP_ID_89530_ (0x9E08) ++#define ID_REV_CHIP_ID_9730_ (0x9730) ++ ++#define INT_STS (0x08) ++#define INT_STS_TX_STOP_ (0x00020000) ++#define INT_STS_RX_STOP_ (0x00010000) ++#define INT_STS_PHY_INT_ (0x00008000) ++#define INT_STS_TXE_ (0x00004000) ++#define INT_STS_TDFU_ (0x00002000) ++#define INT_STS_TDFO_ (0x00001000) ++#define INT_STS_RXDF_ (0x00000800) ++#define INT_STS_GPIOS_ (0x000007FF) ++#define INT_STS_CLEAR_ALL_ (0xFFFFFFFF) ++ ++#define RX_CFG (0x0C) ++#define RX_FIFO_FLUSH_ (0x00000001) ++ ++#define TX_CFG (0x10) ++#define TX_CFG_ON_ (0x00000004) ++#define TX_CFG_STOP_ (0x00000002) ++#define TX_CFG_FIFO_FLUSH_ (0x00000001) ++ ++#define HW_CFG (0x14) ++#define HW_CFG_BIR_ (0x00001000) ++#define HW_CFG_LEDB_ (0x00000800) ++#define HW_CFG_RXDOFF_ (0x00000600) ++#define HW_CFG_DRP_ (0x00000040) ++#define HW_CFG_MEF_ (0x00000020) ++#define HW_CFG_LRST_ (0x00000008) ++#define HW_CFG_PSEL_ (0x00000004) ++#define HW_CFG_BCE_ (0x00000002) ++#define HW_CFG_SRST_ (0x00000001) ++ ++#define RX_FIFO_INF (0x18) ++ ++#define PM_CTRL (0x20) ++#define PM_CTL_RES_CLR_WKP_STS (0x00000200) ++#define PM_CTL_DEV_RDY_ (0x00000080) ++#define PM_CTL_SUS_MODE_ (0x00000060) ++#define PM_CTL_SUS_MODE_0 (0x00000000) ++#define PM_CTL_SUS_MODE_1 (0x00000020) ++#define PM_CTL_SUS_MODE_2 (0x00000040) ++#define PM_CTL_SUS_MODE_3 (0x00000060) ++#define PM_CTL_PHY_RST_ (0x00000010) ++#define PM_CTL_WOL_EN_ (0x00000008) ++#define PM_CTL_ED_EN_ (0x00000004) ++#define PM_CTL_WUPS_ (0x00000003) ++#define PM_CTL_WUPS_NO_ (0x00000000) ++#define PM_CTL_WUPS_ED_ (0x00000001) ++#define PM_CTL_WUPS_WOL_ (0x00000002) ++#define PM_CTL_WUPS_MULTI_ (0x00000003) ++ ++#define LED_GPIO_CFG (0x24) ++#define LED_GPIO_CFG_SPD_LED (0x01000000) ++#define LED_GPIO_CFG_LNK_LED (0x00100000) ++#define LED_GPIO_CFG_FDX_LED (0x00010000) ++ ++#define GPIO_CFG (0x28) ++ ++#define AFC_CFG (0x2C) ++ ++/* Hi watermark = 15.5Kb (~10 mtu pkts) */ ++/* low watermark = 3k (~2 mtu pkts) */ ++/* backpressure duration = ~ 350us */ ++/* Apply FC on any frame. */ ++#define AFC_CFG_DEFAULT (0x00F830A1) ++ ++#define E2P_CMD (0x30) ++#define E2P_CMD_BUSY_ (0x80000000) ++#define E2P_CMD_MASK_ (0x70000000) ++#define E2P_CMD_READ_ (0x00000000) ++#define E2P_CMD_EWDS_ (0x10000000) ++#define E2P_CMD_EWEN_ (0x20000000) ++#define E2P_CMD_WRITE_ (0x30000000) ++#define E2P_CMD_WRAL_ (0x40000000) ++#define E2P_CMD_ERASE_ (0x50000000) ++#define E2P_CMD_ERAL_ (0x60000000) ++#define E2P_CMD_RELOAD_ (0x70000000) ++#define E2P_CMD_TIMEOUT_ (0x00000400) ++#define E2P_CMD_LOADED_ (0x00000200) ++#define E2P_CMD_ADDR_ (0x000001FF) ++ ++#define MAX_EEPROM_SIZE (512) ++ ++#define E2P_DATA (0x34) ++#define E2P_DATA_MASK_ (0x000000FF) ++ ++#define BURST_CAP (0x38) ++ ++#define GPIO_WAKE (0x64) ++ ++#define INT_EP_CTL (0x68) ++#define INT_EP_CTL_INTEP_ (0x80000000) ++#define INT_EP_CTL_MACRTO_ (0x00080000) ++#define INT_EP_CTL_TX_STOP_ (0x00020000) ++#define INT_EP_CTL_RX_STOP_ (0x00010000) ++#define INT_EP_CTL_PHY_INT_ (0x00008000) ++#define INT_EP_CTL_TXE_ (0x00004000) ++#define INT_EP_CTL_TDFU_ (0x00002000) ++#define INT_EP_CTL_TDFO_ (0x00001000) ++#define INT_EP_CTL_RXDF_ (0x00000800) ++#define INT_EP_CTL_GPIOS_ (0x000007FF) ++ ++#define BULK_IN_DLY (0x6C) ++ ++/* MAC CSRs */ ++#define MAC_CR (0x100) ++#define MAC_CR_RXALL_ (0x80000000) ++#define MAC_CR_RCVOWN_ (0x00800000) ++#define MAC_CR_LOOPBK_ (0x00200000) ++#define MAC_CR_FDPX_ (0x00100000) ++#define MAC_CR_MCPAS_ (0x00080000) ++#define MAC_CR_PRMS_ (0x00040000) ++#define MAC_CR_INVFILT_ (0x00020000) ++#define MAC_CR_PASSBAD_ (0x00010000) ++#define MAC_CR_HFILT_ (0x00008000) ++#define MAC_CR_HPFILT_ (0x00002000) ++#define MAC_CR_LCOLL_ (0x00001000) ++#define MAC_CR_BCAST_ (0x00000800) ++#define MAC_CR_DISRTY_ (0x00000400) ++#define MAC_CR_PADSTR_ (0x00000100) ++#define MAC_CR_BOLMT_MASK (0x000000C0) ++#define MAC_CR_DFCHK_ (0x00000020) ++#define MAC_CR_TXEN_ (0x00000008) ++#define MAC_CR_RXEN_ (0x00000004) ++ ++#define ADDRH (0x104) ++ ++#define ADDRL (0x108) ++ ++#define HASHH (0x10C) ++ ++#define HASHL (0x110) ++ ++#define MII_ADDR (0x114) ++#define MII_WRITE_ (0x02) ++#define MII_BUSY_ (0x01) ++#define MII_READ_ (0x00) /* ~of MII Write bit */ ++ ++#define MII_DATA (0x118) ++ ++#define FLOW (0x11C) ++#define FLOW_FCPT_ (0xFFFF0000) ++#define FLOW_FCPASS_ (0x00000004) ++#define FLOW_FCEN_ (0x00000002) ++#define FLOW_FCBSY_ (0x00000001) ++ ++#define VLAN1 (0x120) ++ ++#define VLAN2 (0x124) ++ ++#define WUFF (0x128) ++#define LAN9500_WUFF_NUM (4) ++#define LAN9500A_WUFF_NUM (8) ++ ++#define WUCSR (0x12C) ++#define WUCSR_WFF_PTR_RST_ (0x80000000) ++#define WUCSR_GUE_ (0x00000200) ++#define WUCSR_WUFR_ (0x00000040) ++#define WUCSR_MPR_ (0x00000020) ++#define WUCSR_WAKE_EN_ (0x00000004) ++#define WUCSR_MPEN_ (0x00000002) ++ ++#define COE_CR (0x130) ++#define Tx_COE_EN_ (0x00010000) ++#define Rx_COE_MODE_ (0x00000002) ++#define Rx_COE_EN_ (0x00000001) ++ ++/* Vendor-specific PHY Definitions */ ++ ++/* EDPD NLP / crossover time configuration (LAN9500A only) */ ++#define PHY_EDPD_CONFIG (16) ++#define PHY_EDPD_CONFIG_TX_NLP_EN_ ((u16)0x8000) ++#define PHY_EDPD_CONFIG_TX_NLP_1000_ ((u16)0x0000) ++#define PHY_EDPD_CONFIG_TX_NLP_768_ ((u16)0x2000) ++#define PHY_EDPD_CONFIG_TX_NLP_512_ ((u16)0x4000) ++#define PHY_EDPD_CONFIG_TX_NLP_256_ ((u16)0x6000) ++#define PHY_EDPD_CONFIG_RX_1_NLP_ ((u16)0x1000) ++#define PHY_EDPD_CONFIG_RX_NLP_64_ ((u16)0x0000) ++#define PHY_EDPD_CONFIG_RX_NLP_256_ ((u16)0x0400) ++#define PHY_EDPD_CONFIG_RX_NLP_512_ ((u16)0x0800) ++#define PHY_EDPD_CONFIG_RX_NLP_1000_ ((u16)0x0C00) ++#define PHY_EDPD_CONFIG_EXT_CROSSOVER_ ((u16)0x0001) ++#define PHY_EDPD_CONFIG_DEFAULT (PHY_EDPD_CONFIG_TX_NLP_EN_ | \ ++ PHY_EDPD_CONFIG_TX_NLP_768_ | \ ++ PHY_EDPD_CONFIG_RX_1_NLP_) ++ ++/* Mode Control/Status Register */ ++#define PHY_MODE_CTRL_STS (17) ++#define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000) ++#define MODE_CTRL_STS_ENERGYON_ ((u16)0x0002) ++ ++#define SPECIAL_CTRL_STS (27) ++#define SPECIAL_CTRL_STS_OVRRD_AMDIX_ ((u16)0x8000) ++#define SPECIAL_CTRL_STS_AMDIX_ENABLE_ ((u16)0x4000) ++#define SPECIAL_CTRL_STS_AMDIX_STATE_ ((u16)0x2000) ++ ++#define PHY_INT_SRC (29) ++#define PHY_INT_SRC_ENERGY_ON_ ((u16)0x0080) ++#define PHY_INT_SRC_ANEG_COMP_ ((u16)0x0040) ++#define PHY_INT_SRC_REMOTE_FAULT_ ((u16)0x0020) ++#define PHY_INT_SRC_LINK_DOWN_ ((u16)0x0010) ++ ++#define PHY_INT_MASK (30) ++#define PHY_INT_MASK_ENERGY_ON_ ((u16)0x0080) ++#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040) ++#define PHY_INT_MASK_REMOTE_FAULT_ ((u16)0x0020) ++#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010) ++#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \ ++ PHY_INT_MASK_LINK_DOWN_) ++ ++#define PHY_SPECIAL (31) ++#define PHY_SPECIAL_SPD_ ((u16)0x001C) ++#define PHY_SPECIAL_SPD_10HALF_ ((u16)0x0004) ++#define PHY_SPECIAL_SPD_10FULL_ ((u16)0x0014) ++#define PHY_SPECIAL_SPD_100HALF_ ((u16)0x0008) ++#define PHY_SPECIAL_SPD_100FULL_ ((u16)0x0018) ++ ++/* USB Vendor Requests */ ++#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 ++#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 ++#define USB_VENDOR_REQUEST_GET_STATS 0xA2 ++ ++/* Interrupt Endpoint status word bitfields */ ++#define INT_ENP_TX_STOP_ ((u32)BIT(17)) ++#define INT_ENP_RX_STOP_ ((u32)BIT(16)) ++#define INT_ENP_PHY_INT_ ((u32)BIT(15)) ++#define INT_ENP_TXE_ ((u32)BIT(14)) ++#define INT_ENP_TDFU_ ((u32)BIT(13)) ++#define INT_ENP_TDFO_ ((u32)BIT(12)) ++#define INT_ENP_RXDF_ ((u32)BIT(11)) ++ ++#endif /* _SMSC95XX_H */ +diff -Naur backports-3.18.1-1.org/drivers/net/usb/sr9700.c backports-3.18.1-1/drivers/net/usb/sr9700.c +--- backports-3.18.1-1.org/drivers/net/usb/sr9700.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/sr9700.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,559 @@ ++/* ++ * CoreChip-sz SR9700 one chip USB 1.1 Ethernet Devices ++ * ++ * Author : Liu Junliang ++ * ++ * Based on dm9601.c ++ * ++ * This file is licensed under the terms of the GNU General Public License ++ * version 2. This program is licensed "as is" without any warranty of any ++ * kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sr9700.h" ++ ++static int sr_read(struct usbnet *dev, u8 reg, u16 length, void *data) ++{ ++ int err; ++ ++ err = usbnet_read_cmd(dev, SR_RD_REGS, SR_REQ_RD_REG, 0, reg, data, ++ length); ++ if ((err != length) && (err >= 0)) ++ err = -EINVAL; ++ return err; ++} ++ ++static int sr_write(struct usbnet *dev, u8 reg, u16 length, void *data) ++{ ++ int err; ++ ++ err = usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, 0, reg, data, ++ length); ++ if ((err >= 0) && (err < length)) ++ err = -EINVAL; ++ return err; ++} ++ ++static int sr_read_reg(struct usbnet *dev, u8 reg, u8 *value) ++{ ++ return sr_read(dev, reg, 1, value); ++} ++ ++static int sr_write_reg(struct usbnet *dev, u8 reg, u8 value) ++{ ++ return usbnet_write_cmd(dev, SR_WR_REGS, SR_REQ_WR_REG, ++ value, reg, NULL, 0); ++} ++ ++static void sr_write_async(struct usbnet *dev, u8 reg, u16 length, void *data) ++{ ++ usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, ++ 0, reg, data, length); ++} ++ ++static void sr_write_reg_async(struct usbnet *dev, u8 reg, u8 value) ++{ ++ usbnet_write_cmd_async(dev, SR_WR_REGS, SR_REQ_WR_REG, ++ value, reg, NULL, 0); ++} ++ ++static int wait_phy_eeprom_ready(struct usbnet *dev, int phy) ++{ ++ int i; ++ ++ for (i = 0; i < SR_SHARE_TIMEOUT; i++) { ++ u8 tmp = 0; ++ int ret; ++ ++ udelay(1); ++ ret = sr_read_reg(dev, EPCR, &tmp); ++ if (ret < 0) ++ return ret; ++ ++ /* ready */ ++ if (!(tmp & EPCR_ERRE)) ++ return 0; ++ } ++ ++ netdev_err(dev->net, "%s write timed out!\n", phy ? "phy" : "eeprom"); ++ ++ return -EIO; ++} ++ ++static int sr_share_read_word(struct usbnet *dev, int phy, u8 reg, ++ __le16 *value) ++{ ++ int ret; ++ ++ mutex_lock(&dev->phy_mutex); ++ ++ sr_write_reg(dev, EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); ++ sr_write_reg(dev, EPCR, phy ? (EPCR_EPOS | EPCR_ERPRR) : EPCR_ERPRR); ++ ++ ret = wait_phy_eeprom_ready(dev, phy); ++ if (ret < 0) ++ goto out_unlock; ++ ++ sr_write_reg(dev, EPCR, 0x0); ++ ret = sr_read(dev, EPDR, 2, value); ++ ++ netdev_dbg(dev->net, "read shared %d 0x%02x returned 0x%04x, %d\n", ++ phy, reg, *value, ret); ++ ++out_unlock: ++ mutex_unlock(&dev->phy_mutex); ++ return ret; ++} ++ ++static int sr_share_write_word(struct usbnet *dev, int phy, u8 reg, ++ __le16 value) ++{ ++ int ret; ++ ++ mutex_lock(&dev->phy_mutex); ++ ++ ret = sr_write(dev, EPDR, 2, &value); ++ if (ret < 0) ++ goto out_unlock; ++ ++ sr_write_reg(dev, EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); ++ sr_write_reg(dev, EPCR, phy ? (EPCR_WEP | EPCR_EPOS | EPCR_ERPRW) : ++ (EPCR_WEP | EPCR_ERPRW)); ++ ++ ret = wait_phy_eeprom_ready(dev, phy); ++ if (ret < 0) ++ goto out_unlock; ++ ++ sr_write_reg(dev, EPCR, 0x0); ++ ++out_unlock: ++ mutex_unlock(&dev->phy_mutex); ++ return ret; ++} ++ ++static int sr_read_eeprom_word(struct usbnet *dev, u8 offset, void *value) ++{ ++ return sr_share_read_word(dev, 0, offset, value); ++} ++ ++static int sr9700_get_eeprom_len(struct net_device *netdev) ++{ ++ return SR_EEPROM_LEN; ++} ++ ++static int sr9700_get_eeprom(struct net_device *netdev, ++ struct ethtool_eeprom *eeprom, u8 *data) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ __le16 *buf = (__le16 *)data; ++ int ret = 0; ++ int i; ++ ++ /* access is 16bit */ ++ if ((eeprom->offset & 0x01) || (eeprom->len & 0x01)) ++ return -EINVAL; ++ ++ for (i = 0; i < eeprom->len / 2; i++) { ++ ret = sr_read_eeprom_word(dev, eeprom->offset / 2 + i, buf + i); ++ if (ret < 0) ++ break; ++ } ++ ++ return ret; ++} ++ ++static int sr_mdio_read(struct net_device *netdev, int phy_id, int loc) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ __le16 res; ++ int rc = 0; ++ ++ if (phy_id) { ++ netdev_dbg(netdev, "Only internal phy supported\n"); ++ return 0; ++ } ++ ++ /* Access NSR_LINKST bit for link status instead of MII_BMSR */ ++ if (loc == MII_BMSR) { ++ u8 value; ++ ++ sr_read_reg(dev, NSR, &value); ++ if (value & NSR_LINKST) ++ rc = 1; ++ } ++ sr_share_read_word(dev, 1, loc, &res); ++ if (rc == 1) ++ res = le16_to_cpu(res) | BMSR_LSTATUS; ++ else ++ res = le16_to_cpu(res) & ~BMSR_LSTATUS; ++ ++ netdev_dbg(netdev, "sr_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", ++ phy_id, loc, res); ++ ++ return res; ++} ++ ++static void sr_mdio_write(struct net_device *netdev, int phy_id, int loc, ++ int val) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ __le16 res = cpu_to_le16(val); ++ ++ if (phy_id) { ++ netdev_dbg(netdev, "Only internal phy supported\n"); ++ return; ++ } ++ ++ netdev_dbg(netdev, "sr_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", ++ phy_id, loc, val); ++ ++ sr_share_write_word(dev, 1, loc, res); ++} ++ ++static u32 sr9700_get_link(struct net_device *netdev) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ u8 value = 0; ++ int rc = 0; ++ ++ /* Get the Link Status directly */ ++ sr_read_reg(dev, NSR, &value); ++ if (value & NSR_LINKST) ++ rc = 1; ++ ++ return rc; ++} ++ ++static int sr9700_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ ++ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); ++} ++ ++static const struct ethtool_ops sr9700_ethtool_ops = { ++ .get_drvinfo = usbnet_get_drvinfo, ++ .get_link = sr9700_get_link, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_eeprom_len = sr9700_get_eeprom_len, ++ .get_eeprom = sr9700_get_eeprom, ++ .get_settings = usbnet_get_settings, ++ .set_settings = usbnet_set_settings, ++ .nway_reset = usbnet_nway_reset, ++}; ++ ++static void sr9700_set_multicast(struct net_device *netdev) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ /* We use the 20 byte dev->data for our 8 byte filter buffer ++ * to avoid allocating memory that is tricky to free later ++ */ ++ u8 *hashes = (u8 *)&dev->data; ++ /* rx_ctl setting : enable, disable_long, disable_crc */ ++ u8 rx_ctl = RCR_RXEN | RCR_DIS_CRC | RCR_DIS_LONG; ++ ++ memset(hashes, 0x00, SR_MCAST_SIZE); ++ /* broadcast address */ ++ hashes[SR_MCAST_SIZE - 1] |= SR_MCAST_ADDR_FLAG; ++ if (netdev->flags & IFF_PROMISC) { ++ rx_ctl |= RCR_PRMSC; ++ } else if (netdev->flags & IFF_ALLMULTI || ++ netdev_mc_count(netdev) > SR_MCAST_MAX) { ++ rx_ctl |= RCR_RUNT; ++ } else if (!netdev_mc_empty(netdev)) { ++ struct netdev_hw_addr *ha; ++ ++ netdev_for_each_mc_addr(ha, netdev) { ++ u32 crc = ether_crc(ETH_ALEN, ha->addr) >> 26; ++ hashes[crc >> 3] |= 1 << (crc & 0x7); ++ } ++ } ++ ++ sr_write_async(dev, MAR, SR_MCAST_SIZE, hashes); ++ sr_write_reg_async(dev, RCR, rx_ctl); ++} ++ ++static int sr9700_set_mac_address(struct net_device *netdev, void *p) ++{ ++ struct usbnet *dev = netdev_priv(netdev); ++ struct sockaddr *addr = p; ++ ++ if (!is_valid_ether_addr(addr->sa_data)) { ++ netdev_err(netdev, "not setting invalid mac address %pM\n", ++ addr->sa_data); ++ return -EINVAL; ++ } ++ ++ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); ++ sr_write_async(dev, PAR, 6, netdev->dev_addr); ++ ++ return 0; ++} ++ ++static const struct net_device_ops sr9700_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = usbnet_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_do_ioctl = sr9700_ioctl, ++ .ndo_set_rx_mode = sr9700_set_multicast, ++ .ndo_set_mac_address = sr9700_set_mac_address, ++}; ++ ++static int sr9700_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ struct net_device *netdev; ++ struct mii_if_info *mii; ++ int ret; ++ ++ ret = usbnet_get_endpoints(dev, intf); ++ if (ret) ++ goto out; ++ ++ netdev = dev->net; ++ ++ netdev->netdev_ops = &sr9700_netdev_ops; ++ netdev->ethtool_ops = &sr9700_ethtool_ops; ++ netdev->hard_header_len += SR_TX_OVERHEAD; ++ dev->hard_mtu = netdev->mtu + netdev->hard_header_len; ++ /* bulkin buffer is preferably not less than 3K */ ++ dev->rx_urb_size = 3072; ++ ++ mii = &dev->mii; ++ mii->dev = netdev; ++ mii->mdio_read = sr_mdio_read; ++ mii->mdio_write = sr_mdio_write; ++ mii->phy_id_mask = 0x1f; ++ mii->reg_num_mask = 0x1f; ++ ++ sr_write_reg(dev, NCR, NCR_RST); ++ udelay(20); ++ ++ /* read MAC ++ * After Chip Power on, the Chip will reload the MAC from ++ * EEPROM automatically to PAR. In case there is no EEPROM externally, ++ * a default MAC address is stored in PAR for making chip work properly. ++ */ ++ if (sr_read(dev, PAR, ETH_ALEN, netdev->dev_addr) < 0) { ++ netdev_err(netdev, "Error reading MAC address\n"); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ /* power up and reset phy */ ++ sr_write_reg(dev, PRR, PRR_PHY_RST); ++ /* at least 10ms, here 20ms for safe */ ++ mdelay(20); ++ sr_write_reg(dev, PRR, 0); ++ /* at least 1ms, here 2ms for reading right register */ ++ udelay(2 * 1000); ++ ++ /* receive broadcast packets */ ++ sr9700_set_multicast(netdev); ++ ++ sr_mdio_write(netdev, mii->phy_id, MII_BMCR, BMCR_RESET); ++ sr_mdio_write(netdev, mii->phy_id, MII_ADVERTISE, ADVERTISE_ALL | ++ ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); ++ mii_nway_restart(mii); ++ ++out: ++ return ret; ++} ++ ++static int sr9700_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ struct sk_buff *sr_skb; ++ int len; ++ ++ /* skb content (packets) format : ++ * p0 p1 p2 ...... pm ++ * / \ ++ * / \ ++ * / \ ++ * / \ ++ * p0b0 p0b1 p0b2 p0b3 ...... p0b(n-4) p0b(n-3)...p0bn ++ * ++ * p0 : packet 0 ++ * p0b0 : packet 0 byte 0 ++ * ++ * b0: rx status ++ * b1: packet length (incl crc) low ++ * b2: packet length (incl crc) high ++ * b3..n-4: packet data ++ * bn-3..bn: ethernet packet crc ++ */ ++ if (unlikely(skb->len < SR_RX_OVERHEAD)) { ++ netdev_err(dev->net, "unexpected tiny rx frame\n"); ++ return 0; ++ } ++ ++ /* one skb may contains multiple packets */ ++ while (skb->len > SR_RX_OVERHEAD) { ++ if (skb->data[0] != 0x40) ++ return 0; ++ ++ /* ignore the CRC length */ ++ len = (skb->data[1] | (skb->data[2] << 8)) - 4; ++ ++ if (len > ETH_FRAME_LEN) ++ return 0; ++ ++ /* the last packet of current skb */ ++ if (skb->len == (len + SR_RX_OVERHEAD)) { ++ skb_pull(skb, 3); ++ skb->len = len; ++ skb_set_tail_pointer(skb, len); ++ skb->truesize = len + sizeof(struct sk_buff); ++ return 2; ++ } ++ ++ /* skb_clone is used for address align */ ++ sr_skb = skb_clone(skb, GFP_ATOMIC); ++ if (!sr_skb) ++ return 0; ++ ++ sr_skb->len = len; ++ sr_skb->data = skb->data + 3; ++ skb_set_tail_pointer(sr_skb, len); ++ sr_skb->truesize = len + sizeof(struct sk_buff); ++ usbnet_skb_return(dev, sr_skb); ++ ++ skb_pull(skb, len + SR_RX_OVERHEAD); ++ }; ++ ++ return 0; ++} ++ ++static struct sk_buff *sr9700_tx_fixup(struct usbnet *dev, struct sk_buff *skb, ++ gfp_t flags) ++{ ++ int len; ++ ++ /* SR9700 can only send out one ethernet packet at once. ++ * ++ * b0 b1 b2 b3 ...... b(n-4) b(n-3)...bn ++ * ++ * b0: rx status ++ * b1: packet length (incl crc) low ++ * b2: packet length (incl crc) high ++ * b3..n-4: packet data ++ * bn-3..bn: ethernet packet crc ++ */ ++ ++ len = skb->len; ++ ++ if (skb_headroom(skb) < SR_TX_OVERHEAD) { ++ struct sk_buff *skb2; ++ ++ skb2 = skb_copy_expand(skb, SR_TX_OVERHEAD, 0, flags); ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ if (!skb) ++ return NULL; ++ } ++ ++ __skb_push(skb, SR_TX_OVERHEAD); ++ ++ /* usbnet adds padding if length is a multiple of packet size ++ * if so, adjust length value in header ++ */ ++ if ((skb->len % dev->maxpacket) == 0) ++ len++; ++ ++ skb->data[0] = len; ++ skb->data[1] = len >> 8; ++ ++ return skb; ++} ++ ++static void sr9700_status(struct usbnet *dev, struct urb *urb) ++{ ++ int link; ++ u8 *buf; ++ ++ /* format: ++ b0: net status ++ b1: tx status 1 ++ b2: tx status 2 ++ b3: rx status ++ b4: rx overflow ++ b5: rx count ++ b6: tx count ++ b7: gpr ++ */ ++ ++ if (urb->actual_length < 8) ++ return; ++ ++ buf = urb->transfer_buffer; ++ ++ link = !!(buf[0] & 0x40); ++ if (netif_carrier_ok(dev->net) != link) { ++ usbnet_link_change(dev, link, 1); ++ netdev_dbg(dev->net, "Link Status is: %d\n", link); ++ } ++} ++ ++static int sr9700_link_reset(struct usbnet *dev) ++{ ++ struct ethtool_cmd ecmd; ++ ++ mii_check_media(&dev->mii, 1, 1); ++ mii_ethtool_gset(&dev->mii, &ecmd); ++ ++ netdev_dbg(dev->net, "link_reset() speed: %d duplex: %d\n", ++ ecmd.speed, ecmd.duplex); ++ ++ return 0; ++} ++ ++static const struct driver_info sr9700_driver_info = { ++ .description = "CoreChip SR9700 USB Ethernet", ++ .flags = FLAG_ETHER, ++ .bind = sr9700_bind, ++ .rx_fixup = sr9700_rx_fixup, ++ .tx_fixup = sr9700_tx_fixup, ++ .status = sr9700_status, ++ .link_reset = sr9700_link_reset, ++ .reset = sr9700_link_reset, ++}; ++ ++static const struct usb_device_id products[] = { ++ { ++ USB_DEVICE(0x0fe6, 0x9700), /* SR9700 device */ ++ .driver_info = (unsigned long)&sr9700_driver_info, ++ }, ++ {}, /* END */ ++}; ++ ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver sr9700_usb_driver = { ++ .name = "sr9700", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(sr9700_usb_driver); ++ ++MODULE_AUTHOR("liujl "); ++MODULE_DESCRIPTION("SR9700 one chip USB 1.1 USB to Ethernet device from http://www.corechip-sz.com/"); ++MODULE_LICENSE("GPL"); +diff -Naur backports-3.18.1-1.org/drivers/net/usb/sr9700.h backports-3.18.1-1/drivers/net/usb/sr9700.h +--- backports-3.18.1-1.org/drivers/net/usb/sr9700.h 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/sr9700.h 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,173 @@ ++/* ++ * CoreChip-sz SR9700 one chip USB 1.1 Ethernet Devices ++ * ++ * Author : Liu Junliang ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#ifndef _SR9700_H ++#define _SR9700_H ++ ++/* sr9700 spec. register table on Linux platform */ ++ ++/* Network Control Reg */ ++#define NCR 0x00 ++#define NCR_RST (1 << 0) ++#define NCR_LBK (3 << 1) ++#define NCR_FDX (1 << 3) ++#define NCR_WAKEEN (1 << 6) ++/* Network Status Reg */ ++#define NSR 0x01 ++#define NSR_RXRDY (1 << 0) ++#define NSR_RXOV (1 << 1) ++#define NSR_TX1END (1 << 2) ++#define NSR_TX2END (1 << 3) ++#define NSR_TXFULL (1 << 4) ++#define NSR_WAKEST (1 << 5) ++#define NSR_LINKST (1 << 6) ++#define NSR_SPEED (1 << 7) ++/* Tx Control Reg */ ++#define TCR 0x02 ++#define TCR_CRC_DIS (1 << 1) ++#define TCR_PAD_DIS (1 << 2) ++#define TCR_LC_CARE (1 << 3) ++#define TCR_CRS_CARE (1 << 4) ++#define TCR_EXCECM (1 << 5) ++#define TCR_LF_EN (1 << 6) ++/* Tx Status Reg for Packet Index 1 */ ++#define TSR1 0x03 ++#define TSR1_EC (1 << 2) ++#define TSR1_COL (1 << 3) ++#define TSR1_LC (1 << 4) ++#define TSR1_NC (1 << 5) ++#define TSR1_LOC (1 << 6) ++#define TSR1_TLF (1 << 7) ++/* Tx Status Reg for Packet Index 2 */ ++#define TSR2 0x04 ++#define TSR2_EC (1 << 2) ++#define TSR2_COL (1 << 3) ++#define TSR2_LC (1 << 4) ++#define TSR2_NC (1 << 5) ++#define TSR2_LOC (1 << 6) ++#define TSR2_TLF (1 << 7) ++/* Rx Control Reg*/ ++#define RCR 0x05 ++#define RCR_RXEN (1 << 0) ++#define RCR_PRMSC (1 << 1) ++#define RCR_RUNT (1 << 2) ++#define RCR_ALL (1 << 3) ++#define RCR_DIS_CRC (1 << 4) ++#define RCR_DIS_LONG (1 << 5) ++/* Rx Status Reg */ ++#define RSR 0x06 ++#define RSR_AE (1 << 2) ++#define RSR_MF (1 << 6) ++#define RSR_RF (1 << 7) ++/* Rx Overflow Counter Reg */ ++#define ROCR 0x07 ++#define ROCR_ROC (0x7F << 0) ++#define ROCR_RXFU (1 << 7) ++/* Back Pressure Threshold Reg */ ++#define BPTR 0x08 ++#define BPTR_JPT (0x0F << 0) ++#define BPTR_BPHW (0x0F << 4) ++/* Flow Control Threshold Reg */ ++#define FCTR 0x09 ++#define FCTR_LWOT (0x0F << 0) ++#define FCTR_HWOT (0x0F << 4) ++/* rx/tx Flow Control Reg */ ++#define FCR 0x0A ++#define FCR_FLCE (1 << 0) ++#define FCR_BKPA (1 << 4) ++#define FCR_TXPEN (1 << 5) ++#define FCR_TXPF (1 << 6) ++#define FCR_TXP0 (1 << 7) ++/* Eeprom & Phy Control Reg */ ++#define EPCR 0x0B ++#define EPCR_ERRE (1 << 0) ++#define EPCR_ERPRW (1 << 1) ++#define EPCR_ERPRR (1 << 2) ++#define EPCR_EPOS (1 << 3) ++#define EPCR_WEP (1 << 4) ++/* Eeprom & Phy Address Reg */ ++#define EPAR 0x0C ++#define EPAR_EROA (0x3F << 0) ++#define EPAR_PHY_ADR_MASK (0x03 << 6) ++#define EPAR_PHY_ADR (0x01 << 6) ++/* Eeprom & Phy Data Reg */ ++#define EPDR 0x0D /* 0x0D ~ 0x0E for Data Reg Low & High */ ++/* Wakeup Control Reg */ ++#define WCR 0x0F ++#define WCR_MAGICST (1 << 0) ++#define WCR_LINKST (1 << 2) ++#define WCR_MAGICEN (1 << 3) ++#define WCR_LINKEN (1 << 5) ++/* Physical Address Reg */ ++#define PAR 0x10 /* 0x10 ~ 0x15 6 bytes for PAR */ ++/* Multicast Address Reg */ ++#define MAR 0x16 /* 0x16 ~ 0x1D 8 bytes for MAR */ ++/* 0x1e unused */ ++/* Phy Reset Reg */ ++#define PRR 0x1F ++#define PRR_PHY_RST (1 << 0) ++/* Tx sdram Write Pointer Address Low */ ++#define TWPAL 0x20 ++/* Tx sdram Write Pointer Address High */ ++#define TWPAH 0x21 ++/* Tx sdram Read Pointer Address Low */ ++#define TRPAL 0x22 ++/* Tx sdram Read Pointer Address High */ ++#define TRPAH 0x23 ++/* Rx sdram Write Pointer Address Low */ ++#define RWPAL 0x24 ++/* Rx sdram Write Pointer Address High */ ++#define RWPAH 0x25 ++/* Rx sdram Read Pointer Address Low */ ++#define RRPAL 0x26 ++/* Rx sdram Read Pointer Address High */ ++#define RRPAH 0x27 ++/* Vendor ID register */ ++#define VID 0x28 /* 0x28 ~ 0x29 2 bytes for VID */ ++/* Product ID register */ ++#define PID 0x2A /* 0x2A ~ 0x2B 2 bytes for PID */ ++/* CHIP Revision register */ ++#define CHIPR 0x2C ++/* 0x2D --> 0xEF unused */ ++/* USB Device Address */ ++#define USBDA 0xF0 ++#define USBDA_USBFA (0x7F << 0) ++/* RX packet Counter Reg */ ++#define RXC 0xF1 ++/* Tx packet Counter & USB Status Reg */ ++#define TXC_USBS 0xF2 ++#define TXC_USBS_TXC0 (1 << 0) ++#define TXC_USBS_TXC1 (1 << 1) ++#define TXC_USBS_TXC2 (1 << 2) ++#define TXC_USBS_EP1RDY (1 << 5) ++#define TXC_USBS_SUSFLAG (1 << 6) ++#define TXC_USBS_RXFAULT (1 << 7) ++/* USB Control register */ ++#define USBC 0xF4 ++#define USBC_EP3NAK (1 << 4) ++#define USBC_EP3ACK (1 << 5) ++ ++/* Register access commands and flags */ ++#define SR_RD_REGS 0x00 ++#define SR_WR_REGS 0x01 ++#define SR_WR_REG 0x03 ++#define SR_REQ_RD_REG (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE) ++#define SR_REQ_WR_REG (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE) ++ ++/* parameters */ ++#define SR_SHARE_TIMEOUT 1000 ++#define SR_EEPROM_LEN 256 ++#define SR_MCAST_SIZE 8 ++#define SR_MCAST_ADDR_FLAG 0x80 ++#define SR_MCAST_MAX 64 ++#define SR_TX_OVERHEAD 2 /* 2bytes header */ ++#define SR_RX_OVERHEAD 7 /* 3bytes header + 4crc tail */ ++ ++#endif /* _SR9700_H */ +diff -Naur backports-3.18.1-1.org/drivers/net/usb/sr9800.c backports-3.18.1-1/drivers/net/usb/sr9800.c +--- backports-3.18.1-1.org/drivers/net/usb/sr9800.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/sr9800.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,874 @@ ++/* CoreChip-sz SR9800 one chip USB 2.0 Ethernet Devices ++ * ++ * Author : Liu Junliang ++ * ++ * Based on asix_common.c, asix_devices.c ++ * ++ * This file is licensed under the terms of the GNU General Public License ++ * version 2. This program is licensed "as is" without any warranty of any ++ * kind, whether express or implied.* ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sr9800.h" ++ ++static int sr_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data) ++{ ++ int err; ++ ++ err = usbnet_read_cmd(dev, cmd, SR_REQ_RD_REG, value, index, ++ data, size); ++ if ((err != size) && (err >= 0)) ++ err = -EINVAL; ++ ++ return err; ++} ++ ++static int sr_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data) ++{ ++ int err; ++ ++ err = usbnet_write_cmd(dev, cmd, SR_REQ_WR_REG, value, index, ++ data, size); ++ if ((err != size) && (err >= 0)) ++ err = -EINVAL; ++ ++ return err; ++} ++ ++static void ++sr_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, ++ u16 size, void *data) ++{ ++ usbnet_write_cmd_async(dev, cmd, SR_REQ_WR_REG, value, index, data, ++ size); ++} ++ ++static int sr_rx_fixup(struct usbnet *dev, struct sk_buff *skb) ++{ ++ int offset = 0; ++ ++ /* This check is no longer done by usbnet */ ++ if (skb->len < dev->net->hard_header_len) ++ return 0; ++ ++ while (offset + sizeof(u32) < skb->len) { ++ struct sk_buff *sr_skb; ++ u16 size; ++ u32 header = get_unaligned_le32(skb->data + offset); ++ ++ offset += sizeof(u32); ++ /* get the packet length */ ++ size = (u16) (header & 0x7ff); ++ if (size != ((~header >> 16) & 0x07ff)) { ++ netdev_err(dev->net, "%s : Bad Header Length\n", ++ __func__); ++ return 0; ++ } ++ ++ if ((size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) || ++ (size + offset > skb->len)) { ++ netdev_err(dev->net, "%s : Bad RX Length %d\n", ++ __func__, size); ++ return 0; ++ } ++ sr_skb = netdev_alloc_skb_ip_align(dev->net, size); ++ if (!sr_skb) ++ return 0; ++ ++ skb_put(sr_skb, size); ++ memcpy(sr_skb->data, skb->data + offset, size); ++ usbnet_skb_return(dev, sr_skb); ++ ++ offset += (size + 1) & 0xfffe; ++ } ++ ++ if (skb->len != offset) { ++ netdev_err(dev->net, "%s : Bad SKB Length %d\n", __func__, ++ skb->len); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct sk_buff *sr_tx_fixup(struct usbnet *dev, struct sk_buff *skb, ++ gfp_t flags) ++{ ++ int headroom = skb_headroom(skb); ++ int tailroom = skb_tailroom(skb); ++ u32 padbytes = 0xffff0000; ++ u32 packet_len; ++ int padlen; ++ ++ padlen = ((skb->len + 4) % (dev->maxpacket - 1)) ? 0 : 4; ++ ++ if ((!skb_cloned(skb)) && ((headroom + tailroom) >= (4 + padlen))) { ++ if ((headroom < 4) || (tailroom < padlen)) { ++ skb->data = memmove(skb->head + 4, skb->data, ++ skb->len); ++ skb_set_tail_pointer(skb, skb->len); ++ } ++ } else { ++ struct sk_buff *skb2; ++ skb2 = skb_copy_expand(skb, 4, padlen, flags); ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ if (!skb) ++ return NULL; ++ } ++ ++ skb_push(skb, 4); ++ packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4); ++ cpu_to_le32s(&packet_len); ++ skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); ++ ++ if (padlen) { ++ cpu_to_le32s(&padbytes); ++ memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); ++ skb_put(skb, sizeof(padbytes)); ++ } ++ ++ return skb; ++} ++ ++static void sr_status(struct usbnet *dev, struct urb *urb) ++{ ++ struct sr9800_int_data *event; ++ int link; ++ ++ if (urb->actual_length < 8) ++ return; ++ ++ event = urb->transfer_buffer; ++ link = event->link & 0x01; ++ if (netif_carrier_ok(dev->net) != link) { ++ usbnet_link_change(dev, link, 1); ++ netdev_dbg(dev->net, "Link Status is: %d\n", link); ++ } ++ ++ return; ++} ++ ++static inline int sr_set_sw_mii(struct usbnet *dev) ++{ ++ int ret; ++ ++ ret = sr_write_cmd(dev, SR_CMD_SET_SW_MII, 0x0000, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, "Failed to enable software MII access\n"); ++ return ret; ++} ++ ++static inline int sr_set_hw_mii(struct usbnet *dev) ++{ ++ int ret; ++ ++ ret = sr_write_cmd(dev, SR_CMD_SET_HW_MII, 0x0000, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, "Failed to enable hardware MII access\n"); ++ return ret; ++} ++ ++static inline int sr_get_phy_addr(struct usbnet *dev) ++{ ++ u8 buf[2]; ++ int ret; ++ ++ ret = sr_read_cmd(dev, SR_CMD_READ_PHY_ID, 0, 0, 2, buf); ++ if (ret < 0) { ++ netdev_err(dev->net, "%s : Error reading PHYID register:%02x\n", ++ __func__, ret); ++ goto out; ++ } ++ netdev_dbg(dev->net, "%s : returning 0x%04x\n", __func__, ++ *((__le16 *)buf)); ++ ++ ret = buf[1]; ++ ++out: ++ return ret; ++} ++ ++static int sr_sw_reset(struct usbnet *dev, u8 flags) ++{ ++ int ret; ++ ++ ret = sr_write_cmd(dev, SR_CMD_SW_RESET, flags, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, "Failed to send software reset:%02x\n", ++ ret); ++ ++ return ret; ++} ++ ++static u16 sr_read_rx_ctl(struct usbnet *dev) ++{ ++ __le16 v; ++ int ret; ++ ++ ret = sr_read_cmd(dev, SR_CMD_READ_RX_CTL, 0, 0, 2, &v); ++ if (ret < 0) { ++ netdev_err(dev->net, "Error reading RX_CTL register:%02x\n", ++ ret); ++ goto out; ++ } ++ ++ ret = le16_to_cpu(v); ++out: ++ return ret; ++} ++ ++static int sr_write_rx_ctl(struct usbnet *dev, u16 mode) ++{ ++ int ret; ++ ++ netdev_dbg(dev->net, "%s : mode = 0x%04x\n", __func__, mode); ++ ret = sr_write_cmd(dev, SR_CMD_WRITE_RX_CTL, mode, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, ++ "Failed to write RX_CTL mode to 0x%04x:%02x\n", ++ mode, ret); ++ ++ return ret; ++} ++ ++static u16 sr_read_medium_status(struct usbnet *dev) ++{ ++ __le16 v; ++ int ret; ++ ++ ret = sr_read_cmd(dev, SR_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v); ++ if (ret < 0) { ++ netdev_err(dev->net, ++ "Error reading Medium Status register:%02x\n", ret); ++ return ret; /* TODO: callers not checking for error ret */ ++ } ++ ++ return le16_to_cpu(v); ++} ++ ++static int sr_write_medium_mode(struct usbnet *dev, u16 mode) ++{ ++ int ret; ++ ++ netdev_dbg(dev->net, "%s : mode = 0x%04x\n", __func__, mode); ++ ret = sr_write_cmd(dev, SR_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, ++ "Failed to write Medium Mode mode to 0x%04x:%02x\n", ++ mode, ret); ++ return ret; ++} ++ ++static int sr_write_gpio(struct usbnet *dev, u16 value, int sleep) ++{ ++ int ret; ++ ++ netdev_dbg(dev->net, "%s : value = 0x%04x\n", __func__, value); ++ ret = sr_write_cmd(dev, SR_CMD_WRITE_GPIOS, value, 0, 0, NULL); ++ if (ret < 0) ++ netdev_err(dev->net, "Failed to write GPIO value 0x%04x:%02x\n", ++ value, ret); ++ if (sleep) ++ msleep(sleep); ++ ++ return ret; ++} ++ ++/* SR9800 have a 16-bit RX_CTL value */ ++static void sr_set_multicast(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct sr_data *data = (struct sr_data *)&dev->data; ++ u16 rx_ctl = SR_DEFAULT_RX_CTL; ++ ++ if (net->flags & IFF_PROMISC) { ++ rx_ctl |= SR_RX_CTL_PRO; ++ } else if (net->flags & IFF_ALLMULTI || ++ netdev_mc_count(net) > SR_MAX_MCAST) { ++ rx_ctl |= SR_RX_CTL_AMALL; ++ } else if (netdev_mc_empty(net)) { ++ /* just broadcast and directed */ ++ } else { ++ /* We use the 20 byte dev->data ++ * for our 8 byte filter buffer ++ * to avoid allocating memory that ++ * is tricky to free later ++ */ ++ struct netdev_hw_addr *ha; ++ u32 crc_bits; ++ ++ memset(data->multi_filter, 0, SR_MCAST_FILTER_SIZE); ++ ++ /* Build the multicast hash filter. */ ++ netdev_for_each_mc_addr(ha, net) { ++ crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26; ++ data->multi_filter[crc_bits >> 3] |= ++ 1 << (crc_bits & 7); ++ } ++ ++ sr_write_cmd_async(dev, SR_CMD_WRITE_MULTI_FILTER, 0, 0, ++ SR_MCAST_FILTER_SIZE, data->multi_filter); ++ ++ rx_ctl |= SR_RX_CTL_AM; ++ } ++ ++ sr_write_cmd_async(dev, SR_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL); ++} ++ ++static int sr_mdio_read(struct net_device *net, int phy_id, int loc) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ __le16 res; ++ ++ mutex_lock(&dev->phy_mutex); ++ sr_set_sw_mii(dev); ++ sr_read_cmd(dev, SR_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, &res); ++ sr_set_hw_mii(dev); ++ mutex_unlock(&dev->phy_mutex); ++ ++ netdev_dbg(dev->net, ++ "%s : phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", __func__, ++ phy_id, loc, le16_to_cpu(res)); ++ ++ return le16_to_cpu(res); ++} ++ ++static void ++sr_mdio_write(struct net_device *net, int phy_id, int loc, int val) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ __le16 res = cpu_to_le16(val); ++ ++ netdev_dbg(dev->net, ++ "%s : phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", __func__, ++ phy_id, loc, val); ++ mutex_lock(&dev->phy_mutex); ++ sr_set_sw_mii(dev); ++ sr_write_cmd(dev, SR_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); ++ sr_set_hw_mii(dev); ++ mutex_unlock(&dev->phy_mutex); ++} ++ ++/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */ ++static u32 sr_get_phyid(struct usbnet *dev) ++{ ++ int phy_reg; ++ u32 phy_id; ++ int i; ++ ++ /* Poll for the rare case the FW or phy isn't ready yet. */ ++ for (i = 0; i < 100; i++) { ++ phy_reg = sr_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1); ++ if (phy_reg != 0 && phy_reg != 0xFFFF) ++ break; ++ mdelay(1); ++ } ++ ++ if (phy_reg <= 0 || phy_reg == 0xFFFF) ++ return 0; ++ ++ phy_id = (phy_reg & 0xffff) << 16; ++ ++ phy_reg = sr_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2); ++ if (phy_reg < 0) ++ return 0; ++ ++ phy_id |= (phy_reg & 0xffff); ++ ++ return phy_id; ++} ++ ++static void ++sr_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ u8 opt; ++ ++ if (sr_read_cmd(dev, SR_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) { ++ wolinfo->supported = 0; ++ wolinfo->wolopts = 0; ++ return; ++ } ++ wolinfo->supported = WAKE_PHY | WAKE_MAGIC; ++ wolinfo->wolopts = 0; ++ if (opt & SR_MONITOR_LINK) ++ wolinfo->wolopts |= WAKE_PHY; ++ if (opt & SR_MONITOR_MAGIC) ++ wolinfo->wolopts |= WAKE_MAGIC; ++} ++ ++static int ++sr_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ u8 opt = 0; ++ ++ if (wolinfo->wolopts & WAKE_PHY) ++ opt |= SR_MONITOR_LINK; ++ if (wolinfo->wolopts & WAKE_MAGIC) ++ opt |= SR_MONITOR_MAGIC; ++ ++ if (sr_write_cmd(dev, SR_CMD_WRITE_MONITOR_MODE, ++ opt, 0, 0, NULL) < 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int sr_get_eeprom_len(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct sr_data *data = (struct sr_data *)&dev->data; ++ ++ return data->eeprom_len; ++} ++ ++static int sr_get_eeprom(struct net_device *net, ++ struct ethtool_eeprom *eeprom, u8 *data) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ __le16 *ebuf = (__le16 *)data; ++ int ret; ++ int i; ++ ++ /* Crude hack to ensure that we don't overwrite memory ++ * if an odd length is supplied ++ */ ++ if (eeprom->len % 2) ++ return -EINVAL; ++ ++ eeprom->magic = SR_EEPROM_MAGIC; ++ ++ /* sr9800 returns 2 bytes from eeprom on read */ ++ for (i = 0; i < eeprom->len / 2; i++) { ++ ret = sr_read_cmd(dev, SR_CMD_READ_EEPROM, eeprom->offset + i, ++ 0, 2, &ebuf[i]); ++ if (ret < 0) ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void sr_get_drvinfo(struct net_device *net, ++ struct ethtool_drvinfo *info) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct sr_data *data = (struct sr_data *)&dev->data; ++ ++ /* Inherit standard device info */ ++ usbnet_get_drvinfo(net, info); ++ strncpy(info->driver, DRIVER_NAME, sizeof(info->driver)); ++ strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); ++ info->eedump_len = data->eeprom_len; ++} ++ ++static u32 sr_get_link(struct net_device *net) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ ++ return mii_link_ok(&dev->mii); ++} ++ ++static int sr_ioctl(struct net_device *net, struct ifreq *rq, int cmd) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ ++ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); ++} ++ ++static int sr_set_mac_address(struct net_device *net, void *p) ++{ ++ struct usbnet *dev = netdev_priv(net); ++ struct sr_data *data = (struct sr_data *)&dev->data; ++ struct sockaddr *addr = p; ++ ++ if (netif_running(net)) ++ return -EBUSY; ++ if (!is_valid_ether_addr(addr->sa_data)) ++ return -EADDRNOTAVAIL; ++ ++ memcpy(net->dev_addr, addr->sa_data, ETH_ALEN); ++ ++ /* We use the 20 byte dev->data ++ * for our 6 byte mac buffer ++ * to avoid allocating memory that ++ * is tricky to free later ++ */ ++ memcpy(data->mac_addr, addr->sa_data, ETH_ALEN); ++ sr_write_cmd_async(dev, SR_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, ++ data->mac_addr); ++ ++ return 0; ++} ++ ++static const struct ethtool_ops sr9800_ethtool_ops = { ++ .get_drvinfo = sr_get_drvinfo, ++ .get_link = sr_get_link, ++ .get_msglevel = usbnet_get_msglevel, ++ .set_msglevel = usbnet_set_msglevel, ++ .get_wol = sr_get_wol, ++ .set_wol = sr_set_wol, ++ .get_eeprom_len = sr_get_eeprom_len, ++ .get_eeprom = sr_get_eeprom, ++ .get_settings = usbnet_get_settings, ++ .set_settings = usbnet_set_settings, ++ .nway_reset = usbnet_nway_reset, ++}; ++ ++static int sr9800_link_reset(struct usbnet *dev) ++{ ++ struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; ++ u16 mode; ++ ++ mii_check_media(&dev->mii, 1, 1); ++ mii_ethtool_gset(&dev->mii, &ecmd); ++ mode = SR9800_MEDIUM_DEFAULT; ++ ++ if (ethtool_cmd_speed(&ecmd) != SPEED_100) ++ mode &= ~SR_MEDIUM_PS; ++ ++ if (ecmd.duplex != DUPLEX_FULL) ++ mode &= ~SR_MEDIUM_FD; ++ ++ netdev_dbg(dev->net, "%s : speed: %u duplex: %d mode: 0x%04x\n", ++ __func__, ethtool_cmd_speed(&ecmd), ecmd.duplex, mode); ++ ++ sr_write_medium_mode(dev, mode); ++ ++ return 0; ++} ++ ++ ++static int sr9800_set_default_mode(struct usbnet *dev) ++{ ++ u16 rx_ctl; ++ int ret; ++ ++ sr_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); ++ sr_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ++ ADVERTISE_ALL | ADVERTISE_CSMA); ++ mii_nway_restart(&dev->mii); ++ ++ ret = sr_write_medium_mode(dev, SR9800_MEDIUM_DEFAULT); ++ if (ret < 0) ++ goto out; ++ ++ ret = sr_write_cmd(dev, SR_CMD_WRITE_IPG012, ++ SR9800_IPG0_DEFAULT | SR9800_IPG1_DEFAULT, ++ SR9800_IPG2_DEFAULT, 0, NULL); ++ if (ret < 0) { ++ netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret); ++ goto out; ++ } ++ ++ /* Set RX_CTL to default values with 2k buffer, and enable cactus */ ++ ret = sr_write_rx_ctl(dev, SR_DEFAULT_RX_CTL); ++ if (ret < 0) ++ goto out; ++ ++ rx_ctl = sr_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n", ++ rx_ctl); ++ ++ rx_ctl = sr_read_medium_status(dev); ++ netdev_dbg(dev->net, "Medium Status:0x%04x after all initializations\n", ++ rx_ctl); ++ ++ return 0; ++out: ++ return ret; ++} ++ ++static int sr9800_reset(struct usbnet *dev) ++{ ++ struct sr_data *data = (struct sr_data *)&dev->data; ++ int ret, embd_phy; ++ u16 rx_ctl; ++ ++ ret = sr_write_gpio(dev, ++ SR_GPIO_RSE | SR_GPIO_GPO_2 | SR_GPIO_GPO2EN, 5); ++ if (ret < 0) ++ goto out; ++ ++ embd_phy = ((sr_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0); ++ ++ ret = sr_write_cmd(dev, SR_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); ++ if (ret < 0) { ++ netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); ++ goto out; ++ } ++ ++ ret = sr_sw_reset(dev, SR_SWRESET_IPPD | SR_SWRESET_PRL); ++ if (ret < 0) ++ goto out; ++ ++ msleep(150); ++ ++ ret = sr_sw_reset(dev, SR_SWRESET_CLEAR); ++ if (ret < 0) ++ goto out; ++ ++ msleep(150); ++ ++ if (embd_phy) { ++ ret = sr_sw_reset(dev, SR_SWRESET_IPRL); ++ if (ret < 0) ++ goto out; ++ } else { ++ ret = sr_sw_reset(dev, SR_SWRESET_PRTE); ++ if (ret < 0) ++ goto out; ++ } ++ ++ msleep(150); ++ rx_ctl = sr_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); ++ ret = sr_write_rx_ctl(dev, 0x0000); ++ if (ret < 0) ++ goto out; ++ ++ rx_ctl = sr_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); ++ ++ ret = sr_sw_reset(dev, SR_SWRESET_PRL); ++ if (ret < 0) ++ goto out; ++ ++ msleep(150); ++ ++ ret = sr_sw_reset(dev, SR_SWRESET_IPRL | SR_SWRESET_PRL); ++ if (ret < 0) ++ goto out; ++ ++ msleep(150); ++ ++ ret = sr9800_set_default_mode(dev); ++ if (ret < 0) ++ goto out; ++ ++ /* Rewrite MAC address */ ++ memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN); ++ ret = sr_write_cmd(dev, SR_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, ++ data->mac_addr); ++ if (ret < 0) ++ goto out; ++ ++ return 0; ++ ++out: ++ return ret; ++} ++ ++static const struct net_device_ops sr9800_netdev_ops = { ++ .ndo_open = usbnet_open, ++ .ndo_stop = usbnet_stop, ++ .ndo_start_xmit = usbnet_start_xmit, ++ .ndo_tx_timeout = usbnet_tx_timeout, ++ .ndo_change_mtu = usbnet_change_mtu, ++ .ndo_set_mac_address = sr_set_mac_address, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_do_ioctl = sr_ioctl, ++ .ndo_set_rx_mode = sr_set_multicast, ++}; ++ ++static int sr9800_phy_powerup(struct usbnet *dev) ++{ ++ int ret; ++ ++ /* set the embedded Ethernet PHY in power-down state */ ++ ret = sr_sw_reset(dev, SR_SWRESET_IPPD | SR_SWRESET_IPRL); ++ if (ret < 0) { ++ netdev_err(dev->net, "Failed to power down PHY : %d\n", ret); ++ return ret; ++ } ++ msleep(20); ++ ++ /* set the embedded Ethernet PHY in power-up state */ ++ ret = sr_sw_reset(dev, SR_SWRESET_IPRL); ++ if (ret < 0) { ++ netdev_err(dev->net, "Failed to reset PHY: %d\n", ret); ++ return ret; ++ } ++ msleep(600); ++ ++ /* set the embedded Ethernet PHY in reset state */ ++ ret = sr_sw_reset(dev, SR_SWRESET_CLEAR); ++ if (ret < 0) { ++ netdev_err(dev->net, "Failed to power up PHY: %d\n", ret); ++ return ret; ++ } ++ msleep(20); ++ ++ /* set the embedded Ethernet PHY in power-up state */ ++ ret = sr_sw_reset(dev, SR_SWRESET_IPRL); ++ if (ret < 0) { ++ netdev_err(dev->net, "Failed to reset PHY: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int sr9800_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ struct sr_data *data = (struct sr_data *)&dev->data; ++ u16 led01_mux, led23_mux; ++ int ret, embd_phy; ++ u32 phyid; ++ u16 rx_ctl; ++ ++ data->eeprom_len = SR9800_EEPROM_LEN; ++ ++ usbnet_get_endpoints(dev, intf); ++ ++ /* LED Setting Rule : ++ * AABB:CCDD ++ * AA : MFA0(LED0) ++ * BB : MFA1(LED1) ++ * CC : MFA2(LED2), Reserved for SR9800 ++ * DD : MFA3(LED3), Reserved for SR9800 ++ */ ++ led01_mux = (SR_LED_MUX_LINK_ACTIVE << 8) | SR_LED_MUX_LINK; ++ led23_mux = (SR_LED_MUX_LINK_ACTIVE << 8) | SR_LED_MUX_TX_ACTIVE; ++ ret = sr_write_cmd(dev, SR_CMD_LED_MUX, led01_mux, led23_mux, 0, NULL); ++ if (ret < 0) { ++ netdev_err(dev->net, "set LINK LED failed : %d\n", ret); ++ goto out; ++ } ++ ++ /* Get the MAC address */ ++ ret = sr_read_cmd(dev, SR_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, ++ dev->net->dev_addr); ++ if (ret < 0) { ++ netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret); ++ return ret; ++ } ++ netdev_dbg(dev->net, "mac addr : %pM\n", dev->net->dev_addr); ++ ++ /* Initialize MII structure */ ++ dev->mii.dev = dev->net; ++ dev->mii.mdio_read = sr_mdio_read; ++ dev->mii.mdio_write = sr_mdio_write; ++ dev->mii.phy_id_mask = 0x1f; ++ dev->mii.reg_num_mask = 0x1f; ++ dev->mii.phy_id = sr_get_phy_addr(dev); ++ ++ dev->net->netdev_ops = &sr9800_netdev_ops; ++ dev->net->ethtool_ops = &sr9800_ethtool_ops; ++ ++ embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); ++ /* Reset the PHY to normal operation mode */ ++ ret = sr_write_cmd(dev, SR_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL); ++ if (ret < 0) { ++ netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret); ++ return ret; ++ } ++ ++ /* Init PHY routine */ ++ ret = sr9800_phy_powerup(dev); ++ if (ret < 0) ++ goto out; ++ ++ rx_ctl = sr_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl); ++ ret = sr_write_rx_ctl(dev, 0x0000); ++ if (ret < 0) ++ goto out; ++ ++ rx_ctl = sr_read_rx_ctl(dev); ++ netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); ++ ++ /* Read PHYID register *AFTER* the PHY was reset properly */ ++ phyid = sr_get_phyid(dev); ++ netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid); ++ ++ /* medium mode setting */ ++ ret = sr9800_set_default_mode(dev); ++ if (ret < 0) ++ goto out; ++ ++ if (dev->udev->speed == USB_SPEED_HIGH) { ++ ret = sr_write_cmd(dev, SR_CMD_BULKIN_SIZE, ++ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_4K].byte_cnt, ++ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_4K].threshold, ++ 0, NULL); ++ if (ret < 0) { ++ netdev_err(dev->net, "Reset RX_CTL failed: %d\n", ret); ++ goto out; ++ } ++ dev->rx_urb_size = ++ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_4K].size; ++ } else { ++ ret = sr_write_cmd(dev, SR_CMD_BULKIN_SIZE, ++ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_2K].byte_cnt, ++ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_2K].threshold, ++ 0, NULL); ++ if (ret < 0) { ++ netdev_err(dev->net, "Reset RX_CTL failed: %d\n", ret); ++ goto out; ++ } ++ dev->rx_urb_size = ++ SR9800_BULKIN_SIZE[SR9800_MAX_BULKIN_2K].size; ++ } ++ netdev_dbg(dev->net, "%s : setting rx_urb_size with : %zu\n", __func__, ++ dev->rx_urb_size); ++ return 0; ++ ++out: ++ return ret; ++} ++ ++static const struct driver_info sr9800_driver_info = { ++ .description = "CoreChip SR9800 USB 2.0 Ethernet", ++ .bind = sr9800_bind, ++ .status = sr_status, ++ .link_reset = sr9800_link_reset, ++ .reset = sr9800_reset, ++ .flags = DRIVER_FLAG, ++ .rx_fixup = sr_rx_fixup, ++ .tx_fixup = sr_tx_fixup, ++}; ++ ++static const struct usb_device_id products[] = { ++ { ++ USB_DEVICE(0x0fe6, 0x9800), /* SR9800 Device */ ++ .driver_info = (unsigned long) &sr9800_driver_info, ++ }, ++ {}, /* END */ ++}; ++ ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver sr_driver = { ++ .name = DRIVER_NAME, ++ .id_table = products, ++ .probe = usbnet_probe, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disconnect = usbnet_disconnect, ++ .supports_autosuspend = 1, ++}; ++ ++module_usb_driver(sr_driver); ++ ++MODULE_AUTHOR("Liu Junliang ++ * ++ * This file is licensed under the terms of the GNU General Public License ++ * version 2. This program is licensed "as is" without any warranty of any ++ * kind, whether express or implied. ++ */ ++ ++#ifndef _SR9800_H ++#define _SR9800_H ++ ++/* SR9800 spec. command table on Linux Platform */ ++ ++/* command : Software Station Management Control Reg */ ++#define SR_CMD_SET_SW_MII 0x06 ++/* command : PHY Read Reg */ ++#define SR_CMD_READ_MII_REG 0x07 ++/* command : PHY Write Reg */ ++#define SR_CMD_WRITE_MII_REG 0x08 ++/* command : Hardware Station Management Control Reg */ ++#define SR_CMD_SET_HW_MII 0x0a ++/* command : SROM Read Reg */ ++#define SR_CMD_READ_EEPROM 0x0b ++/* command : SROM Write Reg */ ++#define SR_CMD_WRITE_EEPROM 0x0c ++/* command : SROM Write Enable Reg */ ++#define SR_CMD_WRITE_ENABLE 0x0d ++/* command : SROM Write Disable Reg */ ++#define SR_CMD_WRITE_DISABLE 0x0e ++/* command : RX Control Read Reg */ ++#define SR_CMD_READ_RX_CTL 0x0f ++#define SR_RX_CTL_PRO (1 << 0) ++#define SR_RX_CTL_AMALL (1 << 1) ++#define SR_RX_CTL_SEP (1 << 2) ++#define SR_RX_CTL_AB (1 << 3) ++#define SR_RX_CTL_AM (1 << 4) ++#define SR_RX_CTL_AP (1 << 5) ++#define SR_RX_CTL_ARP (1 << 6) ++#define SR_RX_CTL_SO (1 << 7) ++#define SR_RX_CTL_RH1M (1 << 8) ++#define SR_RX_CTL_RH2M (1 << 9) ++#define SR_RX_CTL_RH3M (1 << 10) ++/* command : RX Control Write Reg */ ++#define SR_CMD_WRITE_RX_CTL 0x10 ++/* command : IPG0/IPG1/IPG2 Control Read Reg */ ++#define SR_CMD_READ_IPG012 0x11 ++/* command : IPG0/IPG1/IPG2 Control Write Reg */ ++#define SR_CMD_WRITE_IPG012 0x12 ++/* command : Node ID Read Reg */ ++#define SR_CMD_READ_NODE_ID 0x13 ++/* command : Node ID Write Reg */ ++#define SR_CMD_WRITE_NODE_ID 0x14 ++/* command : Multicast Filter Array Read Reg */ ++#define SR_CMD_READ_MULTI_FILTER 0x15 ++/* command : Multicast Filter Array Write Reg */ ++#define SR_CMD_WRITE_MULTI_FILTER 0x16 ++/* command : Eth/HomePNA PHY Address Reg */ ++#define SR_CMD_READ_PHY_ID 0x19 ++/* command : Medium Status Read Reg */ ++#define SR_CMD_READ_MEDIUM_STATUS 0x1a ++#define SR_MONITOR_LINK (1 << 1) ++#define SR_MONITOR_MAGIC (1 << 2) ++#define SR_MONITOR_HSFS (1 << 4) ++/* command : Medium Status Write Reg */ ++#define SR_CMD_WRITE_MEDIUM_MODE 0x1b ++#define SR_MEDIUM_GM (1 << 0) ++#define SR_MEDIUM_FD (1 << 1) ++#define SR_MEDIUM_AC (1 << 2) ++#define SR_MEDIUM_ENCK (1 << 3) ++#define SR_MEDIUM_RFC (1 << 4) ++#define SR_MEDIUM_TFC (1 << 5) ++#define SR_MEDIUM_JFE (1 << 6) ++#define SR_MEDIUM_PF (1 << 7) ++#define SR_MEDIUM_RE (1 << 8) ++#define SR_MEDIUM_PS (1 << 9) ++#define SR_MEDIUM_RSV (1 << 10) ++#define SR_MEDIUM_SBP (1 << 11) ++#define SR_MEDIUM_SM (1 << 12) ++/* command : Monitor Mode Status Read Reg */ ++#define SR_CMD_READ_MONITOR_MODE 0x1c ++/* command : Monitor Mode Status Write Reg */ ++#define SR_CMD_WRITE_MONITOR_MODE 0x1d ++/* command : GPIO Status Read Reg */ ++#define SR_CMD_READ_GPIOS 0x1e ++#define SR_GPIO_GPO0EN (1 << 0) /* GPIO0 Output enable */ ++#define SR_GPIO_GPO_0 (1 << 1) /* GPIO0 Output value */ ++#define SR_GPIO_GPO1EN (1 << 2) /* GPIO1 Output enable */ ++#define SR_GPIO_GPO_1 (1 << 3) /* GPIO1 Output value */ ++#define SR_GPIO_GPO2EN (1 << 4) /* GPIO2 Output enable */ ++#define SR_GPIO_GPO_2 (1 << 5) /* GPIO2 Output value */ ++#define SR_GPIO_RESERVED (1 << 6) /* Reserved */ ++#define SR_GPIO_RSE (1 << 7) /* Reload serial EEPROM */ ++/* command : GPIO Status Write Reg */ ++#define SR_CMD_WRITE_GPIOS 0x1f ++/* command : Eth PHY Power and Reset Control Reg */ ++#define SR_CMD_SW_RESET 0x20 ++#define SR_SWRESET_CLEAR 0x00 ++#define SR_SWRESET_RR (1 << 0) ++#define SR_SWRESET_RT (1 << 1) ++#define SR_SWRESET_PRTE (1 << 2) ++#define SR_SWRESET_PRL (1 << 3) ++#define SR_SWRESET_BZ (1 << 4) ++#define SR_SWRESET_IPRL (1 << 5) ++#define SR_SWRESET_IPPD (1 << 6) ++/* command : Software Interface Selection Status Read Reg */ ++#define SR_CMD_SW_PHY_STATUS 0x21 ++/* command : Software Interface Selection Status Write Reg */ ++#define SR_CMD_SW_PHY_SELECT 0x22 ++/* command : BULK in Buffer Size Reg */ ++#define SR_CMD_BULKIN_SIZE 0x2A ++/* command : LED_MUX Control Reg */ ++#define SR_CMD_LED_MUX 0x70 ++#define SR_LED_MUX_TX_ACTIVE (1 << 0) ++#define SR_LED_MUX_RX_ACTIVE (1 << 1) ++#define SR_LED_MUX_COLLISION (1 << 2) ++#define SR_LED_MUX_DUP_COL (1 << 3) ++#define SR_LED_MUX_DUP (1 << 4) ++#define SR_LED_MUX_SPEED (1 << 5) ++#define SR_LED_MUX_LINK_ACTIVE (1 << 6) ++#define SR_LED_MUX_LINK (1 << 7) ++ ++/* Register Access Flags */ ++#define SR_REQ_RD_REG (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE) ++#define SR_REQ_WR_REG (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE) ++ ++/* Multicast Filter Array size & Max Number */ ++#define SR_MCAST_FILTER_SIZE 8 ++#define SR_MAX_MCAST 64 ++ ++/* IPG0/1/2 Default Value */ ++#define SR9800_IPG0_DEFAULT 0x15 ++#define SR9800_IPG1_DEFAULT 0x0c ++#define SR9800_IPG2_DEFAULT 0x12 ++ ++/* Medium Status Default Mode */ ++#define SR9800_MEDIUM_DEFAULT \ ++ (SR_MEDIUM_FD | SR_MEDIUM_RFC | \ ++ SR_MEDIUM_TFC | SR_MEDIUM_PS | \ ++ SR_MEDIUM_AC | SR_MEDIUM_RE) ++ ++/* RX Control Default Setting */ ++#define SR_DEFAULT_RX_CTL \ ++ (SR_RX_CTL_SO | SR_RX_CTL_AB | SR_RX_CTL_RH1M) ++ ++/* EEPROM Magic Number & EEPROM Size */ ++#define SR_EEPROM_MAGIC 0xdeadbeef ++#define SR9800_EEPROM_LEN 0xff ++ ++/* SR9800 Driver Version and Driver Name */ ++#define DRIVER_VERSION "11-Nov-2013" ++#define DRIVER_NAME "CoreChips" ++#define DRIVER_FLAG \ ++ (FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET) ++ ++/* SR9800 BULKIN Buffer Size */ ++#define SR9800_MAX_BULKIN_2K 0 ++#define SR9800_MAX_BULKIN_4K 1 ++#define SR9800_MAX_BULKIN_6K 2 ++#define SR9800_MAX_BULKIN_8K 3 ++#define SR9800_MAX_BULKIN_16K 4 ++#define SR9800_MAX_BULKIN_20K 5 ++#define SR9800_MAX_BULKIN_24K 6 ++#define SR9800_MAX_BULKIN_32K 7 ++ ++struct {unsigned short size, byte_cnt, threshold; } SR9800_BULKIN_SIZE[] = { ++ /* 2k */ ++ {2048, 0x8000, 0x8001}, ++ /* 4k */ ++ {4096, 0x8100, 0x8147}, ++ /* 6k */ ++ {6144, 0x8200, 0x81EB}, ++ /* 8k */ ++ {8192, 0x8300, 0x83D7}, ++ /* 16 */ ++ {16384, 0x8400, 0x851E}, ++ /* 20k */ ++ {20480, 0x8500, 0x8666}, ++ /* 24k */ ++ {24576, 0x8600, 0x87AE}, ++ /* 32k */ ++ {32768, 0x8700, 0x8A3D}, ++}; ++ ++/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */ ++struct sr_data { ++ u8 multi_filter[SR_MCAST_FILTER_SIZE]; ++ u8 mac_addr[ETH_ALEN]; ++ u8 phymode; ++ u8 ledmode; ++ u8 eeprom_len; ++}; ++ ++struct sr9800_int_data { ++ __le16 res1; ++ u8 link; ++ __le16 res2; ++ u8 status; ++ __le16 res3; ++} __packed; ++ ++#endif /* _SR9800_H */ +diff -Naur backports-3.18.1-1.org/drivers/net/usb/zaurus.c backports-3.18.1-1/drivers/net/usb/zaurus.c +--- backports-3.18.1-1.org/drivers/net/usb/zaurus.c 1970-01-01 01:00:00.000000000 +0100 ++++ backports-3.18.1-1/drivers/net/usb/zaurus.c 2014-12-16 18:39:45.000000000 +0100 +@@ -0,0 +1,385 @@ ++/* ++ * Copyright (C) 2002 Pavel Machek ++ * Copyright (C) 2002-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, see . ++ */ ++ ++// #define DEBUG // error path messages, extra info ++// #define VERBOSE // more; success messages ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * All known Zaurii lie about their standards conformance. At least ++ * the earliest SA-1100 models lie by saying they support CDC Ethernet. ++ * Some later models (especially PXA-25x and PXA-27x based ones) lie ++ * and say they support CDC MDLM (for access to cell phone modems). ++ * ++ * There are non-Zaurus products that use these same protocols too. ++ * ++ * The annoying thing is that at the same time Sharp was developing ++ * that annoying standards-breaking software, the Linux community had ++ * a simple "CDC Subset" working reliably on the same SA-1100 hardware. ++ * That is, the same functionality but not violating standards. ++ * ++ * The CDC Ethernet nonconformance points are troublesome to hosts ++ * with a true CDC Ethernet implementation: ++ * - Framing appends a CRC, which the spec says drivers "must not" do; ++ * - Transfers data in altsetting zero, instead of altsetting 1; ++ * - All these peripherals use the same ethernet address. ++ * ++ * The CDC MDLM nonconformance is less immediately troublesome, since all ++ * MDLM implementations are quasi-proprietary anyway. ++ */ ++ ++static struct sk_buff * ++zaurus_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) ++{ ++ int padlen; ++ struct sk_buff *skb2; ++ ++ padlen = 2; ++ if (!skb_cloned(skb)) { ++ int tailroom = skb_tailroom(skb); ++ if ((padlen + 4) <= tailroom) ++ goto done; ++ } ++ skb2 = skb_copy_expand(skb, 0, 4 + padlen, flags); ++ dev_kfree_skb_any(skb); ++ skb = skb2; ++ if (skb) { ++ u32 fcs; ++done: ++ fcs = crc32_le(~0, skb->data, skb->len); ++ fcs = ~fcs; ++ ++ *skb_put (skb, 1) = fcs & 0xff; ++ *skb_put (skb, 1) = (fcs>> 8) & 0xff; ++ *skb_put (skb, 1) = (fcs>>16) & 0xff; ++ *skb_put (skb, 1) = (fcs>>24) & 0xff; ++ } ++ return skb; ++} ++ ++static int zaurus_bind(struct usbnet *dev, struct usb_interface *intf) ++{ ++ /* Belcarra's funky framing has other options; mostly ++ * TRAILERS (!) with 4 bytes CRC, and maybe 2 pad bytes. ++ */ ++ dev->net->hard_header_len += 6; ++ dev->rx_urb_size = dev->net->hard_header_len + dev->net->mtu; ++ return usbnet_generic_cdc_bind(dev, intf); ++} ++ ++/* PDA style devices are always connected if present */ ++static int always_connected (struct usbnet *dev) ++{ ++ return 0; ++} ++ ++static const struct driver_info zaurus_sl5x00_info = { ++ .description = "Sharp Zaurus SL-5x00", ++ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z, ++ .check_connect = always_connected, ++ .bind = zaurus_bind, ++ .unbind = usbnet_cdc_unbind, ++ .tx_fixup = zaurus_tx_fixup, ++}; ++#define ZAURUS_STRONGARM_INFO ((unsigned long)&zaurus_sl5x00_info) ++ ++static const struct driver_info zaurus_pxa_info = { ++ .description = "Sharp Zaurus, PXA-2xx based", ++ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z, ++ .check_connect = always_connected, ++ .bind = zaurus_bind, ++ .unbind = usbnet_cdc_unbind, ++ .tx_fixup = zaurus_tx_fixup, ++}; ++#define ZAURUS_PXA_INFO ((unsigned long)&zaurus_pxa_info) ++ ++static const struct driver_info olympus_mxl_info = { ++ .description = "Olympus R1000", ++ .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_Z, ++ .check_connect = always_connected, ++ .bind = zaurus_bind, ++ .unbind = usbnet_cdc_unbind, ++ .tx_fixup = zaurus_tx_fixup, ++}; ++#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] & ~0x02) != 0x01) { ++ /* bmDataCapabilities == 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; ++ } ++ ++ /* same extra framing as for non-BLAN mode */ ++ dev->net->hard_header_len += 6; ++ dev->rx_urb_size = dev->net->hard_header_len ++ + dev->net->mtu; ++ 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 usbnet_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_POINTTOPOINT | FLAG_FRAMING_Z, ++ .check_connect = always_connected, ++ .tx_fixup = zaurus_tx_fixup, ++ .bind = blan_mdlm_bind, ++}; ++ ++static const struct usb_device_id products [] = { ++#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. */ ++{ ++ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO ++ | USB_DEVICE_ID_MATCH_DEVICE, ++ .idVendor = 0x04DD, ++ .idProduct = 0x8004, ++ ZAURUS_MASTER_INTERFACE, ++ .driver_info = ZAURUS_STRONGARM_INFO, ++}, ++ ++/* PXA-2xx based models are also lying-about-cdc. If you add any ++ * more devices that claim to be CDC Ethernet, make sure they get ++ * added to the blacklist in cdc_ether too. ++ * ++ * NOTE: OpenZaurus versions with 2.6 kernels won't use these entries, ++ * unlike the older ones with 2.4 "embedix" kernels. ++ */ ++{ ++ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO ++ | USB_DEVICE_ID_MATCH_DEVICE, ++ .idVendor = 0x04DD, ++ .idProduct = 0x8005, /* A-300 */ ++ ZAURUS_MASTER_INTERFACE, ++ .driver_info = ZAURUS_PXA_INFO, ++}, { ++ .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 = ZAURUS_PXA_INFO, ++}, { ++ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO ++ | USB_DEVICE_ID_MATCH_DEVICE, ++ .idVendor = 0x04DD, ++ .idProduct = 0x8007, /* C-700 */ ++ ZAURUS_MASTER_INTERFACE, ++ .driver_info = ZAURUS_PXA_INFO, ++}, { ++ .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 = ZAURUS_PXA_INFO, ++}, { ++ /* C-750/C-760/C-860/SL-C3000 PDA in MDLM mode */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x04DD, 0x9031, USB_CLASS_COMM, ++ USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), ++ .driver_info = (unsigned long) &bogus_mdlm_info, ++}, { ++ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO ++ | USB_DEVICE_ID_MATCH_DEVICE, ++ .idVendor = 0x04DD, ++ .idProduct = 0x9032, /* SL-6000 */ ++ ZAURUS_MASTER_INTERFACE, ++ .driver_info = ZAURUS_PXA_INFO, ++}, { ++ .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 = ZAURUS_PXA_INFO, ++}, ++{ ++ /* Motorola Rokr E6 */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x6027, USB_CLASS_COMM, ++ USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), ++ .driver_info = (unsigned long) &bogus_mdlm_info, ++}, { ++ /* Motorola MOTOMAGX phones */ ++ USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x6425, USB_CLASS_COMM, ++ USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), ++ .driver_info = (unsigned long) &bogus_mdlm_info, ++}, ++ ++/* Olympus has some models with a Zaurus-compatible option. ++ * R-1000 uses a FreeScale i.MXL cpu (ARMv4T) ++ */ ++{ ++ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO ++ | USB_DEVICE_ID_MATCH_DEVICE, ++ .idVendor = 0x07B4, ++ .idProduct = 0x0F02, /* R-1000 */ ++ ZAURUS_MASTER_INTERFACE, ++ .driver_info = OLYMPUS_MXL_INFO, ++}, ++ ++/* Logitech Harmony 900 - uses the pseudo-MDLM (BLAN) driver */ ++{ ++ USB_DEVICE_AND_INTERFACE_INFO(0x046d, 0xc11f, USB_CLASS_COMM, ++ USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), ++ .driver_info = (unsigned long) &bogus_mdlm_info, ++}, ++ { }, // END ++}; ++MODULE_DEVICE_TABLE(usb, products); ++ ++static struct usb_driver zaurus_driver = { ++ .name = "zaurus", ++ .id_table = products, ++ .probe = usbnet_probe, ++ .disconnect = usbnet_disconnect, ++ .suspend = usbnet_suspend, ++ .resume = usbnet_resume, ++ .disable_hub_initiated_lpm = 1, ++}; ++ ++module_usb_driver(zaurus_driver); ++ ++MODULE_AUTHOR("Pavel Machek, David Brownell"); ++MODULE_DESCRIPTION("Sharp Zaurus PDA, and compatible products"); ++MODULE_LICENSE("GPL"); -- 2.39.2