support 3 usb ethernet: dm9620, ax8872b, sr9700 (2)
author胡卫国 <hwg@rock-chips.com>
Wed, 20 Jul 2011 07:06:20 +0000 (15:06 +0800)
committer杨云 <yangyun@rock-chips.com>
Wed, 20 Jul 2011 07:06:20 +0000 (15:06 +0800)
drivers/net/usb/asix.h [new file with mode: 0755]
drivers/net/usb/axusbnet.c [new file with mode: 0755]
drivers/net/usb/axusbnet.h [new file with mode: 0755]
drivers/net/usb/dm9620.c [new file with mode: 0755]
drivers/net/usb/sr9700.c [new file with mode: 0755]
drivers/net/usb/sr9700.h [new file with mode: 0755]

diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h
new file mode 100755 (executable)
index 0000000..eb86d24
--- /dev/null
@@ -0,0 +1,550 @@
+#ifndef        __LINUX_USBNET_ASIX_H
+#define        __LINUX_USBNET_ASIX_H
+
+/*
+ * Turn on this flag if the implementation of your USB host controller
+ * cannot handle non-double word aligned buffer.
+ * When turn on this flag, driver will fixup egress packet aligned on double
+ * word boundary before deliver to USB host controller. And will Disable the
+ * function "skb_reserve (skb, NET_IP_ALIGN)" to retain the buffer aligned on
+ * double word alignment for ingress packets.
+ */
+#define AX_FORCE_BUFF_ALIGN            1
+
+#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 AX_MEDIUM_FULL_DUPLEX          0x02
+#define AX_MEDIUM_TX_ABORT_ALLOW       0x04
+#define AX_MEDIUM_FLOW_CONTROL_EN      0x10
+#define AX_MCAST_FILTER_SIZE           8
+#define AX_MAX_MCAST                   64
+
+#define AX_EEPROM_LEN                  0x40
+
+#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 AX_SWRESET_IPOSC               0x0080
+#define AX_SWRESET_IPPSL_0             0x0100
+#define AX_SWRESET_IPPSL_1             0x0200
+#define AX_SWRESET_IPCOPS              0x0400
+#define AX_SWRESET_IPCOPSC             0x0800
+#define AX_SWRESET_AUTODETACH          0x1000
+#define AX_SWRESET_WOLLP               0x8000
+
+#define AX88772_IPG0_DEFAULT           0x15
+#define AX88772_IPG1_DEFAULT           0x0c
+#define AX88772_IPG2_DEFAULT           0x0E
+
+#define AX88772A_IPG0_DEFAULT          0x15
+#define AX88772A_IPG1_DEFAULT          0x16
+#define AX88772A_IPG2_DEFAULT          0x1A
+
+#define AX88772_MEDIUM_FULL_DUPLEX     0x0002
+#define AX88772_MEDIUM_RESERVED                0x0004
+#define AX88772_MEDIUM_RX_FC_ENABLE    0x0010
+#define AX88772_MEDIUM_TX_FC_ENABLE    0x0020
+#define AX88772_MEDIUM_PAUSE_FORMAT    0x0080
+#define AX88772_MEDIUM_RX_ENABLE       0x0100
+#define AX88772_MEDIUM_100MB           0x0200
+#define AX88772_MEDIUM_DEFAULT \
+       (AX88772_MEDIUM_FULL_DUPLEX | AX88772_MEDIUM_RX_FC_ENABLE | \
+        AX88772_MEDIUM_TX_FC_ENABLE | AX88772_MEDIUM_100MB | \
+        AX88772_MEDIUM_RESERVED | AX88772_MEDIUM_RX_ENABLE )
+
+#define AX_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_EEPROM_EN         0x0d
+#define AX_CMD_WRITE_EEPROM_DIS                0x0e
+#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_WRITE_IPG2              0x14
+#define AX_CMD_WRITE_MULTI_FILTER      0x16
+#define AX_CMD_READ_NODE_ID            0x17
+#define AX_CMD_READ_PHY_ID             0x19
+#define AX_CMD_READ_MEDIUM_MODE                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_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_PHYSEL_PSEL          (1 << 0)
+       #define AX_PHYSEL_ASEL          (1 << 1)
+       #define AX_PHYSEL_SSMII         (1 << 2)
+       #define AX_PHYSEL_SSRMII        (2 << 2)
+       #define AX_PHYSEL_SSRRMII       (3 << 2)
+       #define AX_PHYSEL_SSEN          (1 << 4)
+#define AX88772_CMD_READ_NODE_ID       0x13
+#define AX88772_CMD_WRITE_NODE_ID      0x14
+#define AX_CMD_READ_RXCOE_CTL          0x2b
+#define AX_CMD_WRITE_RXCOE_CTL         0x2c
+#define AX_CMD_READ_TXCOE_CTL          0x2d
+#define AX_CMD_WRITE_TXCOE_CTL         0x2e
+
+#define REG_LENGTH                     2
+#define PHY_ID_MASK                    0x1f
+
+#define AX_RXCOE_IPCE                  0x0001
+#define AX_RXCOE_IPVE                  0x0002
+#define AX_RXCOE_V6VE                  0x0004
+#define AX_RXCOE_TCPE                  0x0008
+#define AX_RXCOE_UDPE                  0x0010
+#define AX_RXCOE_ICMP                  0x0020
+#define AX_RXCOE_IGMP                  0x0040
+#define AX_RXCOE_ICV6                  0x0080
+#define AX_RXCOE_TCPV6                 0x0100
+#define AX_RXCOE_UDPV6                 0x0200
+#define AX_RXCOE_ICMV6                 0x0400
+#define AX_RXCOE_IGMV6                 0x0800
+#define AX_RXCOE_ICV6V6                        0x1000
+#define AX_RXCOE_FOPC                  0x8000
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)
+#define AX_RXCOE_DEF_CSUM              (AX_RXCOE_IPCE | AX_RXCOE_IPVE | \
+                                        AX_RXCOE_V6VE | AX_RXCOE_TCPE | \
+                                        AX_RXCOE_UDPE |  AX_RXCOE_ICV6 | \
+                                        AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6)
+#else
+#define AX_RXCOE_DEF_CSUM              (AX_RXCOE_IPCE | AX_RXCOE_IPVE | \
+                                        AX_RXCOE_TCPE | AX_RXCOE_UDPE)
+#endif
+
+#define AX_RXCOE_64TE                  0x0100
+#define AX_RXCOE_PPPOE                 0x0200
+#define AX_RXCOE_RPCE                  0x8000
+
+#define AX_TXCOE_IP                    0x0001
+#define AX_TXCOE_TCP                   0x0002
+#define AX_TXCOE_UDP                   0x0004
+#define AX_TXCOE_ICMP                  0x0008
+#define AX_TXCOE_IGMP                  0x0010
+#define AX_TXCOE_ICV6                  0x0020
+
+#define AX_TXCOE_TCPV6                 0x0100
+#define AX_TXCOE_UDPV6                 0x0200
+#define AX_TXCOE_ICMV6                 0x0400
+#define AX_TXCOE_IGMV6                 0x0800
+#define AX_TXCOE_ICV6V6                        0x1000
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)
+#define AX_TXCOE_DEF_CSUM              (AX_TXCOE_TCP | AX_TXCOE_UDP | \
+                                        AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6)
+#else
+#define AX_TXCOE_DEF_CSUM              (AX_TXCOE_TCP | AX_TXCOE_UDP)
+#endif
+
+#define AX_TXCOE_64TE                  0x0001
+#define AX_TXCOE_PPPE                  0x0002
+
+#define AX88772B_MAX_BULKIN_2K         0
+#define AX88772B_MAX_BULKIN_4K         1
+#define AX88772B_MAX_BULKIN_6K         2
+#define AX88772B_MAX_BULKIN_8K         3
+#define AX88772B_MAX_BULKIN_16K                4
+#define AX88772B_MAX_BULKIN_20K                5
+#define AX88772B_MAX_BULKIN_24K                6
+#define AX88772B_MAX_BULKIN_32K                7
+struct {unsigned short size, byte_cnt,threshold;} AX88772B_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},
+};
+
+
+#define AX_RX_CTL_RH1M                 0x0100          /* Enable RX-Header mode 0 */
+#define AX_RX_CTL_RH2M                 0x0200          /* Enable IP header in receive buffer aligned on 32-bit aligment */
+#define AX_RX_CTL_RH3M                 0x0400          /* checksum value in rx header 3 */
+#define AX_RX_HEADER_DEFAULT           (AX_RX_CTL_RH1M | AX_RX_CTL_RH2M)
+
+#define AX_RX_CTL_MFB                  0x0300          /* Maximum Frame size 16384bytes */
+#define AX_RX_CTL_START                        0x0080          /* Ethernet MAC start */
+#define AX_RX_CTL_AP                   0x0020          /* Accept physcial address from Multicast array */
+#define AX_RX_CTL_AM                   0x0010  
+#define AX_RX_CTL_AB                   0x0008          /* Accetp Brocadcast frames*/
+#define AX_RX_CTL_SEP                  0x0004          /* Save error packets */        
+#define AX_RX_CTL_AMALL                        0x0002          /* Accetp all multicast frames */
+#define AX_RX_CTL_PRO                  0x0001          /* Promiscuous Mode */
+#define AX_RX_CTL_STOP                 0x0000          /* Stop MAC */
+
+#define AX_MONITOR_MODE                0x01
+#define AX_MONITOR_LINK                0x02
+#define AX_MONITOR_MAGIC               0x04
+#define AX_MONITOR_HSFS                0x10
+
+#define AX_MCAST_FILTER_SIZE           8
+#define AX_MAX_MCAST                   64
+#define AX_INTERRUPT_BUFSIZE           8
+
+#define AX_EEPROM_LEN                  0x40
+#define AX_EEPROM_MAGIC                0xdeadbeef
+#define EEPROMMASK                     0x7f
+
+/* GPIO REGISTER */
+#define AXGPIOS_GPO0EN                 0X01 // 1 << 0
+#define AXGPIOS_GPO0                   0X02 // 1 << 1
+#define AXGPIOS_GPO1EN                 0X04 // 1 << 2
+#define AXGPIOS_GPO1                   0X08 // 1 << 3
+#define AXGPIOS_GPO2EN                 0X10 // 1 << 4
+#define AXGPIOS_GPO2                   0X20 // 1 << 5
+#define AXGPIOS_RSE                    0X80 // 1 << 7
+
+/* TX-header format */
+#define AX_TX_HDR_CPHI                 0x4000
+#define AX_TX_HDR_DICF                 0x8000
+
+// GMII register definitions
+#define GMII_PHY_CONTROL               0x00    // control reg
+#define GMII_PHY_STATUS                        0x01    // status reg
+#define GMII_PHY_OUI                   0x02    // most of the OUI bits
+#define GMII_PHY_MODEL                 0x03    // model/rev bits, and rest of OUI
+#define GMII_PHY_ANAR                  0x04    // AN advertisement reg
+#define GMII_PHY_ANLPAR                        0x05    // AN Link Partner
+#define GMII_PHY_ANER                  0x06    // AN expansion reg
+#define GMII_PHY_1000BT_CONTROL                0x09    // control reg for 1000BT
+#define GMII_PHY_1000BT_STATUS         0x0A    // status reg for 1000BT
+
+// Bit definitions: GMII Control
+#define GMII_CONTROL_RESET             0x8000  // reset bit in control reg
+#define GMII_CONTROL_LOOPBACK          0x4000  // loopback bit in control reg
+#define GMII_CONTROL_10MB              0x0000  // 10 Mbit
+#define GMII_CONTROL_100MB             0x2000  // 100Mbit
+#define GMII_CONTROL_1000MB            0x0040  // 1000Mbit
+#define GMII_CONTROL_SPEED_BITS                0x2040  // speed bit mask
+#define GMII_CONTROL_ENABLE_AUTO       0x1000  // autonegotiate enable
+#define GMII_CONTROL_POWER_DOWN                0x0800
+#define GMII_CONTROL_ISOLATE           0x0400  // islolate bit
+#define GMII_CONTROL_START_AUTO                0x0200  // restart autonegotiate
+#define GMII_CONTROL_FULL_DUPLEX       0x0100
+
+// Bit definitions: GMII Status
+#define GMII_STATUS_100MB_MASK         0xE000  // any of these indicate 100 Mbit
+#define GMII_STATUS_10MB_MASK          0x1800  // either of these indicate 10 Mbit
+#define GMII_STATUS_AUTO_DONE          0x0020  // auto negotiation complete
+#define GMII_STATUS_AUTO               0x0008  // auto negotiation is available
+#define GMII_STATUS_LINK_UP            0x0004  // link status bit
+#define GMII_STATUS_EXTENDED           0x0001  // extended regs exist
+#define GMII_STATUS_100T4              0x8000  // capable of 100BT4
+#define GMII_STATUS_100TXFD            0x4000  // capable of 100BTX full duplex
+#define GMII_STATUS_100TX              0x2000  // capable of 100BTX
+#define GMII_STATUS_10TFD              0x1000  // capable of 10BT full duplex
+#define GMII_STATUS_10T                        0x0800  // capable of 10BT
+
+// Bit definitions: Auto-Negotiation Advertisement
+#define GMII_ANAR_ASYM_PAUSE           0x0800  // support asymetric pause
+#define GMII_ANAR_PAUSE                        0x0400  // support pause packets
+#define GMII_ANAR_100T4                        0x0200  // support 100BT4
+#define GMII_ANAR_100TXFD              0x0100  // support 100BTX full duplex
+#define GMII_ANAR_100TX                        0x0080  // support 100BTX half duplex
+#define GMII_ANAR_10TFD                        0x0040  // support 10BT full duplex
+#define GMII_ANAR_10T                  0x0020  // support 10BT half duplex
+#define GMII_SELECTOR_FIELD            0x001F  // selector field.
+
+// Bit definitions: Auto-Negotiation Link Partner Ability
+#define GMII_ANLPAR_100T4              0x0200  // support 100BT4
+#define GMII_ANLPAR_100TXFD            0x0100  // support 100BTX full duplex
+#define GMII_ANLPAR_100TX              0x0080  // support 100BTX half duplex
+#define GMII_ANLPAR_10TFD              0x0040  // support 10BT full duplex
+#define GMII_ANLPAR_10T                        0x0020  // support 10BT half duplex
+#define GMII_ANLPAR_PAUSE              0x0400  // support pause packets
+#define GMII_ANLPAR_ASYM_PAUSE         0x0800  // support asymetric pause
+#define GMII_ANLPAR_ACK                        0x4000  // means LCB was successfully rx'd
+#define GMII_SELECTOR_8023             0x0001;
+
+// Bit definitions: 1000BaseT AUX Control
+#define GMII_1000_AUX_CTRL_MASTER_SLAVE                0x1000
+#define GMII_1000_AUX_CTRL_FD_CAPABLE          0x0200  // full duplex capable
+#define GMII_1000_AUX_CTRL_HD_CAPABLE          0x0100  // half duplex capable
+
+// Bit definitions: 1000BaseT AUX Status
+#define GMII_1000_AUX_STATUS_FD_CAPABLE                0x0800  // full duplex capable
+#define GMII_1000_AUX_STATUS_HD_CAPABLE        0x0400  // half duplex capable
+
+// Cicada MII Registers
+#define GMII_AUX_CTRL_STATUS                   0x1C
+#define GMII_AUX_ANEG_CPLT                     0x8000
+#define GMII_AUX_FDX                           0x0020
+#define GMII_AUX_SPEED_1000                    0x0010
+#define GMII_AUX_SPEED_100                     0x0008
+
+#ifndef ADVERTISE_PAUSE_CAP
+#define ADVERTISE_PAUSE_CAP                    0x0400
+#endif
+
+#ifndef MII_STAT1000
+#define MII_STAT1000                           0x000A
+#endif
+
+#ifndef LPA_1000FULL
+#define LPA_1000FULL                           0x0800
+#endif
+
+// medium mode register
+#define MEDIUM_GIGA_MODE                       0x0001
+#define MEDIUM_FULL_DUPLEX_MODE                        0x0002
+#define MEDIUM_TX_ABORT_MODE                   0x0004
+#define MEDIUM_ENABLE_125MHZ                   0x0008
+#define MEDIUM_ENABLE_RX_FLOWCTRL              0x0010
+#define MEDIUM_ENABLE_TX_FLOWCTRL              0x0020
+#define MEDIUM_ENABLE_JUMBO_FRAME              0x0040
+#define MEDIUM_CHECK_PAUSE_FRAME_MODE          0x0080
+#define MEDIUM_ENABLE_RECEIVE                  0x0100
+#define MEDIUM_MII_100M_MODE                   0x0200
+#define MEDIUM_ENABLE_JAM_PATTERN              0x0400
+#define MEDIUM_ENABLE_STOP_BACKPRESSURE                0x0800
+#define MEDIUM_ENABLE_SUPPER_MAC_SUPPORT       0x1000
+
+/* PHY mode */
+#define PHY_MODE_MARVELL               0
+#define PHY_MODE_CICADA_FAMILY         1
+#define PHY_MODE_CICADA_V1             1
+#define PHY_MODE_AGERE_FAMILY          2
+#define PHY_MODE_AGERE_V0              2
+#define PHY_MODE_CICADA_V2             5
+#define PHY_MODE_AGERE_V0_GMII         6
+#define PHY_MODE_CICADA_V2_ASIX                9
+#define PHY_MODE_VSC8601               10
+#define PHY_MODE_RTL8211CL             12
+#define PHY_MODE_RTL8211BN             13
+#define PHY_MODE_RTL8251CL             14
+#define PHY_MODE_ATTANSIC_V0           0x40
+#define PHY_MODE_ATTANSIC_FAMILY       0x40
+#define PHY_MODE_MAC_TO_MAC_GMII       0x7C
+
+/*  */
+#define LED_MODE_MARVELL               0
+#define LED_MODE_CAMEO                 1
+
+#define MARVELL_LED_CTRL               0x18
+#define MARVELL_MANUAL_LED             0x19
+
+#define PHY_IDENTIFIER                 0x0002
+#define PHY_AGERE_IDENTIFIER           0x0282
+#define PHY_CICADA_IDENTIFIER          0x000f
+#define PHY_MARVELL_IDENTIFIER         0x0141
+
+#define PHY_MARVELL_STATUS             0x001b
+#define MARVELL_STATUS_HWCFG           0x0004          /* SGMII without clock */
+
+#define PHY_MARVELL_CTRL               0x0014
+#define MARVELL_CTRL_RXDELAY           0x0080
+#define MARVELL_CTRL_TXDELAY           0x0002
+
+#define PHY_CICADA_EXTPAGE             0x001f
+#define CICADA_EXTPAGE_EN              0x0001
+#define CICADA_EXTPAGE_DIS             0x0000
+
+
+struct {unsigned short value, offset; } CICADA_FAMILY_HWINIT[] =
+{
+       {0x0001, 0x001f}, {0x1c25, 0x0017}, {0x2a30, 0x001f}, {0x234c, 0x0010},
+       {0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0xa7fa, 0x0000},
+       {0x0012, 0x0002}, {0x3002, 0x0001}, {0x87fa, 0x0000}, {0x52b5, 0x001f},
+       {0xafac, 0x0000}, {0x000d, 0x0002}, {0x001c, 0x0001}, {0x8fac, 0x0000},
+       {0x2a30, 0x001f}, {0x0012, 0x0008}, {0x2a30, 0x001f}, {0x0400, 0x0014},
+       {0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0xa760, 0x0000},
+       {0x0000, 0x0002}, {0xfaff, 0x0001}, {0x8760, 0x0000}, {0x52b5, 0x001f},
+       {0xa760, 0x0000}, {0x0000, 0x0002}, {0xfaff, 0x0001}, {0x8760, 0x0000},
+       {0x52b5, 0x001f}, {0xafae, 0x0000}, {0x0004, 0x0002}, {0x0671, 0x0001},
+       {0x8fae, 0x0000}, {0x2a30, 0x001f}, {0x0012, 0x0008}, {0x0000, 0x001f},
+};
+
+struct {unsigned short value, offset; } CICADA_V2_HWINIT[] =
+{
+       {0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0x000f, 0x0002},
+       {0x472a, 0x0001}, {0x8fa4, 0x0000}, {0x2a30, 0x001f}, {0x0212, 0x0008},
+       {0x0000, 0x001f},
+};
+
+struct {unsigned short value, offset; } CICADA_V2_ASIX_HWINIT[] =
+{
+       {0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0x0012, 0x0002},
+       {0x3002, 0x0001}, {0x87fa, 0x0000}, {0x52b5, 0x001f}, {0x000f, 0x0002},
+       {0x472a, 0x0001}, {0x8fa4, 0x0000}, {0x2a30, 0x001f}, {0x0212, 0x0008},
+       {0x0000, 0x001f},
+};
+
+struct {unsigned short value, offset; } AGERE_FAMILY_HWINIT[] =
+{
+       {0x0800, 0x0000}, {0x0007, 0x0012}, {0x8805, 0x0010}, {0xb03e, 0x0011},
+       {0x8808, 0x0010}, {0xe110, 0x0011}, {0x8806, 0x0010}, {0xb03e, 0x0011},
+       {0x8807, 0x0010}, {0xff00, 0x0011}, {0x880e, 0x0010}, {0xb4d3, 0x0011},
+       {0x880f, 0x0010}, {0xb4d3, 0x0011}, {0x8810, 0x0010}, {0xb4d3, 0x0011},
+       {0x8817, 0x0010}, {0x1c00, 0x0011}, {0x300d, 0x0010}, {0x0001, 0x0011},
+       {0x0002, 0x0012},
+};
+
+struct ax88178_data {
+       u16     EepromData;
+       u16     MediaLink;
+       int     UseGpio0;
+       int     UseRgmii;
+       u8      PhyMode;
+       u8      LedMode;
+       u8      BuffaloOld;
+};
+
+enum watchdog_state {
+       AX_NOP = 0,
+       CHK_LINK,                       /* Routine A */
+       CHK_CABLE_EXIST,                /* Called by A */
+       CHK_CABLE_EXIST_AGAIN,          /* Routine B */
+       PHY_POWER_UP,                   /* Called by B */
+       PHY_POWER_UP_BH,
+       PHY_POWER_DOWN,
+       CHK_CABLE_STATUS,               /* Routine C */
+       WAIT_AUTONEG_COMPLETE,
+       AX_SET_RX_CFG,
+};
+
+struct ax88772b_data {
+       struct usbnet *dev;
+       struct workqueue_struct *ax_work;
+       struct work_struct check_link;
+       unsigned long time_to_chk;
+       u16 psc;
+       u8 pw_enabled;
+       u8 Event;
+       u8 checksum;
+};
+
+struct ax88772a_data {
+       struct usbnet *dev;
+       struct workqueue_struct *ax_work;
+       struct work_struct check_link;
+       unsigned long autoneg_start;
+#define AX88772B_WATCHDOG      (6 * HZ)
+       u8 Event;
+       u8 TickToExpire;
+       u8 DlyIndex;
+       u8 DlySel;
+       u16 EepromData;
+};
+
+struct ax88772_data {
+       struct usbnet *dev;
+       struct workqueue_struct *ax_work;
+       struct work_struct check_link;
+       unsigned long autoneg_start;
+       u8 Event;
+       u8 TickToExpire;
+};
+
+#define AX_RX_CHECKSUM         1
+#define AX_TX_CHECKSUM         2
+
+/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
+struct ax8817x_data {
+       u8 multi_filter[AX_MCAST_FILTER_SIZE];
+       int (*resume) (struct usb_interface *intf);
+       int (*suspend) (struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,10)
+                                       pm_message_t message);
+#else
+                                       u32 message);
+#endif
+};
+
+struct ax88172_int_data {
+       u16 res1;
+#define AX_INT_PPLS_LINK               (1 << 0)
+#define AX_INT_SPLS_LINK               (1 << 1)
+#define AX_INT_CABOFF_UNPLUG           (1 << 7)
+       u8 link;
+       u16 res2;
+       u8 status;
+       u16 res3;
+} __attribute__ ((packed));
+
+#define AX_RXHDR_L4_ERR                (1 << 8)
+#define AX_RXHDR_L3_ERR                (1 << 9)
+
+#define AX_RXHDR_L4_TYPE_UDP           1
+#define AX_RXHDR_L4_TYPE_ICMP          2
+#define AX_RXHDR_L4_TYPE_IGMP          3
+#define AX_RXHDR_L4_TYPE_TCP           4
+#define AX_RXHDR_L4_TYPE_TCMPV6        5
+#define AX_RXHDR_L4_TYPE_MASK          7
+
+#define AX_RXHDR_L3_TYPE_IP            1
+#define AX_RXHDR_L3_TYPE_IPV6          2
+
+struct ax88772b_rx_header {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+       u16     len:11,
+               res1:1,
+               crc:1,
+               mii:1,
+               runt:1,
+               mc_bc:1;
+
+       u16     len_bar:11,
+               res2:5;
+
+       u8      vlan_ind:3,
+               vlan_tag_striped:1,
+               pri:3,
+               res3:1;
+
+       u8      l4_csum_err:1,
+               l3_csum_err:1,
+               l4_type:3,
+               l3_type:2,
+               ce:1;
+#elif defined (__BIG_ENDIAN_BITFIELD)
+       u16     mc_bc:1,
+               runt:1,
+               mii:1,
+               crc:1,
+               res1:1,
+               len:11;
+
+       u16     res2:5,
+               len_bar:11;
+
+       u8      res3:1,
+               pri:3,
+               vlan_tag_striped:1,
+               vlan_ind:3;
+
+       u8      ce:1,
+               l3_type:2,
+               l4_type:3,
+               l3_csum_err:1,
+               l4_csum_err:1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+} __attribute__ ((packed));
+
+#endif /* __LINUX_USBNET_ASIX_H */
+
diff --git a/drivers/net/usb/axusbnet.c b/drivers/net/usb/axusbnet.c
new file mode 100755 (executable)
index 0000000..bc930c0
--- /dev/null
@@ -0,0 +1,1380 @@
+/*
+ * USB Network driver infrastructure
+ * Copyright (C) 2000-2005 by David Brownell
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * This is a generic "USB networking" framework that works with several
+ * kinds of full and high speed networking devices:  host-to-host cables,
+ * smart usb peripherals, and actual Ethernet adapters.
+ *
+ * These devices usually differ in terms of control protocols (if they
+ * even have one!) and sometimes they define new framing to wrap or batch
+ * Ethernet packets.  Otherwise, they talk to USB pretty much the same,
+ * so interface (un)binding, endpoint I/O queues, fault handling, and other
+ * issues can usefully be addressed by this framework.
+ */
+
+#define        DEBUG                   // error path messages, extra info
+// #define     VERBOSE                 // more; success messages
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ctype.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+/*#include <linux/usb/usbnet.h>*/
+
+#include "asix.h"
+#include "axusbnet.h"
+
+#define DRIVER_VERSION         "22-Aug-2005"
+#define  TAG "AX88xx------>"
+
+static void axusbnet_unlink_rx_urbs(struct usbnet *);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Nineteen USB 1.1 max size bulk transactions per frame (ms), max.
+ * Several dozen bytes of IPv4 data can fit in two such transactions.
+ * One maximum size Ethernet packet takes twenty four of them.
+ * For high speed, each frame comfortably fits almost 36 max size
+ * Ethernet packets (so queues should be bigger).
+ *
+ * REVISIT qlens should be members of 'struct usbnet'; the goal is to
+ * let the USB host controller be busy for 5msec or more before an irq
+ * is required, under load.  Jumbograms change the equation.
+ */
+#define RX_MAX_QUEUE_MEMORY (60 * 1518)
+#define        RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+                       (RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4)
+#define        TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+                       (RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4)
+
+// reawaken network queue this soon after stopping; else watchdog barks
+//#define TX_TIMEOUT_JIFFIES   (5*HZ)
+#define TX_TIMEOUT_JIFFIES     (30*HZ)
+
+// throttle rx/tx briefly after some faults, so khubd might disconnect()
+// us (it polls at HZ/4 usually) before we report too many false errors.
+#define THROTTLE_JIFFIES       (HZ/8)
+
+// between wakeups
+#define UNLINK_TIMEOUT_MS      3
+
+/*-------------------------------------------------------------------------*/
+
+static const char driver_name [] = "axusbnet";
+
+/* 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");
+
+/*-------------------------------------------------------------------------*/
+
+/* handles CDC Ethernet and many other network "bulk data" interfaces */
+static
+int axusbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf)
+{
+       int                             tmp;
+       struct usb_host_interface       *alt = NULL;
+       struct usb_host_endpoint        *in = NULL, *out = NULL;
+       struct usb_host_endpoint        *status = NULL;
+
+       for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
+               unsigned        ep;
+
+               in = out = status = NULL;
+               alt = intf->altsetting + tmp;
+
+               /* take the first altsetting with in-bulk + out-bulk;
+                * remember any status endpoint, just in case;
+                * ignore other endpoints and altsetttings.
+                */
+               for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
+                       struct usb_host_endpoint        *e;
+                       int                             intr = 0;
+
+                       e = alt->endpoint + ep;
+                       switch (e->desc.bmAttributes) {
+                       case USB_ENDPOINT_XFER_INT:
+                               if (!(e->desc.bEndpointAddress & USB_DIR_IN))
+                                       continue;
+                               intr = 1;
+                               /* FALLTHROUGH */
+                       case USB_ENDPOINT_XFER_BULK:
+                               break;
+                       default:
+                               continue;
+                       }
+                       if (e->desc.bEndpointAddress & USB_DIR_IN) {
+                               if (!intr && !in)
+                                       in = e;
+                               else if (intr && !status)
+                                       status = e;
+                       } else {
+                               if (!out)
+                                       out = e;
+                       }
+               }
+               if (in && out)
+                       break;
+       }
+       if (!alt || !in || !out)
+               return -EINVAL;
+
+       if (alt->desc.bAlternateSetting != 0
+                       || !(dev->driver_info->flags & FLAG_NO_SETINT)) {
+               tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
+                               alt->desc.bAlternateSetting);
+               if (tmp < 0)
+                       return tmp;
+       }
+
+       dev->in = usb_rcvbulkpipe (dev->udev,
+                       in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+       dev->out = usb_sndbulkpipe (dev->udev,
+                       out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+       dev->status = status;
+       return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static void intr_complete (struct urb *urb, struct pt_regs *regs);
+#else
+static void intr_complete (struct urb *urb);
+#endif
+
+static int init_status (struct usbnet *dev, struct usb_interface *intf)
+{
+       char            *buf = NULL;
+       unsigned        pipe = 0;
+       unsigned        maxp;
+       unsigned        period;
+
+       if (!dev->driver_info->status)
+               return 0;
+
+       pipe = usb_rcvintpipe (dev->udev,
+                       dev->status->desc.bEndpointAddress
+                               & USB_ENDPOINT_NUMBER_MASK);
+       maxp = usb_maxpacket (dev->udev, pipe, 0);
+
+       /* avoid 1 msec chatter:  min 8 msec poll rate */
+       period = max ((int) dev->status->desc.bInterval,
+               (dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);
+
+       buf = kmalloc (maxp, GFP_KERNEL);
+       if (buf) {
+               dev->interrupt = usb_alloc_urb (0, GFP_KERNEL);
+               if (!dev->interrupt) {
+                       kfree (buf);
+                       return -ENOMEM;
+               } else {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+                       dev->interrupt->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+                       usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
+                               buf, maxp, intr_complete, dev, period);
+                       devdbg(dev,
+                               "status ep%din, %d bytes period %d",
+                               usb_pipeendpoint(pipe), maxp, period);
+               }
+       }
+       return 0;
+}
+
+/* Passes this packet up the stack, updating its accounting.
+ * Some link protocols batch packets, so their rx_fixup paths
+ * can return clones as well as just modify the original skb.
+ */
+static
+void axusbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
+{
+       int     status;
+
+       skb->dev = dev->net;
+       skb->protocol = eth_type_trans (skb, dev->net);
+       dev->stats.rx_packets++;
+       dev->stats.rx_bytes += skb->len;
+
+       if (netif_msg_rx_status (dev))
+               devdbg (dev, "< rx, len %zu, type 0x%x",
+                       skb->len + sizeof (struct ethhdr), skb->protocol);
+       memset (skb->cb, 0, sizeof (struct skb_data));
+       status = netif_rx (skb);
+       if (status != NET_RX_SUCCESS && netif_msg_rx_err (dev))
+               devdbg (dev, "netif_rx status %d", status);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * Network Device Driver (peer link to "Host Device", from USB host)
+ *
+ *-------------------------------------------------------------------------*/
+
+static
+int axusbnet_change_mtu (struct net_device *net, int new_mtu)
+{
+       struct usbnet   *dev = netdev_priv(net);
+       int             ll_mtu = new_mtu + net->hard_header_len;
+       int             old_hard_mtu = dev->hard_mtu;
+       int             old_rx_urb_size = dev->rx_urb_size;
+
+       if (new_mtu <= 0)
+               return -EINVAL;
+       // no second zero-length packet read wanted after mtu-sized packets
+       if ((ll_mtu % dev->maxpacket) == 0)
+               return -EDOM;
+       net->mtu = new_mtu;
+
+       dev->hard_mtu = net->mtu + net->hard_header_len;
+       if (dev->rx_urb_size == old_hard_mtu) {
+               dev->rx_urb_size = dev->hard_mtu;
+               if (dev->rx_urb_size > old_rx_urb_size)
+                       axusbnet_unlink_rx_urbs(dev);
+       }
+
+       return 0;
+}
+
+static struct net_device_stats *axusbnet_get_stats (struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv (net);
+       return &dev->stats;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from
+ * completion callbacks.  2.5 should have fixed those bugs...
+ */
+
+static void
+defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list)
+{
+       unsigned long           flags;
+
+       spin_lock_irqsave(&list->lock, flags);
+       __skb_unlink(skb, list);
+       spin_unlock(&list->lock);
+       spin_lock(&dev->done.lock);
+       __skb_queue_tail(&dev->done, skb);
+       if (dev->done.qlen == 1)
+               tasklet_schedule(&dev->bh);
+       spin_unlock_irqrestore(&dev->done.lock, flags);
+}
+
+/* some work can't be done in tasklets, so we use keventd
+ *
+ * NOTE:  annoying asymmetry:  if it's active, schedule_work() fails,
+ * but tasklet_schedule() doesn't.  hope the failure is rare.
+ */
+static
+void axusbnet_defer_kevent (struct usbnet *dev, int work)
+{
+       set_bit (work, &dev->flags);
+       if (!schedule_work (&dev->kevent))
+               deverr (dev, "kevent %d may have been dropped", work);
+       else
+               devdbg (dev, "kevent %d scheduled", work);
+}
+
+/*-------------------------------------------------------------------------*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static void rx_complete (struct urb *urb, struct pt_regs *regs);
+#else
+static void rx_complete (struct urb *urb);
+#endif
+
+static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
+{
+       struct sk_buff          *skb;
+       struct skb_data         *entry;
+       int                     retval = 0;
+       unsigned long           lockflags;
+       size_t                  size = dev->rx_urb_size;
+       struct driver_info      *info = dev->driver_info;
+       u8                      align;
+
+#if (AX_FORCE_BUFF_ALIGN)
+       align = 0;
+#else
+       if (!(info->flags & FLAG_HW_IP_ALIGNMENT))
+               align = NET_IP_ALIGN;
+       else
+               align = 0;
+#endif
+
+       if ((skb = alloc_skb (size + align, flags)) == NULL) {
+               if (netif_msg_rx_err (dev))
+                       devdbg (dev, "no rx skb");
+               axusbnet_defer_kevent (dev, EVENT_RX_MEMORY);
+               usb_free_urb (urb);
+               return;
+       }
+
+       if (align)
+               skb_reserve (skb, NET_IP_ALIGN);
+
+       entry = (struct skb_data *) skb->cb;
+       entry->urb = urb;
+       entry->dev = dev;
+       entry->state = rx_start;
+       entry->length = 0;
+
+       usb_fill_bulk_urb (urb, dev->udev, dev->in,
+               skb->data, size, rx_complete, skb);
+
+       spin_lock_irqsave (&dev->rxq.lock, lockflags);
+
+       if (netif_running (dev->net)
+                       && netif_device_present (dev->net)
+                       && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+               switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) {
+               case -EPIPE:
+                       axusbnet_defer_kevent (dev, EVENT_RX_HALT);
+                       break;
+               case -ENOMEM:
+                       axusbnet_defer_kevent (dev, EVENT_RX_MEMORY);
+                       break;
+               case -ENODEV:
+                       if (netif_msg_ifdown (dev))
+                               devdbg (dev, "device gone");
+                       netif_device_detach (dev->net);
+                       break;
+               default:
+                       if (netif_msg_rx_err (dev))
+                               devdbg (dev, "rx submit, %d", retval);
+                       tasklet_schedule (&dev->bh);
+                       break;
+               case 0:
+                       __skb_queue_tail (&dev->rxq, skb);
+               }
+       } else {
+               if (netif_msg_ifdown (dev))
+                       devdbg (dev, "rx: stopped");
+               retval = -ENOLINK;
+       }
+       spin_unlock_irqrestore (&dev->rxq.lock, lockflags);
+       if (retval) {
+               dev_kfree_skb_any (skb);
+               usb_free_urb (urb);
+       }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
+{
+       if (dev->driver_info->rx_fixup
+                       && !dev->driver_info->rx_fixup (dev, skb))
+               goto error;
+       // else network stack removes extra byte if we forced a short packet
+
+       if (skb->len)
+               axusbnet_skb_return (dev, skb);
+       else {
+               if (netif_msg_rx_err (dev))
+                       devdbg (dev, "drop");
+error:
+               dev->stats.rx_errors++;
+               skb_queue_tail (&dev->done, skb);
+       }
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static void rx_complete (struct urb *urb, struct pt_regs *regs)
+#else
+static void rx_complete (struct urb *urb)
+#endif
+{
+       struct sk_buff          *skb = (struct sk_buff *) urb->context;
+       struct skb_data         *entry = (struct skb_data *) skb->cb;
+       struct usbnet           *dev = entry->dev;
+       int                     urb_status = urb->status;
+
+       skb_put (skb, urb->actual_length);
+       entry->state = rx_done;
+       entry->urb = NULL;
+
+       switch (urb_status) {
+       /* success */
+       case 0:
+               if (skb->len < dev->net->hard_header_len) {
+                       entry->state = rx_cleanup;
+                       dev->stats.rx_errors++;
+                       dev->stats.rx_length_errors++;
+                       if (netif_msg_rx_err (dev))
+                               devdbg (dev, "rx length %d", skb->len);
+               }
+               break;
+
+       /* stalls need manual reset. this is rare ... except that
+        * when going through USB 2.0 TTs, unplug appears this way.
+        * we avoid the highspeed version of the ETIMEDOUT/EILSEQ
+        * storm, recovering as needed.
+        */
+       case -EPIPE:
+               dev->stats.rx_errors++;
+               axusbnet_defer_kevent (dev, EVENT_RX_HALT);
+               // FALLTHROUGH
+
+       /* software-driven interface shutdown */
+       case -ECONNRESET:               /* async unlink */
+       case -ESHUTDOWN:                /* hardware gone */
+               if (netif_msg_ifdown (dev))
+                       devdbg (dev, "rx shutdown, code %d", urb_status);
+               goto block;
+
+       /* we get controller i/o faults during khubd disconnect() delays.
+        * throttle down resubmits, to avoid log floods; just temporarily,
+        * so we still recover when the fault isn't a khubd delay.
+        */
+       case -EPROTO:
+       case -ETIME:
+       case -EILSEQ:
+               dev->stats.rx_errors++;
+               if (!timer_pending (&dev->delay)) {
+                       mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
+                       if (netif_msg_link (dev))
+                               devdbg (dev, "rx throttle %d", urb_status);
+               }
+block:
+               entry->state = rx_cleanup;
+               entry->urb = urb;
+               urb = NULL;
+               break;
+
+       /* data overrun ... flush fifo? */
+       case -EOVERFLOW:
+               dev->stats.rx_over_errors++;
+               // FALLTHROUGH
+
+       default:
+               entry->state = rx_cleanup;
+               dev->stats.rx_errors++;
+               if (netif_msg_rx_err (dev))
+                       devdbg (dev, "rx status %d", urb_status);
+               break;
+       }
+
+       defer_bh(dev, skb, &dev->rxq);
+
+       if (urb) {
+               if (netif_running (dev->net)
+                               && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+                       rx_submit (dev, urb, GFP_ATOMIC);
+                       return;
+               }
+               usb_free_urb (urb);
+       }
+       if (netif_msg_rx_err (dev))
+               devdbg (dev, "no read resubmitted");
+}
+extern void dwc_otg_clear_halt(struct urb *_urb);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static void intr_complete (struct urb *urb, struct pt_regs *regs)
+#else
+static void intr_complete (struct urb *urb)
+#endif
+{
+       struct usbnet   *dev = urb->context;
+       int             status = urb->status;
+       //devdbg(dev, "status %d running? %d\n", status,netif_running (dev->net));
+
+       dev->intr_complete = 1;
+       wake_up_interruptible(&dev->intr_wait);
+
+       switch (status) {
+       /* success */
+       case 0:
+               dev->driver_info->status(dev, urb);
+               break;
+
+       /* software-driven interface shutdown */
+       case -ENOENT:           /* urb killed */
+       case -ESHUTDOWN:        /* hardware gone */
+               if (netif_msg_ifdown (dev))
+                       devdbg (dev, "intr shutdown, code %d", status);
+               dwc_otg_clear_halt(urb);
+               break;
+               
+       //      return;
+
+       /* NOTE:  not throttling like RX/TX, since this endpoint
+        * already polls infrequently
+        */
+       default:
+               devdbg (dev, "intr status %d", status);
+               if(status < 0)
+                       dwc_otg_clear_halt(urb);
+               break;
+       }
+
+       if (!netif_running (dev->net) || dev->asix_suspend) {
+               dev->asix_suspend = 0;
+               devdbg (dev, "netif_running is false");
+               return;
+       }
+
+       dev->intr_complete = 0;
+       memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
+       status = usb_submit_urb (urb, GFP_ATOMIC);
+       if (status != 0 && netif_msg_timer (dev))
+               deverr(dev, "intr resubmit --> %d", status);
+}
+
+/*-------------------------------------------------------------------------*/
+
+// unlink pending rx/tx; completion handlers do all other cleanup
+
+static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
+{
+       unsigned long           flags;
+       struct sk_buff          *skb, *skbnext;
+       int                     count = 0;
+
+       spin_lock_irqsave (&q->lock, flags);
+       skb_queue_walk_safe(q, skb, skbnext) {
+               struct skb_data         *entry;
+               struct urb              *urb;
+               int                     retval;
+
+               entry = (struct skb_data *) skb->cb;
+               urb = entry->urb;
+
+               // during some PM-driven resume scenarios,
+               // these (async) unlinks complete immediately
+               retval = usb_unlink_urb (urb);
+               if (retval != -EINPROGRESS && retval != 0)
+                       devdbg (dev, "unlink urb err, %d", retval);
+               else
+                       count++;
+       }
+       spin_unlock_irqrestore (&q->lock, flags);
+       return count;
+}
+
+// Flush all pending rx urbs
+// minidrivers may need to do this when the MTU changes
+
+static
+void axusbnet_unlink_rx_urbs(struct usbnet *dev)
+{
+       if (netif_running(dev->net)) {
+               (void) unlink_urbs (dev, &dev->rxq);
+               tasklet_schedule(&dev->bh);
+       }
+}
+
+/*-------------------------------------------------------------------------*/
+
+// precondition: never called in_interrupt
+
+static
+int axusbnet_stop (struct net_device *net)
+{
+       struct usbnet           *dev = netdev_priv(net);
+       struct driver_info      *info = dev->driver_info;
+       int                     temp;
+       int                     retval;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
+       DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup);
+#else
+       DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup);
+#endif
+       DECLARE_WAITQUEUE (wait, current);
+
+       devdbg(dev," %s %d\n",__FUNCTION__,__LINE__);
+       netif_stop_queue (net);
+
+       if (netif_msg_ifdown (dev))
+               devinfo (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld",
+                       dev->stats.rx_packets, dev->stats.tx_packets,
+                       dev->stats.rx_errors, dev->stats.tx_errors
+                       );
+
+       /* allow minidriver to stop correctly (wireless devices to turn off
+        * radio etc) */
+       if (info->stop) {
+               retval = info->stop(dev);
+               if (retval < 0 && netif_msg_ifdown(dev))
+                       devinfo(dev,
+                               "stop fail (%d) usbnet usb-%s-%s, %s",
+                               retval,
+                               dev->udev->bus->bus_name, dev->udev->devpath,
+                               info->description);
+       }
+
+       if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) {
+               /* ensure there are no more active urbs */
+               add_wait_queue(&unlink_wakeup, &wait);
+               dev->wait = &unlink_wakeup;
+               temp = unlink_urbs(dev, &dev->txq) +
+                       unlink_urbs(dev, &dev->rxq);
+
+               /* maybe wait for deletions to finish. */
+               while (!skb_queue_empty(&dev->rxq)
+                               && !skb_queue_empty(&dev->txq)
+                               && !skb_queue_empty(&dev->done)) {
+                       msleep(UNLINK_TIMEOUT_MS);
+                       if (netif_msg_ifdown(dev))
+                               devdbg(dev, "waited for %d urb completions",
+                                       temp);
+               }
+               dev->wait = NULL;
+               remove_wait_queue(&unlink_wakeup, &wait);
+       }
+
+       usb_kill_urb(dev->interrupt);
+
+       /* deferred work (task, timer, softirq) must also stop.
+        * can't flush_scheduled_work() until we drop rtnl (later),
+        * else workers could deadlock; so make workers a NOP.
+        */
+       dev->flags = 0;
+       del_timer_sync (&dev->delay);
+       tasklet_kill (&dev->bh);
+
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// posts reads, and enables write queuing
+
+// precondition: never called in_interrupt
+
+static
+int axusbnet_open (struct net_device *net)
+{
+       struct usbnet           *dev = netdev_priv(net);
+       int                     retval = 0;
+       struct driver_info      *info = dev->driver_info;
+       devdbg(dev, " %s %d\n",__FUNCTION__,__LINE__);
+       // put into "known safe" state
+       if (info->reset && (retval = info->reset (dev)) < 0) {
+               if (netif_msg_ifup (dev))
+                       devinfo (dev,
+                               "open reset fail (%d) usbnet usb-%s-%s, %s",
+                               retval,
+                               dev->udev->bus->bus_name, dev->udev->devpath,
+                       info->description);
+               goto done;
+       }
+
+       // insist peer be connected
+       if (info->check_connect && (retval = info->check_connect (dev)) < 0) {
+               if (netif_msg_ifup (dev))
+                       devdbg (dev, "can't open; %d", retval);
+               goto done;
+       }
+
+       /* start any status interrupt transfer */
+       if (dev->interrupt) {
+               retval = usb_submit_urb (dev->interrupt, GFP_KERNEL);
+               if (retval < 0) {
+                       if (netif_msg_ifup (dev))
+                               deverr (dev, "intr submit %d", retval);
+                       goto done;
+               }
+       }
+
+       netif_start_queue (net);
+       if (netif_msg_ifup (dev)) {
+               char    *framing;
+
+               if (dev->driver_info->flags & FLAG_FRAMING_NC)
+                       framing = "NetChip";
+               else if (dev->driver_info->flags & FLAG_FRAMING_GL)
+                       framing = "GeneSys";
+               else if (dev->driver_info->flags & FLAG_FRAMING_Z)
+                       framing = "Zaurus";
+               else if (dev->driver_info->flags & FLAG_FRAMING_RN)
+                       framing = "RNDIS";
+               else if (dev->driver_info->flags & FLAG_FRAMING_AX)
+                       framing = "ASIX";
+               else
+                       framing = "simple";
+
+               devinfo (dev, "open: enable queueing "
+                               "(rx %d, tx %d) mtu %d %s framing",
+                       (int)RX_QLEN (dev), (int)TX_QLEN (dev), dev->net->mtu,
+                       framing);
+       }
+
+       // delay posting reads until we're fully open
+       tasklet_schedule (&dev->bh);
+       return retval;
+done:
+       return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ethtool methods; minidrivers may need to add some more, but
+ * they'll probably want to use this base set.
+ */
+
+static
+int axusbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       if (!dev->mii.mdio_read)
+               return -EOPNOTSUPP;
+
+       return mii_ethtool_gset(&dev->mii, cmd);
+}
+
+static
+int axusbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd)
+{
+       struct usbnet *dev = netdev_priv(net);
+       int retval;
+
+       if (!dev->mii.mdio_write)
+               return -EOPNOTSUPP;
+
+       retval = mii_ethtool_sset(&dev->mii, cmd);
+
+       /* link speed/duplex might have changed */
+       if (dev->driver_info->link_reset)
+               dev->driver_info->link_reset(dev);
+
+       return retval;
+
+}
+
+static
+u32 axusbnet_get_link (struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       /* If a check_connect is defined, return its result */
+       if (dev->driver_info->check_connect)
+               return dev->driver_info->check_connect (dev) == 0;
+
+       /* if the device has mii operations, use those */
+       if (dev->mii.mdio_read)
+               return mii_link_ok(&dev->mii);
+
+       /* Otherwise, dtrt for drivers calling netif_carrier_{on,off} */
+       return ethtool_op_get_link(net);
+}
+
+static
+int axusbnet_nway_reset(struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       if (!dev->mii.mdio_write)
+               return -EOPNOTSUPP;
+
+       return mii_nway_restart(&dev->mii);
+}
+
+static
+void axusbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       strncpy (info->driver, dev->driver_name, sizeof info->driver);
+       strncpy (info->version, DRIVER_VERSION, sizeof info->version);
+       strncpy (info->fw_version, dev->driver_info->description,
+               sizeof info->fw_version);
+       usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info);
+}
+
+static
+u32 axusbnet_get_msglevel (struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       return dev->msg_enable;
+}
+
+static
+void axusbnet_set_msglevel (struct net_device *net, u32 level)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       dev->msg_enable = level;
+}
+
+/* drivers may override default ethtool_ops in their bind() routine */
+static struct ethtool_ops axusbnet_ethtool_ops = {
+       .get_settings           = axusbnet_get_settings,
+       .set_settings           = axusbnet_set_settings,
+       .get_link               = axusbnet_get_link,
+       .nway_reset             = axusbnet_nway_reset,
+       .get_drvinfo            = axusbnet_get_drvinfo,
+       .get_msglevel           = axusbnet_get_msglevel,
+       .set_msglevel           = axusbnet_set_msglevel,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* work that cannot be done in interrupt context uses keventd.
+ *
+ * NOTE:  with 2.5 we could do more of this using completion callbacks,
+ * especially now that control transfers can be queued.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static void kevent (void *data)
+{
+       struct usbnet *dev = (struct usbnet *)data;
+#else
+static void kevent (struct work_struct *work)
+{
+       struct usbnet           *dev =
+               container_of(work, struct usbnet, kevent);
+#endif
+       int                     status;
+
+       /* usb_clear_halt() needs a thread context */
+       if (test_bit (EVENT_TX_HALT, &dev->flags)) {
+printk ("EVENT_TX_HALT\n");
+               unlink_urbs (dev, &dev->txq);
+               status = usb_clear_halt (dev->udev, dev->out);
+               if (status < 0
+                               && status != -EPIPE
+                               && status != -ESHUTDOWN) {
+                       if (netif_msg_tx_err (dev))
+                               deverr (dev, "can't clear tx halt, status %d",
+                                       status);
+               } else {
+                       clear_bit (EVENT_TX_HALT, &dev->flags);
+                       if (status != -ESHUTDOWN)
+                               netif_wake_queue (dev->net);
+               }
+               
+               //dwc_otg_clear_halt(urb);
+       }
+       if (test_bit (EVENT_RX_HALT, &dev->flags)) {
+printk ("EVENT_RX_HALT\n");
+               unlink_urbs (dev, &dev->rxq);
+               status = usb_clear_halt (dev->udev, dev->in);
+               if (status < 0
+                               && status != -EPIPE
+                               && status != -ESHUTDOWN) {
+                       if (netif_msg_rx_err (dev))
+                               deverr (dev, "can't clear rx halt, status %d",
+                                       status);
+               } else {
+                       clear_bit (EVENT_RX_HALT, &dev->flags);
+                       tasklet_schedule (&dev->bh);
+               }
+       }
+
+       /* tasklet could resubmit itself forever if memory is tight */
+       if (test_bit (EVENT_RX_MEMORY, &dev->flags)) {
+               struct urb      *urb = NULL;
+printk ("EVENT_RX_MEMORY\n");
+               if (netif_running (dev->net))
+                       urb = usb_alloc_urb (0, GFP_KERNEL);
+               else
+                       clear_bit (EVENT_RX_MEMORY, &dev->flags);
+               if (urb != NULL) {
+                       clear_bit (EVENT_RX_MEMORY, &dev->flags);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+                       urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+                       rx_submit (dev, urb, GFP_KERNEL);
+                       tasklet_schedule (&dev->bh);
+               }
+       }
+
+       if (test_bit (EVENT_LINK_RESET, &dev->flags)) {
+               struct driver_info      *info = dev->driver_info;
+               int                     retval = 0;
+
+               clear_bit (EVENT_LINK_RESET, &dev->flags);
+               if(info->link_reset && (retval = info->link_reset(dev)) < 0) {
+                       devinfo(dev, "link reset failed (%d) usbnet usb-%s-%s, %s",
+                               retval,
+                               dev->udev->bus->bus_name, dev->udev->devpath,
+                               info->description);
+               }
+       }
+
+       if (dev->flags)
+               devdbg (dev, "kevent done, flags = 0x%lx",
+                       dev->flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+static void tx_complete (struct urb *urb, struct pt_regs *regs)
+#else
+static void tx_complete (struct urb *urb)
+#endif
+{
+       struct sk_buff          *skb = (struct sk_buff *) urb->context;
+       struct skb_data         *entry = (struct skb_data *) skb->cb;
+       struct usbnet           *dev = entry->dev;
+
+       if (urb->status == 0) {
+               dev->stats.tx_packets++;
+               dev->stats.tx_bytes += entry->length;
+       } else {
+               dev->stats.tx_errors++;
+
+               switch (urb->status) {
+               case -EPIPE:
+                       axusbnet_defer_kevent (dev, EVENT_TX_HALT);
+                       break;
+
+               /* software-driven interface shutdown */
+               case -ECONNRESET:               // async unlink
+               case -ESHUTDOWN:                // hardware gone
+                       break;
+
+               // like rx, tx gets controller i/o faults during khubd delays
+               // and so it uses the same throttling mechanism.
+               case -EPROTO:
+               case -ETIME:
+               case -EILSEQ:
+                       if (!timer_pending (&dev->delay)) {
+                               mod_timer (&dev->delay,
+                                       jiffies + THROTTLE_JIFFIES);
+                               if (netif_msg_link (dev))
+                                       devdbg (dev, "tx throttle %d",
+                                                       urb->status);
+                       }
+                       netif_stop_queue (dev->net);
+                       break;
+               default:
+                       if (netif_msg_tx_err (dev))
+                               devdbg (dev, "tx err %d", entry->urb->status);
+                       break;
+               }
+       }
+
+       urb->dev = NULL;
+       entry->state = tx_done;
+       defer_bh(dev, skb, &dev->txq);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static
+void axusbnet_tx_timeout (struct net_device *net)
+{
+       struct usbnet           *dev = netdev_priv(net);
+
+       devdbg(dev,"---> %s %d\n",__FUNCTION__, __LINE__);
+       unlink_urbs (dev, &dev->txq);
+       tasklet_schedule (&dev->bh);
+
+       // FIXME: device recovery -- reset?
+}
+
+/*-------------------------------------------------------------------------*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+static int
+#else
+static netdev_tx_t
+#endif
+axusbnet_start_xmit (struct sk_buff *skb,
+                                    struct net_device *net)
+{
+       struct usbnet           *dev = netdev_priv(net);
+       int                     length;
+       struct urb              *urb = NULL;
+       struct skb_data         *entry;
+       struct driver_info      *info = dev->driver_info;
+       unsigned long           flags;
+       int retval;
+
+       // some devices want funky USB-level framing, for
+       // win32 driver (usually) and/or hardware quirks
+       if (info->tx_fixup) {
+               skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
+               if (!skb) {
+                       if (netif_msg_tx_err (dev))
+                               devdbg (dev, "can't tx_fixup skb");
+                       goto drop;
+               }
+       }
+       length = skb->len;
+
+       if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
+               if (netif_msg_tx_err (dev))
+                       devdbg (dev, "no urb");
+               goto drop;
+       }
+
+       entry = (struct skb_data *) skb->cb;
+       entry->urb = urb;
+       entry->dev = dev;
+       entry->state = tx_start;
+       entry->length = length;
+
+       usb_fill_bulk_urb (urb, dev->udev, dev->out,
+                       skb->data, skb->len, tx_complete, skb);
+
+       /* don't assume the hardware handles USB_ZERO_PACKET
+        * NOTE:  strictly conforming cdc-ether devices should expect
+        * the ZLP here, but ignore the one-byte packet.
+        */
+       if (!(info->flags & FLAG_SEND_ZLP) && (length % dev->maxpacket) == 0) {
+               urb->transfer_buffer_length++;
+               if (skb_tailroom(skb)) {
+                       skb->data[skb->len] = 0;
+                       __skb_put(skb, 1);
+               }
+       }
+
+       spin_lock_irqsave (&dev->txq.lock, flags);
+
+       switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
+       case -EPIPE:
+               netif_stop_queue (net);
+               axusbnet_defer_kevent (dev, EVENT_TX_HALT);
+               break;
+       default:
+               if (netif_msg_tx_err (dev))
+                       devdbg (dev, "tx: submit urb err %d", retval);
+               break;
+       case 0:
+               net->trans_start = jiffies;
+               __skb_queue_tail (&dev->txq, skb);
+               if (dev->txq.qlen >= TX_QLEN (dev))
+                       netif_stop_queue (net);
+       }
+       spin_unlock_irqrestore (&dev->txq.lock, flags);
+
+       if (retval) {
+               if (netif_msg_tx_err (dev))
+                       devdbg (dev, "drop, code %d", retval);
+drop:
+               dev->stats.tx_dropped++;
+               if (skb)
+                       dev_kfree_skb_any (skb);
+               usb_free_urb (urb);
+       } else if (netif_msg_tx_queued (dev)) {
+               devdbg (dev, "> tx, len %d, type 0x%x",
+                       length, skb->protocol);
+       }
+       return NETDEV_TX_OK;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// tasklet (work deferred from completions, in_irq) or timer
+
+static void axusbnet_bh (unsigned long param)
+{
+       struct usbnet           *dev = (struct usbnet *) param;
+       struct sk_buff          *skb;
+       struct skb_data         *entry;
+       while ((skb = skb_dequeue (&dev->done))) {
+               entry = (struct skb_data *) skb->cb;
+               switch (entry->state) {
+               case rx_done:
+                       entry->state = rx_cleanup;
+                       rx_process (dev, skb);
+                       continue;
+               case tx_done:
+               case rx_cleanup:
+                       usb_free_urb (entry->urb);
+                       dev_kfree_skb (skb);
+                       continue;
+               default:
+                       devdbg (dev, "bogus skb state %d", entry->state);
+               }
+       }
+
+       // waiting for all pending urbs to complete?
+       if (dev->wait) {
+               if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) {
+                       wake_up (dev->wait);
+               }
+
+       // or are we maybe short a few urbs?
+       } else if (netif_running (dev->net)
+                       && netif_device_present (dev->net)
+                       && !timer_pending (&dev->delay)
+                       && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+               int     temp = dev->rxq.qlen;
+               int     qlen = RX_QLEN (dev);
+
+               if (temp < qlen) {
+                       struct urb      *urb;
+                       int             i;
+
+                       // don't refill the queue all at once
+                       for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {
+                               urb = usb_alloc_urb (0, GFP_ATOMIC);
+                               if (urb != NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+                                       urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+                                       rx_submit (dev, urb, GFP_ATOMIC);
+                               }
+                       }
+                       if (temp != dev->rxq.qlen && netif_msg_link (dev))
+                               devdbg (dev, "rxqlen %d --> %d",
+                                               temp, dev->rxq.qlen);
+                       if (dev->rxq.qlen < qlen)
+                               tasklet_schedule (&dev->bh);
+               }
+               if (dev->txq.qlen < TX_QLEN (dev))
+                       netif_wake_queue (dev->net);
+       }
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * USB Device Driver support
+ *
+ *-------------------------------------------------------------------------*/
+
+// precondition: never called in_interrupt
+
+static
+void axusbnet_disconnect (struct usb_interface *intf)
+{
+       struct usbnet           *dev;
+       struct usb_device       *xdev;
+       struct net_device       *net;
+
+       dev = usb_get_intfdata(intf);
+       usb_set_intfdata(intf, NULL);
+       if (!dev)
+               return;
+
+       xdev = interface_to_usbdev (intf);
+
+       if (netif_msg_probe (dev))
+               devinfo (dev, "unregister '%s' usb-%s-%s, %s",
+                       intf->dev.driver->name,
+                       xdev->bus->bus_name, xdev->devpath,
+                       dev->driver_info->description);
+
+       net = dev->net;
+       unregister_netdev (net);
+
+       /* we don't hold rtnl here ... */
+       flush_scheduled_work ();
+
+       if (dev->driver_info->unbind)
+               dev->driver_info->unbind (dev, intf);
+
+       free_netdev(net);
+       usb_put_dev (xdev);
+}
+
+/*-------------------------------------------------------------------------*/
+
+// precondition: never called in_interrupt
+
+static int
+axusbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
+{
+       struct usbnet                   *dev;
+       struct net_device               *net;
+       struct usb_host_interface       *interface;
+       struct driver_info              *info;
+       struct usb_device               *xdev;
+       int                             status;
+       const char                      *name;
+
+       name = udev->dev.driver->name;
+       info = (struct driver_info *) prod->driver_info;
+       if (!info) {
+               printk (KERN_ERR "blacklisted by %s\n", name);
+               return -ENODEV;
+       }
+       xdev = interface_to_usbdev (udev);
+       interface = udev->cur_altsetting;
+
+       usb_get_dev (xdev);
+
+       status = -ENOMEM;
+
+       // set up our own records
+       net = alloc_etherdev(sizeof(*dev));
+       if (!net) {
+               dbg ("can't kmalloc dev");
+               goto out;
+       }
+
+       dev = netdev_priv(net);
+       dev->udev = xdev;
+       dev->intf = udev;
+       dev->driver_info = info;
+       dev->driver_name = name;
+       dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
+                               | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_IFDOWN |NETIF_MSG_IFUP);
+       skb_queue_head_init (&dev->rxq);
+       skb_queue_head_init (&dev->txq);
+       skb_queue_head_init (&dev->done);
+       dev->bh.func = axusbnet_bh;
+       dev->bh.data = (unsigned long) dev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       INIT_WORK (&dev->kevent, kevent, dev);
+#else
+       INIT_WORK (&dev->kevent, kevent);
+#endif
+
+       dev->delay.function = axusbnet_bh;
+       dev->delay.data = (unsigned long) dev;
+       init_timer (&dev->delay);
+//     mutex_init (&dev->phy_mutex);
+
+       dev->net = net;
+
+       /* rx and tx sides can use different message sizes;
+        * bind() should set rx_urb_size in that case.
+        */
+       dev->hard_mtu = net->mtu + net->hard_header_len;
+
+       dev->asix_suspend = 0;
+       dev->intr_complete = 1;
+       init_waitqueue_head(&dev->intr_wait);
+
+#if 0
+// dma_supported() is deeply broken on almost all architectures
+       // possible with some EHCI controllers
+       if (dma_supported (&udev->dev, DMA_BIT_MASK(64)))
+               net->features |= NETIF_F_HIGHDMA;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+       net->open               = axusbnet_open,
+       net->stop               = axusbnet_stop,
+       net->hard_start_xmit    = axusbnet_start_xmit,
+       net->tx_timeout = axusbnet_tx_timeout,
+       net->get_stats = axusbnet_get_stats;
+#endif
+
+       net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
+       net->ethtool_ops = &axusbnet_ethtool_ops;
+
+       // allow device-specific bind/init procedures
+       // NOTE net->name still not usable ...
+       status = info->bind (dev, udev);
+       if (status < 0) {
+               deverr(dev, "Binding device failed: %d", status);
+               goto out1;
+       } else  {
+               printk("----> %s %d:bind %s\n",__FUNCTION__,__LINE__,info->description);
+       }
+
+       /* maybe the remote can't receive an Ethernet MTU */
+       if (net->mtu > (dev->hard_mtu - net->hard_header_len))
+               net->mtu = dev->hard_mtu - net->hard_header_len;
+
+       status = init_status (dev, udev);
+       if (status < 0)
+               goto out3;
+
+       if (!dev->rx_urb_size)
+               dev->rx_urb_size = dev->hard_mtu;
+       dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
+
+       SET_NETDEV_DEV(net, &udev->dev);
+       status = register_netdev (net);
+       if (status) {
+               deverr(dev, "net device registration failed: %d", status);
+               goto out3;
+       }
+
+       if (netif_msg_probe (dev))
+               devinfo (dev, "register '%s' at usb-%s-%s, %s, %pM",
+                       udev->dev.driver->name,
+                       xdev->bus->bus_name, xdev->devpath,
+                       dev->driver_info->description,
+                       net->dev_addr);
+
+       // ok, it's ready to go.
+       usb_set_intfdata (udev, dev);
+
+       // start as if the link is up
+       netif_device_attach (net);
+
+       return 0;
+
+out3:
+       if (info->unbind)
+               info->unbind (dev, udev);
+out1:
+       free_netdev(net);
+out:
+       usb_put_dev(xdev);
+       return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * suspend the whole driver as soon as the first interface is suspended
+ * resume only when the last interface is resumed
+ */
+
+static int axusbnet_suspend (struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,10)
+pm_message_t message)
+#else
+u32 message)
+#endif
+{
+       struct usbnet           *dev = usb_get_intfdata(intf);
+
+       if (!dev->suspend_count++) {
+               /*
+                * accelerate emptying of the rx and queues, to avoid
+                * having everything error out.
+                */
+               netif_device_detach (dev->net);
+               (void) unlink_urbs (dev, &dev->rxq);
+               (void) unlink_urbs (dev, &dev->txq);
+               /*
+                * reattach so runtime management can use and
+                * wake the device
+                */
+               netif_device_attach (dev->net);
+       }
+       return 0;
+}
+
+static int
+axusbnet_resume (struct usb_interface *intf)
+{
+       struct usbnet           *dev = usb_get_intfdata(intf);
+
+       if (!--dev->suspend_count)
+               tasklet_schedule (&dev->bh);
+
+       return 0;
+}
+
diff --git a/drivers/net/usb/axusbnet.h b/drivers/net/usb/axusbnet.h
new file mode 100755 (executable)
index 0000000..a644763
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * USB Networking Link Interface
+ *
+ * Copyright (C) 2000-2005 by David Brownell <dbrownell@users.sourceforge.net>
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef        __LINUX_USB_USBNET_H
+#define        __LINUX_USB_USBNET_H
+
+#ifndef gfp_t
+#define gfp_t int
+#endif
+
+/* interface from usbnet core to each USB networking link we handle */
+struct usbnet {
+       /* housekeeping */
+       struct usb_device       *udev;
+       struct usb_interface    *intf;
+       struct driver_info      *driver_info;
+       const char              *driver_name;
+       void                    *driver_priv;
+       wait_queue_head_t       *wait;
+//     struct mutex            phy_mutex;
+       unsigned char           suspend_count;
+
+       /* i/o info: pipes etc */
+       unsigned                in, out;
+       struct usb_host_endpoint *status;
+       unsigned                maxpacket;
+       struct timer_list       delay;
+
+       /* protocol/interface state */
+       struct net_device       *net;
+       struct net_device_stats stats;
+       int                     msg_enable;
+       unsigned long           data [5];
+       u32                     xid;
+       u32                     hard_mtu;       /* count any extra framing */
+       size_t                  rx_urb_size;    /* size for rx urbs */
+       struct mii_if_info      mii;
+
+       /* various kinds of pending driver work */
+       struct sk_buff_head     rxq;
+       struct sk_buff_head     txq;
+       struct sk_buff_head     done;
+       struct sk_buff_head     rxq_pause;
+       struct urb              *interrupt;
+       struct tasklet_struct   bh;
+
+       struct work_struct      kevent;
+       unsigned long           flags;
+#              define EVENT_TX_HALT    0
+#              define EVENT_RX_HALT    1
+#              define EVENT_RX_MEMORY  2
+#              define EVENT_STS_SPLIT  3
+#              define EVENT_LINK_RESET 4
+#              define EVENT_RX_PAUSED  5
+
+       void                    *priv;  /* point to minidriver private data */
+       unsigned char           rx_size;
+       int                     asix_suspend;
+       int                             intr_complete;
+       wait_queue_head_t intr_wait;
+};
+
+static inline struct usb_driver *driver_of(struct usb_interface *intf)
+{
+       return to_usb_driver(intf->dev.driver);
+}
+
+/* interface from the device/framing level "minidriver" to core */
+struct driver_info {
+       char            *description;
+
+       int             flags;
+/* framing is CDC Ethernet, not writing ZLPs (hw issues), or optionally: */
+#define FLAG_FRAMING_NC        0x0001          /* guard against device dropouts */
+#define FLAG_FRAMING_GL        0x0002          /* genelink batches packets */
+#define FLAG_FRAMING_Z 0x0004          /* zaurus adds a trailer */
+#define FLAG_FRAMING_RN        0x0008          /* RNDIS batches, plus huge header */
+
+#define FLAG_NO_SETINT 0x0010          /* device can't set_interface() */
+#define FLAG_ETHER     0x0020          /* maybe use "eth%d" names */
+
+#define FLAG_FRAMING_AX 0x0040         /* AX88772/178 packets */
+#define FLAG_WLAN      0x0080          /* use "wlan%d" names */
+#define FLAG_AVOID_UNLINK_URBS 0x0100  /* don't unlink urbs at usbnet_stop() */
+#define FLAG_SEND_ZLP  0x0200          /* hw requires ZLPs are sent */
+#define FLAG_HW_IP_ALIGNMENT   0x0400  /* AX88772B support hardware IP alignment */
+
+
+       /* init device ... can sleep, or cause probe() failure */
+       int     (*bind)(struct usbnet *, struct usb_interface *);
+
+       /* cleanup device ... can sleep, but can't fail */
+       void    (*unbind)(struct usbnet *, struct usb_interface *);
+
+       /* reset device ... can sleep */
+       int     (*reset)(struct usbnet *);
+
+       /* stop device ... can sleep */
+       int     (*stop)(struct usbnet *);
+
+       /* see if peer is connected ... can sleep */
+       int     (*check_connect)(struct usbnet *);
+
+       /* for status polling */
+       void    (*status)(struct usbnet *, struct urb *);
+
+       /* link reset handling, called from defer_kevent */
+       int     (*link_reset)(struct usbnet *);
+
+       /* fixup rx packet (strip framing) */
+       int     (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
+
+       /* fixup tx packet (add framing) */
+       struct sk_buff  *(*tx_fixup)(struct usbnet *dev,
+                               struct sk_buff *skb, gfp_t flags);
+
+       /* early initialization code, can sleep. This is for minidrivers
+        * having 'subminidrivers' that need to do extra initialization
+        * right after minidriver have initialized hardware. */
+       int     (*early_init)(struct usbnet *dev);
+
+       /* called by minidriver when receiving indication */
+       void    (*indication)(struct usbnet *dev, void *ind, int indlen);
+
+       /* for new devices, use the descriptor-reading code instead */
+       int             in;             /* rx endpoint */
+       int             out;            /* tx endpoint */
+
+       unsigned long   data;           /* Misc driver specific data */
+};
+
+/* Drivers that reuse some of the standard USB CDC infrastructure
+ * (notably, using multiple interfaces according to the CDC
+ * union descriptor) get some helper code.
+ */
+struct cdc_state {
+       struct usb_cdc_header_desc      *header;
+       struct usb_cdc_union_desc       *u;
+       struct usb_cdc_ether_desc       *ether;
+       struct usb_interface            *control;
+       struct usb_interface            *data;
+};
+
+/* CDC and RNDIS support the same host-chosen packet filters for IN transfers */
+#define        DEFAULT_FILTER  (USB_CDC_PACKET_TYPE_BROADCAST \
+                       |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+                       |USB_CDC_PACKET_TYPE_PROMISCUOUS \
+                       |USB_CDC_PACKET_TYPE_DIRECTED)
+
+
+/* we record the state for each of our queued skbs */
+enum skb_state {
+       illegal = 0,
+       tx_start, tx_done,
+       rx_start, rx_done, rx_cleanup
+};
+
+struct skb_data {      /* skb->cb is one of these */
+       struct urb              *urb;
+       struct usbnet           *dev;
+       enum skb_state          state;
+       size_t                  length;
+};
+
+#ifndef skb_queue_walk_safe
+#define skb_queue_walk_safe(queue, skb, tmp)                           \
+                       for (skb = (queue)->next, tmp = skb->next;      \
+                       skb != (struct sk_buff *)(queue);               \
+                       skb = tmp, tmp = skb->next)
+#endif
+
+/* messaging support includes the interface name, so it must not be
+ * used before it has one ... notably, in minidriver bind() calls.
+ */
+#ifdef DEBUG
+#define devdbg(usbnet, fmt, arg...) \
+       printk("%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+#else
+#define devdbg(usbnet, fmt, arg...) \
+       ({ if (0) printk("%s: " fmt "\n" , (usbnet)->net->name , \
+               ## arg); 0; })
+#endif
+
+#define deverr(usbnet, fmt, arg...) \
+       printk(KERN_ERR "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+#define devwarn(usbnet, fmt, arg...) \
+       printk(KERN_WARNING "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+
+#define devinfo(usbnet, fmt, arg...) \
+       printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net->name , ## arg); \
+
+
+#endif /* __LINUX_USB_USBNET_H */
diff --git a/drivers/net/usb/dm9620.c b/drivers/net/usb/dm9620.c
new file mode 100755 (executable)
index 0000000..1c735e7
--- /dev/null
@@ -0,0 +1,1042 @@
+/*
+ * Davicom DM9620 USB 2.0 10/100Mbps ethernet devices
+ *
+ * Peter Korsgaard <jacmet@sunsite.dk>
+ *
+ * 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.
+ * V1.0 - ftp fail fixed
+ * V1.1 - model name checking, & ether plug function enhancement [0x4f, 0x20]
+ * V1.2 - init tx/rx checksum
+ *      - fix dm_write_shared_word(), bug fix
+ *      - fix 10 Mbps link at power saving mode fail  
+ * V1.3 - Support kernel 2.6.31
+ * V1.4 - Support eeprom write of ethtool 
+ *        Support DM9685
+ *        Transmit Check Sum Control by Optopn (Source Code Default: Disable)
+ *        Recieve Drop Check Sum Error Packet Disable as chip default
+ * V1.5 - Support RK2818 (Debug the Register Function) 
+ */
+
+//#define DEBUG
+
+#define RK2818
+
+
+#include <linux/module.h>
+//#include <linux/kernel.h> // new v1.3
+#include <linux/sched.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/usb/usbnet.h>
+#include <linux/ctype.h>
+#include <linux/skbuff.h>   
+#include <linux/version.h> // new v1.3
+
+/* datasheet:
+ http://www.davicom.com.tw
+*/
+
+/* 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_EE_PHY_L    0x0d
+#define DM_EE_PHY_H    0x0e
+#define DM_WAKEUP_CTRL  0x0f
+#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_XPHY_CTRL   0x2e
+#define DM_TX_CRC_CTRL 0x31
+#define DM_RX_CRC_CTRL 0x32 
+#define DM_SMIREG       0x91
+#define USB_CTRL       0xf4
+#define PHY_SPEC_CFG   20
+
+#define MD96XX_EEPROM_MAGIC    0x9620
+#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_9601    7       /* 3 byte header + 4 byte crc tail */
+#define DM_RX_OVERHEAD         8       /* 4 byte header + 4 byte crc tail */
+#define DM_TIMEOUT     1000
+#define DM_MODE9620     0x80
+#define DM_TX_CS_EN    0        /* Transmit Check Sum Control */
+
+struct dm96xx_priv {
+       int     flag_fail_count;
+       u8     mode_9620;       
+};     
+
+
+static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+       devdbg(dev, "dm_read() reg=0x%02x length=%d", reg, length);
+       return usb_control_msg(dev->udev,
+                              usb_rcvctrlpipe(dev->udev, 0),
+                              DM_READ_REGS,
+                              USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                              0, reg, data, length, USB_CTRL_SET_TIMEOUT); //USB_CTRL_SET_TIMEOUT V.S. USB_CTRL_GET_TIMEOUT
+}
+
+static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value)
+{
+//     return dm_read(dev, reg, 1, value);
+
+       //__le16 w;
+       //int ret = dm_read(dev, reg, 2, &w);  // usb_submit_urb
+       //*value= (u8)(w & 0xff);
+       //return ret;
+       u16 *tmpwPtr;
+       int ret;
+       tmpwPtr= kmalloc (2, GFP_ATOMIC);
+       if (!tmpwPtr)
+       {
+               printk("+++++++++++ JJ5 dm_read_reg() Error: can not kmalloc!\n"); //usbnet_suspend (intf, message);
+               return 0; 
+       }
+       
+       ret = dm_read(dev, reg, 2, tmpwPtr);  // usb_submit_urb v.s. usb_control_msg
+       *value= (u8)(*tmpwPtr & 0xff);
+       
+       kfree (tmpwPtr);
+       return ret;
+}
+
+static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+       devdbg(dev, "dm_write() reg=0x%02x, length=%d", reg, length);
+       return usb_control_msg(dev->udev,
+                              usb_sndctrlpipe(dev->udev, 0),
+                              DM_WRITE_REGS,
+                              USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE,
+                              0, reg, data, length, USB_CTRL_SET_TIMEOUT);
+}
+
+static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)
+{
+       devdbg(dev, "dm_write_reg() reg=0x%02x, value=0x%02x", reg, value);
+       return usb_control_msg(dev->udev,
+                              usb_sndctrlpipe(dev->udev, 0),
+                              DM_WRITE_REG,
+                              USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE,
+                              value, reg, NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static void dm_write_async_callback(struct urb *urb)
+{
+       struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+
+       if (urb->status < 0)
+               printk(KERN_DEBUG "dm_write_async_callback() failed with %d\n",
+                      urb->status);
+
+       kfree(req);
+       usb_free_urb(urb);
+}
+
+static void dm_write_async_helper(struct usbnet *dev, u8 reg, u8 value,
+                                 u16 length, void *data)
+{
+       struct usb_ctrlrequest *req;
+       struct urb *urb;
+       int status;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               deverr(dev, "Error allocating URB in dm_write_async_helper!");
+               return;
+       }
+
+       req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+       if (!req) {
+               deverr(dev, "Failed to allocate memory for control request");
+               usb_free_urb(urb);
+               return;
+       }
+
+       req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+       req->bRequest = length ? DM_WRITE_REGS : DM_WRITE_REG;
+       req->wValue = cpu_to_le16(value);
+       req->wIndex = cpu_to_le16(reg);
+       req->wLength = cpu_to_le16(length);
+
+       usb_fill_control_urb(urb, dev->udev,
+                            usb_sndctrlpipe(dev->udev, 0),
+                            (void *)req, data, length,
+                            dm_write_async_callback, req);
+
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+       if (status < 0) {
+               deverr(dev, "Error submitting the control message: status=%d",
+                      status);
+               kfree(req);
+               usb_free_urb(urb);
+       }
+}
+
+static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+       devdbg(dev, "dm_write_async() reg=0x%02x length=%d", reg, length);
+
+       dm_write_async_helper(dev, reg, 0, length, data);
+}
+
+static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
+{
+       devdbg(dev, "dm_write_reg_async() reg=0x%02x value=0x%02x",
+              reg, value);
+
+       dm_write_async_helper(dev, reg, value, 0, NULL);
+}
+
+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;
+
+               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) {
+               deverr(dev, "%s read timed out!", phy ? "phy" : "eeprom");
+               ret = -EIO;
+               goto out;
+       }
+
+       dm_write_reg(dev, DM_SHARED_CTRL, 0x0);
+       ret = dm_read(dev, DM_SHARED_DATA, 2, value);
+
+       devdbg(dev, "read shared %d 0x%02x returned 0x%04x, %d",
+              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 ? 0x0a : 0x12);
+       if (!phy) dm_write_reg(dev, DM_SHARED_CTRL, 0x10);
+       dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x0a : 0x12);
+       dm_write_reg(dev, DM_SHARED_CTRL, 0x10);
+
+       for (i = 0; i < DM_TIMEOUT; i++) {
+               u8 tmp;
+
+               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) {
+               deverr(dev, "%s write timed out!", 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_write_eeprom_word(struct usbnet *dev, int phy, u8 offset, u8 value)
+{
+       int ret, i;
+       u8  reg,dloc;
+       __le16 eeword;
+
+       //devwarn(dev, " offset =0x%x value = 0x%x ", offset,value);
+       
+       /* hank: from offset to determin eeprom word register location,reg */
+       reg = (offset >> 1)&0xff;
+
+       /* hank:  high/low byte by odd/even of offset  */
+       dloc = (offset & 0x01)? DM_EE_PHY_H:DM_EE_PHY_L;
+
+       /* retrieve high and low byte from the corresponding reg*/
+       ret=dm_read_shared_word(dev,0,reg,&eeword);
+       //devwarn(dev, " reg =0x%x dloc = 0x%x eeword = 0x%4x", reg,dloc,eeword);
+       mutex_lock(&dev->phy_mutex);
+       /* hank: write data to eeprom high/low byte reg */
+        dm_write(dev, (offset & 0x01)? DM_EE_PHY_H:DM_EE_PHY_L, 1, &value);
+
+       /* load the unaffected word to value */
+       (offset & 0x01)? (value = eeword << 8):(value = eeword >> 8);
+
+       /* write the not modified 8 bits back to its origional high/low byte reg */ 
+       dm_write(dev, (offset & 0x01)? DM_EE_PHY_L:DM_EE_PHY_H, 1, &value);
+       if (ret < 0)
+               goto out;
+
+       /* hank : write word location to reg 0x0c  */
+       ret = dm_write_reg(dev, DM_SHARED_ADDR, reg);
+               
+       if (!phy) dm_write_reg(dev, DM_SHARED_CTRL, 0x10);
+       dm_write_reg(dev, DM_SHARED_CTRL, 0x12);
+       dm_write_reg(dev, DM_SHARED_CTRL, 0x10);
+
+       for (i = 0; i < DM_TIMEOUT; i++) {
+               u8 tmp;
+
+               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) {
+               deverr(dev, "%s write timed out!", 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 dm9620_set_eeprom(struct net_device *net,struct ethtool_eeprom *eeprom, u8 *data)
+{
+       struct usbnet *dev = netdev_priv(net);
+       
+       devwarn(dev, "EEPROM: magic value, magic = 0x%x offset =0x%x data = 0x%x ",eeprom->magic, eeprom->offset,*data);
+       if (eeprom->magic != MD96XX_EEPROM_MAGIC) {
+               devwarn(dev, "EEPROM: magic value mismatch, magic = 0x%x",
+                       eeprom->magic);
+               return -EINVAL;
+       }
+
+               if(dm_write_eeprom_word(dev, 0, eeprom->offset, *data) < 0)  
+               return -EINVAL;
+       
+       return 0;
+}
+
+static int dm9620_get_eeprom_len(struct net_device *dev)
+{
+       return DM_EEPROM_LEN;
+}
+
+static int dm9620_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 dm9620_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+
+       __le16 res;
+
+       if (phy_id) {
+               devdbg(dev, "Only internal phy supported");
+               return 0;
+       }
+
+       dm_read_shared_word(dev, 1, loc, &res);
+
+       devdbg(dev,
+              "dm9620_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x",
+              phy_id, loc, le16_to_cpu(res));
+
+       return le16_to_cpu(res);
+}
+
+static void dm9620_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);
+       int mdio_val;
+
+       if (phy_id) {
+               devdbg(dev, "Only internal phy supported");
+               return;
+       }
+
+       devdbg(dev,"dm9620_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x",
+              phy_id, loc, val);
+
+       dm_write_shared_word(dev, 1, loc, res);
+       mdelay(1);
+       mdio_val = dm9620_mdio_read(netdev, phy_id, loc);
+
+}
+
+static void dm9620_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 dm9620_get_link(struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+
+       return mii_link_ok(&dev->mii);
+}
+
+static int dm9620_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);
+}
+
+
+#define DM_LINKEN  (1<<5)
+#define DM_MAGICEN (1<<3)
+#define DM_LINKST  (1<<2)
+#define DM_MAGICST (1<<0)
+
+static void
+dm9620_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+       struct usbnet *dev = netdev_priv(net);
+       u8 opt;
+
+       if (dm_read_reg(dev, DM_WAKEUP_CTRL, &opt) < 0) {
+               wolinfo->supported = 0;
+               wolinfo->wolopts = 0;
+               return;
+       }
+       wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+       wolinfo->wolopts = 0;
+
+       if (opt & DM_LINKEN)
+               wolinfo->wolopts |= WAKE_PHY;
+       if (opt & DM_MAGICEN)
+               wolinfo->wolopts |= WAKE_MAGIC;
+}
+
+
+static int
+dm9620_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 |= DM_LINKEN;
+       if (wolinfo->wolopts & WAKE_MAGIC)
+               opt |= DM_MAGICEN;
+
+       dm_write_reg(dev, DM_NET_CTRL, 0x48);  // enable WAKEEN 
+       
+       dm_write_reg(dev, 0x92, 0x3f); //keep clock on Hank Jun 30
+       
+       return dm_write_reg(dev, DM_WAKEUP_CTRL, opt);
+}
+
+static struct ethtool_ops dm9620_ethtool_ops = {
+       .get_drvinfo    = dm9620_get_drvinfo,
+       .get_link       = dm9620_get_link,
+       .get_msglevel   = usbnet_get_msglevel,
+       .set_msglevel   = usbnet_set_msglevel,
+       .get_eeprom_len = dm9620_get_eeprom_len,
+       .get_eeprom     = dm9620_get_eeprom,
+       .set_eeprom     = dm9620_set_eeprom,
+       .get_settings   = usbnet_get_settings,
+       .set_settings   = usbnet_set_settings,
+       .nway_reset     = usbnet_nway_reset,
+       .get_wol        = dm9620_get_wol,
+       .set_wol        = dm9620_set_wol,
+};
+
+static void dm9620_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 || net->mc_count > DM_MAX_MCAST) {
+               rx_ctl |= 0x04;
+       } else if (net->mc_count) {
+               struct dev_mc_list *mc_list = net->mc_list;
+               int i;
+
+               for (i = 0; i < net->mc_count; i++, mc_list = mc_list->next) {
+                       u32 crc = ether_crc(ETH_ALEN, mc_list->dmi_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 __dm9620_set_mac_address(struct usbnet *dev)
+ {
+         dm_write_async(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr);
+ }
+ static int dm9620_set_mac_address(struct net_device *net, void *p)
+ {
+         struct sockaddr *addr = p;
+         struct usbnet *dev = netdev_priv(net);
+        int i;
+#if 1
+        printk("[dm96] Set mac addr %pM\n", addr->sa_data);  // %x:%x:...
+        printk("[dm96] ");
+        for (i=0; i<net->addr_len; i++)
+        printk("[%02x] ", addr->sa_data[i]);
+        printk("\n");
+ #endif
+         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);
+         __dm9620_set_mac_address(dev);
+         return 0;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 
+
+static const struct net_device_ops vm_netdev_ops= { // new kernel 2.6.31  (20091217JJ)
+
+            .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           = dm9620_ioctl,   
+           .ndo_set_multicast_list = dm9620_set_multicast,   
+            .ndo_set_mac_address    = dm9620_set_mac_address,  
+};
+#endif
+
+static int dm9620_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       int ret,mdio_val,i;
+       struct dm96xx_priv* priv;
+       u8 temp;
+       u8 tmp;
+
+       ret = usbnet_get_endpoints(dev, intf);
+       if (ret)
+               goto out;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 
+       dev->net->netdev_ops = &vm_netdev_ops; // new kernel 2.6.31  (20091217JJ)
+       dev->net->ethtool_ops = &dm9620_ethtool_ops;
+#else
+       dev->net->do_ioctl = dm9620_ioctl;  
+       dev->net->set_multicast_list = dm9620_set_multicast;
+       dev->net->ethtool_ops = &dm9620_ethtool_ops;
+#endif
+       dev->net->hard_header_len += DM_TX_OVERHEAD;
+       dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
+#ifdef RK2818   //harris 2010.12.27
+       dev->rx_urb_size = 8912; // ftp fail fixed
+#else
+       dev->rx_urb_size = dev->net->mtu + ETH_HLEN + DM_RX_OVERHEAD+1; // ftp fail fixed
+#endif
+
+       dev->mii.dev = dev->net;
+       dev->mii.mdio_read = dm9620_mdio_read;
+       dev->mii.mdio_write = dm9620_mdio_write;
+       dev->mii.phy_id_mask = 0x1f;
+       dev->mii.reg_num_mask = 0x1f;
+
+//JJ1
+       if ( (ret= dm_read_reg(dev, 0x29, &tmp)) >=0)
+               printk("++++++[dm962]+++++ dm_read_reg() 0x29 0x%02x\n",tmp);
+       else
+               printk("++++++[dm962]+++++ dm_read_reg() 0x29 fail-func-return %d\n", ret);
+               
+       if ( (ret= dm_read_reg(dev, 0x28, &tmp)) >=0)
+               printk("++++++[dm962]+++++ dm_read_reg() 0x28 0x%02x\n",tmp);
+       else
+               printk("++++++[dm962]+++++ dm_read_reg() 0x28 fail-func-return %d\n", ret);
+               
+       if ( (ret= dm_read_reg(dev, 0x2b, &tmp)) >=0)
+               printk("++++++[dm962]+++++ dm_read_reg() 0x2b 0x%02x\n",tmp);
+       else
+               printk("++++++[dm962]+++++ dm_read_reg() 0x2b fail-func-return %d\n", ret);
+       if ( (ret= dm_read_reg(dev, 0x2a, &tmp)) >=0)
+               printk("++++++[dm962]+++++ dm_read_reg() 0x2a 0x%02x\n",tmp);
+       else
+               printk("++++++[dm962]+++++ dm_read_reg() 0x2a fail-func-return %d\n", ret);
+               
+//JJ3
+       if ( (ret= dm_read_reg(dev, 0xF2, &tmp)) >=0)
+               printk("++++++[dm962]+++++ dm_read_reg() 0xF2 0x%02x\n",tmp);
+       else
+               printk("++++++[dm962]+++++ dm_read_reg() 0xF2 fail-func-return %d\n", ret);
+               
+               printk("++++++[dm962]+++++  [Analysis.2] 0xF2, D[7] %d %s\n", tmp>>7, (tmp&(1<<7))? "Err: RX Unexpected condition": "OK" );
+               printk("++++++[dm962]+++++  [Analysis.2] 0xF2, D[6] %d %s\n", (tmp>>6)&1, (tmp&(1<<6))? "Err: Host Suspend condition": "OK" );
+               printk("++++++[dm962]+++++  [Analysis.2] 0xF2, D[5] %d %s\n", (tmp>>5)&1, (tmp&(1<<5))? "EP1: Data Ready": "EP1: Empty" );
+               printk("++++++[dm962]+++++  [Analysis.2] 0xF2, D[3] %d %s\n", (tmp>>3)&1, (tmp&(1<<3))? "Err: Bulk out condition": "OK" );
+               
+               printk("++++++[dm962]+++++  [Analysis.2] 0xF2, D[2] %d %s\n", (tmp>>2)&1, (tmp&(1<<2))? "Err: TX Buffer full": "OK" );
+               printk("++++++[dm962]+++++  [Analysis.2] 0xF2, D[1] %d %s\n", (tmp>>1)&1, (tmp&(1<<1))? "Warn: TX buffer Almost full": "OK" );
+               printk("++++++[dm962]+++++  [Analysis.2] 0xF2, D[0] %d %s\n", (tmp>>0)&1, (tmp&(1<<0))? "Status: TX buffer has pkts": "Status: TX buffer 0 pkts" );
+
+       /* reset */
+       dm_write_reg(dev, DM_XPHY_CTRL, 0); // dm9622/dm9685, bit[5](EXTERNAL), clear-it, add clk & limit to internal PHY
+       dm_write_reg(dev, DM_NET_CTRL, 1);
+       udelay(20);
+       
+       
+
+       /* Add V1.1, Enable auto link while plug in RJ45, Hank July 20, 2009*/
+       dm_write_reg(dev, USB_CTRL, 0x20); 
+       /* read MAC */
+       if (dm_read(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr) < 0) {
+               printk(KERN_ERR "Error reading MAC address\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+#if 1
+        printk("[dm96] Chk mac addr %pM\n", dev->net->dev_addr);  // %x:%x...
+        printk("[dm96] ");
+        for (i=0; i<ETH_ALEN; i++)
+        printk("[%02x] ", dev->net->dev_addr[i]);
+        printk("\n");
+#endif
+
+       /* read SMI mode register */
+        priv = dev->driver_priv = kmalloc(sizeof(struct dm96xx_priv), GFP_ATOMIC);
+       if (!priv) {
+               deverr(dev, "Failed to allocate memory for dm96xx_priv");
+               ret = -ENOMEM;
+               goto out;
+       }
+       
+        /* work-around for 9620 mode */
+       printk("[dm96] Fixme: work around for 9620 mode\n");
+       printk("[dm96] Add tx_fixup() debug...\n");
+       dm_write_reg(dev, DM_MCAST_ADDR, 0);     // clear data bus to 0s
+       dm_read_reg(dev, DM_MCAST_ADDR, &temp);  // clear data bus to 0s
+       ret = dm_read_reg(dev, DM_SMIREG, &temp);   // Must clear data bus before we can read the 'MODE9620' bit
+
+       priv->flag_fail_count= 0;
+       if (ret<0) {
+               printk(KERN_ERR "[dm96] Error read SMI register\n");
+       }
+       else priv->mode_9620 = temp & DM_MODE9620;
+
+       printk(KERN_WARNING "[dm96] 9620 Mode = %d\n", priv->mode_9620);
+       
+       /* power up phy */
+       dm_write_reg(dev, DM_GPR_CTRL, 1);
+       dm_write_reg(dev, DM_GPR_DATA, 0);
+
+       /* Init tx/rx checksum */
+#if    DM_TX_CS_EN
+       dm_write_reg(dev, DM_TX_CRC_CTRL, 7);
+#endif 
+       dm_write_reg(dev, DM_RX_CRC_CTRL, 2);
+
+       /* receive broadcast packets */
+       dm9620_set_multicast(dev->net);
+       dm9620_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+
+       /* Hank add, work for comapubility issue (10M Power control) */ 
+       
+       dm9620_mdio_write(dev->net, dev->mii.phy_id, PHY_SPEC_CFG, 0x800);
+       //printk("[dm96] dm962++ write phy[20]= 0x800\n");
+       mdio_val = dm9620_mdio_read(dev->net, dev->mii.phy_id, PHY_SPEC_CFG);
+       //printk("[dm96] dm962++ read  phy[20]= %x\n",mdio_val );
+       
+       dm9620_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;
+}
+
+void dm9620_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+struct dm96xx_priv* priv= dev->driver_priv;
+//u8 opt=0;
+//int i;
+       printk("dm9620_unbind():\n");
+
+       printk("flag_fail_count  %lu\n", (long unsigned int)priv->flag_fail_count);
+       kfree(dev->driver_priv); // displayed dev->.. above, then can free dev 
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 
+       printk("rx_length_errors %lu\n",dev->net->stats.rx_length_errors);
+       printk("rx_over_errors   %lu\n",dev->net->stats.rx_over_errors  );
+       printk("rx_crc_errors    %lu\n",dev->net->stats.rx_crc_errors   );
+       printk("rx_frame_errors  %lu\n",dev->net->stats.rx_frame_errors );
+       printk("rx_fifo_errors   %lu\n",dev->net->stats.rx_fifo_errors  );
+       printk("rx_missed_errors %lu\n",dev->net->stats.rx_missed_errors);      
+#else
+       printk("rx_length_errors %lu\n",dev->stats.rx_length_errors);
+       printk("rx_over_errors   %lu\n",dev->stats.rx_over_errors  );
+       printk("rx_crc_errors    %lu\n",dev->stats.rx_crc_errors   );
+       printk("rx_frame_errors  %lu\n",dev->stats.rx_frame_errors );
+       printk("rx_fifo_errors   %lu\n",dev->stats.rx_fifo_errors  );
+       printk("rx_missed_errors %lu\n",dev->stats.rx_missed_errors);   
+#endif
+
+// check if dm9620 receive magic packet
+//i=dm_read_reg(dev, DM_WAKEUP_CTRL, &opt);
+//     printk("rx_magic_packet   %lu\n",i);
+
+}
+
+static int dm9620_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+       u8 status;
+       int len;
+       struct dm96xx_priv* priv = (struct dm96xx_priv *)dev->driver_priv;
+
+       /* 9620 format:
+          b0: rx status
+          b1: packet length (incl crc) low
+          b2: packet length (incl crc) high
+          b3..n-4: packet data
+          bn-3..bn: ethernet crc
+        */
+
+       /* 9620 format:
+          one additional byte then 9620 : 
+          rx_flag in the first pos
+        */
+
+       if (unlikely(skb->len < DM_RX_OVERHEAD_9601)) {   // 20090623
+               dev_err(&dev->udev->dev, "unexpected tiny rx frame\n");
+               return 0;
+       }
+
+       if (priv->mode_9620) {
+               /* mode 9620 */
+
+               if (unlikely(skb->len < DM_RX_OVERHEAD)) {  // 20090623
+                       dev_err(&dev->udev->dev, "unexpected tiny rx frame\n");
+                       return 0;
+               }
+
+       //.     struct dm96xx_priv* priv= dev->driver_priv;
+       //      if (skb->data[0]!=0x01)
+       //              priv->flag_fail_count++;
+
+               status = skb->data[1];
+               len = (skb->data[2] | (skb->data[3] << 8)) - 4;
+               
+               if (unlikely(status & 0xbf)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 
+                       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++;
+#else
+                       if (status & 0x01) dev->stats.rx_fifo_errors++;
+                       if (status & 0x02) dev->stats.rx_crc_errors++;
+                       if (status & 0x04) dev->stats.rx_frame_errors++;
+                       if (status & 0x20) dev->stats.rx_missed_errors++;
+                       if (status & 0x90) dev->stats.rx_length_errors++;
+#endif
+                       return 0;
+               }
+
+               skb_pull(skb, 4);
+               skb_trim(skb, len);
+
+       }
+       else { /* mode 9620 (original driver code) */
+               status = skb->data[0];
+               len = (skb->data[1] | (skb->data[2] << 8)) - 4;
+               
+               if (unlikely(status & 0xbf)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 
+                       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++;
+#else
+                       if (status & 0x01) dev->stats.rx_fifo_errors++;
+                       if (status & 0x02) dev->stats.rx_crc_errors++;
+                       if (status & 0x04) dev->stats.rx_frame_errors++;
+                       if (status & 0x20) dev->stats.rx_missed_errors++;
+                       if (status & 0x90) dev->stats.rx_length_errors++;
+#endif
+                       return 0;
+               }
+
+               skb_pull(skb, 3);
+               skb_trim(skb, len);
+       }
+
+       return 1;
+} // 'priv'
+
+static struct sk_buff *dm9620_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+                                      gfp_t flags)
+{
+       int len;
+
+       /* format:
+          b0: packet length low
+          b1: packet length high
+          b3..n: packet data
+       */
+
+       len = skb->len;
+
+       if (skb_headroom(skb) < DM_TX_OVERHEAD) {
+               struct sk_buff *skb2;
+               skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags);
+               dev_kfree_skb_any(skb);
+               skb = skb2;
+               if (!skb)
+                       return NULL;
+       }
+
+       __skb_push(skb, DM_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;
+
+       /* hank, recalcute checksum of TCP */
+
+       
+       return skb;
+} // 'kb'
+
+static void dm9620_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) {
+               if (link) {
+                       netif_carrier_on(dev->net);
+                       usbnet_defer_kevent (dev, EVENT_LINK_RESET);
+               }
+               else
+                       netif_carrier_off(dev->net);
+               devdbg(dev, "Link Status is: %d", link);
+       }
+}
+
+static int dm9620_link_reset(struct usbnet *dev)
+{
+       struct ethtool_cmd ecmd;
+       mii_check_media(&dev->mii, 1, 1);
+       mii_ethtool_gset(&dev->mii, &ecmd);
+       /* hank add*/
+dm9620_mdio_write(dev->net, dev->mii.phy_id, PHY_SPEC_CFG, 0x800);
+       devdbg(dev, "link_reset() speed: %d duplex: %d",
+              ecmd.speed, ecmd.duplex);
+       
+       return 0;
+}
+
+static const struct driver_info dm9620_info = {
+       .description    = "Davicom DM9620 USB Ethernet",
+       .flags          = FLAG_ETHER,
+       .bind           = dm9620_bind,
+       .rx_fixup       = dm9620_rx_fixup,
+       .tx_fixup       = dm9620_tx_fixup,
+       .status         = dm9620_status,
+#ifdef RK2818  //harris 2010.12.27
+
+#else
+       .link_reset     = dm9620_link_reset,
+       .reset          = dm9620_link_reset,
+#endif
+       .unbind     = dm9620_unbind,
+};
+
+static const struct usb_device_id products[] = {
+       {
+        USB_DEVICE(0x07aa, 0x9601),    /* Corega FEther USB-TXC */
+        .driver_info = (unsigned long)&dm9620_info,
+        },
+       {
+        USB_DEVICE(0x0a46, 0x9601),    /* Davicom USB-100 */
+        .driver_info = (unsigned long)&dm9620_info,
+        },
+       {
+        USB_DEVICE(0x0a46, 0x6688),    /* ZT6688 USB NIC */
+        .driver_info = (unsigned long)&dm9620_info,
+        },
+       {
+        USB_DEVICE(0x0a46, 0x0268),    /* ShanTou ST268 USB NIC */
+        .driver_info = (unsigned long)&dm9620_info,
+        },
+       {
+        USB_DEVICE(0x0a46, 0x8515),    /* ADMtek ADM8515 USB NIC */
+        .driver_info = (unsigned long)&dm9620_info,
+        },
+       {
+       USB_DEVICE(0x0a47, 0x9601),     /* Hirose USB-100 */
+       .driver_info = (unsigned long)&dm9620_info,
+        },
+        {
+        USB_DEVICE(0x0a46, 0x9620),     /* Davicom 9620 */
+        .driver_info = (unsigned long)&dm9620_info,
+         },
+        {
+        USB_DEVICE(0x0a46, 0x9621),     /* Davicom 9621 */
+        .driver_info = (unsigned long)&dm9620_info,
+         },
+        {
+        USB_DEVICE(0x0a46, 0x9622),     /* Davicom 9622 */
+        .driver_info = (unsigned long)&dm9620_info,
+         },
+        {
+       USB_DEVICE(0x0fe6, 0x8101),     /* Davicom 9601 USB to Fast Ethernet Adapter */
+        .driver_info = (unsigned long)&dm9620_info,
+         },
+       {},                     // END
+};
+
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver dm9620_driver = {
+//     .name = "dm9601",
+       .name = "dm9620",
+       .id_table = products,
+       .probe = usbnet_probe,
+       .disconnect = usbnet_disconnect,
+       .suspend = usbnet_suspend,
+       .resume = usbnet_resume,
+};
+
+
+
+
+static int __init dm9620_init(void)
+{
+       return usb_register(&dm9620_driver);
+}
+
+static void __exit dm9620_exit(void)
+{
+       usb_deregister(&dm9620_driver);
+}
+
+module_init(dm9620_init);
+module_exit(dm9620_exit);
+
+MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
+MODULE_DESCRIPTION("Davicom DM9620 USB 2.0 ethernet devices");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c
new file mode 100755 (executable)
index 0000000..b25eb29
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ * SR9700_android one chip USB 2.0 ethernet devices
+ *
+ * Author : jokeliujl <jokeliu@163.com>
+ * Date : 2010-10-01
+ *
+ * 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 <linux/module.h>
+#include <linux/sched.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/usb/usbnet.h>
+
+#include "sr9700.h"
+
+/* ------------------------------------------------------------------------------------------ */
+/* sr9700_android mac and phy operations */
+/* sr9700_android read some registers from MAC */
+static int qf_read(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+       void *buf;
+       int err = -ENOMEM;
+
+       devdbg(dev, "qf_read() reg=0x%02x length=%d", reg, length);
+
+       buf = kmalloc(length, GFP_KERNEL);
+       if (!buf)
+               goto out;
+
+       err = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), 
+                               QF_RD_REGS, QF_REQ_RD_REG,
+                           0, reg, buf, length, USB_CTRL_SET_TIMEOUT);
+       if (err == length)
+               memcpy(data, buf, length);
+       else if (err >= 0)
+               err = -EINVAL;
+       kfree(buf);
+
+ out:
+       return err;
+}
+
+/* sr9700_android write some registers to MAC */
+static int qf_write(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+       void *buf = NULL;
+       int err = -ENOMEM;
+
+       devdbg(dev, "qf_write() reg=0x%02x, length=%d", reg, length);
+
+       if (data) {
+               buf = kmalloc(length, GFP_KERNEL);
+               if (!buf)
+                       goto out;
+               memcpy(buf, data, length);
+       }
+
+       err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+                             QF_WR_REGS, QF_REQ_WR_REG,
+                             0, reg, buf, length, USB_CTRL_SET_TIMEOUT);
+       kfree(buf);
+       if (err >= 0 && err < length)
+               err = -EINVAL;
+ out:
+       return err;
+}
+
+/* sr9700_android read one register from MAC */
+static int qf_read_reg(struct usbnet *dev, u8 reg, u8 *value)
+{
+       return qf_read(dev, reg, 1, value);
+}
+
+/* sr9700_android write one register to MAC */
+static int qf_write_reg(struct usbnet *dev, u8 reg, u8 value)
+{
+       devdbg(dev, "qf_write_reg() reg=0x%02x, value=0x%02x", reg, value);
+       return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+                              QF_WR_REG, QF_REQ_WR_REG,
+                              value, reg, NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+/* async mode for writing registers or reg blocks */
+static void qf_write_async_callback(struct urb *urb)
+{
+       struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+
+       if (urb->status < 0)
+               printk(KERN_DEBUG "qf_write_async_callback() failed with %d\n", urb->status);
+
+       kfree(req);
+       usb_free_urb(urb);
+}
+
+static void qf_write_async_helper(struct usbnet *dev, u8 reg, u8 value, u16 length, void *data)
+{
+       struct usb_ctrlrequest *req;
+       struct urb *urb;
+       int status;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               deverr(dev, "Error allocating URB in qf_write_async_helper!");
+               return;
+       }
+
+       req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+       if (!req) {
+               deverr(dev, "Failed to allocate memory for control request");
+               usb_free_urb(urb);
+               return;
+       }
+
+       req->bRequestType = QF_REQ_WR_REG;
+       req->bRequest = length ? QF_WR_REGS : QF_WR_REG;
+       req->wValue = cpu_to_le16(value);
+       req->wIndex = cpu_to_le16(reg);
+       req->wLength = cpu_to_le16(length);
+
+       usb_fill_control_urb(urb, dev->udev, usb_sndctrlpipe(dev->udev, 0),
+                            (void *)req, data, length,
+                            qf_write_async_callback, req);
+
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+       if (status < 0) {
+               deverr(dev, "Error submitting the control message: status=%d",
+                      status);
+               kfree(req);
+               usb_free_urb(urb);
+       }
+
+       return;
+}
+
+static void qf_write_async(struct usbnet *dev, u8 reg, u16 length, void *data)
+{
+       devdbg(dev, "qf_write_async() reg=0x%02x length=%d", reg, length);
+
+       qf_write_async_helper(dev, reg, 0, length, data);
+}
+
+static void qf_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
+{
+       devdbg(dev, "qf_write_reg_async() reg=0x%02x value=0x%02x", reg, value);
+
+       qf_write_async_helper(dev, reg, value, 0, NULL);
+}
+
+/* sr9700_android read one word from phy or eeprom  */
+static int qf_share_read_word(struct usbnet *dev, int phy, u8 reg, __le16 *value)
+{
+       int ret, i;
+
+       mutex_lock(&dev->phy_mutex);
+
+       qf_write_reg(dev, EPAR, phy ? (reg | 0x40) : reg);
+       qf_write_reg(dev, EPCR, phy ? 0xc : 0x4);
+
+       for (i = 0; i < QF_SHARE_TIMEOUT; i++) {
+               u8 tmp;
+
+               udelay(1);
+               ret = qf_read_reg(dev, EPCR, &tmp);
+               if (ret < 0)
+                       goto out;
+
+               /* ready */
+               if ((tmp & 1) == 0)
+                       break;
+       }
+
+       if (i >= QF_SHARE_TIMEOUT) {
+               deverr(dev, "%s read timed out!", phy ? "phy" : "eeprom");
+               ret = -EIO;
+               goto out;
+       }
+
+       qf_write_reg(dev, EPCR, 0x0);
+       ret = qf_read(dev, EPDR, 2, value);
+
+       devdbg(dev, "read shared %d 0x%02x returned 0x%04x, %d",
+              phy, reg, *value, ret);
+
+ out:
+       mutex_unlock(&dev->phy_mutex);
+       return ret;
+}
+
+/* write one word to phy or eeprom */
+static int qf_share_write_word(struct usbnet *dev, int phy, u8 reg, __le16 value)
+{
+       int ret, i;
+
+       mutex_lock(&dev->phy_mutex);
+
+       ret = qf_write(dev, EPDR, 2, &value);
+       if (ret < 0)
+               goto out;
+
+       qf_write_reg(dev, EPAR, phy ? (reg | 0x40) : reg);
+       qf_write_reg(dev, EPCR, phy ? 0x1a : 0x12);
+
+       for (i = 0; i < QF_SHARE_TIMEOUT; i++) {
+               u8 tmp;
+
+               udelay(1);
+               ret = qf_read_reg(dev, EPCR, &tmp);
+               if (ret < 0)
+                       goto out;
+
+               /* ready */
+               if ((tmp & 1) == 0)
+                       break;
+       }
+
+       if (i >= QF_SHARE_TIMEOUT) {
+               deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom");
+               ret = -EIO;
+               goto out;
+       }
+
+       qf_write_reg(dev, EPCR, 0x0);
+
+out:
+       mutex_unlock(&dev->phy_mutex);
+       return ret;
+}
+
+static int qf_read_eeprom_word(struct usbnet *dev, u8 offset, void *value)
+{
+       return qf_share_read_word(dev, 0, offset, value);
+}
+
+
+static int sr9700_android_get_eeprom_len(struct net_device *dev)
+{
+       return QF_EEPROM_LEN;
+}
+
+/* get sr9700_android eeprom information */
+static int sr9700_android_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 (qf_read_eeprom_word(dev, eeprom->offset / 2 + i, &ebuf[i]) < 0)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+/* sr9700_android mii-phy register read by word */
+static int sr9700_android_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+
+       __le16 res;
+
+       if (phy_id) {
+               devdbg(dev, "Only internal phy supported");
+               return 0;
+       }
+
+       qf_share_read_word(dev, 1, loc, &res);
+
+       devdbg(dev,
+              "sr9700_android_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x",
+              phy_id, loc, le16_to_cpu(res));
+
+       return le16_to_cpu(res);
+}
+
+/* sr9700_android mii-phy register write by word */
+static void sr9700_android_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) {
+               devdbg(dev, "Only internal phy supported");
+               return;
+       }
+
+       devdbg(dev,"sr9700_android_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x",
+              phy_id, loc, val);
+
+       qf_share_write_word(dev, 1, loc, res);
+}
+
+/*-------------------------------------------------------------------------------------------*/
+
+static void sr9700_android_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
+{
+       /* Inherit standard device info */
+       usbnet_get_drvinfo(net, info);
+       info->eedump_len = QF_EEPROM_LEN;
+}
+
+static u32 sr9700_android_get_link(struct net_device *net)
+{
+       struct usbnet *dev = netdev_priv(net);
+       int rc = 0;
+       u8 value = 0;
+
+#if    0
+       rc = mii_link_ok(&dev->mii);
+#else
+       qf_read_reg(dev, NSR, &value);
+       if(value & NSR_LINKST) {
+               rc = 1;
+       }
+#endif
+
+       return rc;
+}
+
+static int sr9700_android_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 sr9700_android_ethtool_ops = {
+       .get_drvinfo    = sr9700_android_get_drvinfo,
+       .get_link       = sr9700_android_get_link,
+       .get_msglevel   = usbnet_get_msglevel,
+       .set_msglevel   = usbnet_set_msglevel,
+       .get_eeprom_len = sr9700_android_get_eeprom_len,
+       .get_eeprom     = sr9700_android_get_eeprom,
+       .get_settings   = usbnet_get_settings,
+       .set_settings   = usbnet_set_settings,
+       .nway_reset     = usbnet_nway_reset,
+};
+
+static void sr9700_android_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;       // enable, disable_long, disable_crc
+
+       memset(hashes, 0x00, QF_MCAST_SIZE);
+       hashes[QF_MCAST_SIZE - 1] |= 0x80;      /* broadcast address */
+
+       if (net->flags & IFF_PROMISC) {
+               rx_ctl |= 0x02;
+       } else if (net->flags & IFF_ALLMULTI || net->mc_count > QF_MCAST_MAX) {
+               rx_ctl |= 0x04;
+       } else if (net->mc_count) {
+               struct dev_mc_list *mc_list = net->mc_list;
+               int i;
+
+               for (i = 0; i < net->mc_count; i++, mc_list = mc_list->next) {
+                       u32 crc = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26;
+                       hashes[crc >> 3] |= 1 << (crc & 0x7);
+               }
+       }
+
+       qf_write_async(dev, MAR, QF_MCAST_SIZE, hashes);
+       qf_write_reg_async(dev, RCR, rx_ctl);
+}
+
+static int sr9700_android_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);
+       qf_write_async(dev, PAR, 6, dev->net->dev_addr);
+
+       return 0;
+}
+
+static const struct net_device_ops sr9700_android_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_android_ioctl,
+       .ndo_set_multicast_list = sr9700_android_set_multicast,
+       .ndo_set_mac_address    = sr9700_android_set_mac_address,
+};
+
+static int sr9700_android_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       int ret;
+
+       ret = usbnet_get_endpoints(dev, intf);
+       if (ret)
+               goto out;
+
+       dev->net->netdev_ops = &sr9700_android_netdev_ops;
+       dev->net->ethtool_ops = &sr9700_android_ethtool_ops;
+       dev->net->hard_header_len += QF_TX_OVERHEAD;
+       dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
+       dev->rx_urb_size =4096;// dev->net->mtu + ETH_HLEN + QF_RX_OVERHEAD;
+
+       dev->mii.dev = dev->net;
+       dev->mii.mdio_read = sr9700_android_mdio_read;
+       dev->mii.mdio_write = sr9700_android_mdio_write;
+       dev->mii.phy_id_mask = 0x1f;
+       dev->mii.reg_num_mask = 0x1f;
+
+       /* reset the sr9700_android */
+       qf_write_reg(dev, NCR, 1);
+       udelay(20);
+
+       /* read MAC */
+       if (qf_read(dev, PAR, ETH_ALEN, dev->net->dev_addr) < 0) {
+               printk(KERN_ERR "Error reading MAC address\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       /* power up and reset phy */
+       qf_write_reg(dev, PRR, 1);
+       mdelay(20);             // at least 10ms, here 20ms for safe
+       qf_write_reg(dev, PRR, 0);
+       udelay(2 * 1000);       // at least 1ms, here 2ms for reading right register
+
+       /* receive broadcast packets */
+       sr9700_android_set_multicast(dev->net);
+
+       sr9700_android_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+       sr9700_android_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 sr9700_android_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+       u8 status;
+       int len;
+
+       /* format:
+          b0: rx status
+          b1: packet length (incl crc) low
+          b2: packet length (incl crc) high
+          b3..n-4: packet data
+          bn-3..bn: ethernet crc
+        */
+
+       if (unlikely(skb->len < QF_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 *sr9700_android_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+       int len;
+
+       /* format:
+          b0: packet length low
+          b1: packet length high
+          b3..n: packet data
+       */
+
+       len = skb->len;
+
+       if (skb_headroom(skb) < QF_TX_OVERHEAD) {
+               struct sk_buff *skb2;
+
+               skb2 = skb_copy_expand(skb, QF_TX_OVERHEAD, 0, flags);
+               dev_kfree_skb_any(skb);
+               skb = skb2;
+               if (!skb)
+                       return NULL;
+       }
+
+       __skb_push(skb, QF_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_android_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) {
+               if (link) {
+                       netif_carrier_on(dev->net);
+                       usbnet_defer_kevent (dev, EVENT_LINK_RESET);
+               }
+               else
+                       netif_carrier_off(dev->net);
+               devdbg(dev, "Link Status is: %d", link);
+       }
+}
+
+static int sr9700_android_link_reset(struct usbnet *dev)
+{
+       struct ethtool_cmd ecmd;
+
+       mii_check_media(&dev->mii, 1, 1);
+       mii_ethtool_gset(&dev->mii, &ecmd);
+
+       devdbg(dev, "link_reset() speed: %d duplex: %d",
+              ecmd.speed, ecmd.duplex);
+
+       return 0;
+}
+
+static const struct driver_info sr9700_android_info = {
+       .description    = "SR9700_ANDROID USB Ethernet",
+       .flags          = FLAG_ETHER,
+       .bind           = sr9700_android_bind,
+       .rx_fixup       = sr9700_android_rx_fixup,
+       .tx_fixup       = sr9700_android_tx_fixup,
+       .status         = sr9700_android_status,
+       .link_reset     = sr9700_android_link_reset,
+       .reset          = sr9700_android_link_reset,
+};
+
+static const struct usb_device_id products[] = {
+       {
+        USB_DEVICE(0x0fe6, 0x9700),    /* SR9700_ANDROID device */
+        .driver_info = (unsigned long)&sr9700_android_info,
+        },
+       {},                     // END
+};
+
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver sr9700_android_driver = {
+       .name = "SR9700_android",
+       .id_table = products,
+       .probe = usbnet_probe,
+       .disconnect = usbnet_disconnect,
+       .suspend = usbnet_suspend,
+       .resume = usbnet_resume,
+};
+
+static int __init sr9700_android_init(void)
+{
+       return usb_register(&sr9700_android_driver);
+}
+
+static void __exit sr9700_android_exit(void)
+{
+       usb_deregister(&sr9700_android_driver);
+}
+
+module_init(sr9700_android_init);
+module_exit(sr9700_android_exit);
+
+MODULE_AUTHOR("jokeliu <jokeliu@163.com>");
+MODULE_DESCRIPTION("SR9700 one chip USB 2.0 ethernet devices on android platform");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/sr9700.h b/drivers/net/usb/sr9700.h
new file mode 100755 (executable)
index 0000000..8a198d3
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ *  Copyright (c) 2009 jokeliu@163.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.
+ *
+ * Author : jokeliujl <jokeliu@163.com>
+ * Date : 2010-10-01
+ */
+
+/* sr9700 spec. register table on android platform */
+/* Registers */
+#define        NCR                     0x00
+#define        NSR                     0x01
+#define        TCR                     0x02
+#define        TSR1            0x03
+#define        TSR2            0x04
+#define        RCR                     0x05
+#define        RSR                     0x06
+#define        ROCR            0x07
+#define        BPTR            0x08
+#define        FCTR            0x09
+#define        FCR                     0x0A
+#define        EPCR            0x0B
+#define        EPAR            0x0C
+#define        EPDR            0x0D    // 0x0D ~ 0x0E
+#define        WCR                     0x0F
+#define        PAR                     0x10
+#define        MAR                     0x16
+#define        PRR                     0x1F
+#define        TWPAL           0x20
+#define        TWPAH           0x21
+#define        TRPAL           0x22
+#define        TRPAH           0x23
+#define        RWPAL           0x24
+#define        RWPAH           0x25
+#define        RRPAL           0x26
+#define        RRPAH           0x27
+#define        VID                     0x28
+#define        PID                     0x2A
+#define        CHIPR           0x2C
+#define        USBDA           0xF0
+#define        RXC                     0xF1
+#define        TXC_USBS        0xF2
+#define        USBC            0xF4
+
+/* Bit definition for registers */
+// Network Control Reg
+#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_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_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 1
+#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 2
+#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_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_AE          (1 << 2)
+#define        RSR_MF          (1 << 6)
+#define        RSR_RF          (1 << 7)
+// Recv Overflow Counter Reg
+#define        ROCR_ROC                (0x7F << 0)
+#define        ROCR_RXFU               (1 << 7)
+// Back Pressure Threshold Reg
+#define        BPTR_JPT        (0x0F << 0)
+#define        BPTR_BPHW       (0x0F << 4)
+// Flow Control Threshold Reg
+#define        FCTR_LWOT               (0x0F << 0)
+#define        FCTR_HWOT               (0x0F << 4)
+// rx/tx Flow Control Reg
+#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_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_EROA               (0x3F << 0)
+#define        EPAR_PHY_ADR    (0x03 << 6)
+// Wakeup Control Reg
+#define        WCR_MAGICST             (1 << 0)
+#define        WCR_LINKST              (1 << 2)
+#define        WCR_MAGICEN             (1 << 3)
+#define        WCR_LINKEN              (1 << 5)
+// Phy Reset Reg
+#define        PRR_PHY_RST             (1 << 0)
+// USB Device Address Reg
+#define        USBDA_USBFA     (0x7F << 0)
+// TX packet Counter & USB Status Reg
+#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 Reg
+#define        USBC_EP3NAK     (1 << 4)
+#define        USBC_EP3ACK     (1 << 5)
+
+/* Variables */
+#define        QF_RD_REGS              0x00
+#define        QF_WR_REGS              0x01
+#define        QF_WR_REG               0x03
+#define        QF_REQ_RD_REG   (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE)
+#define        QF_REQ_WR_REG   (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE)
+
+#define        QF_SHARE_TIMEOUT        1000
+#define        QF_EEPROM_LEN           256
+#define        QF_MCAST_SIZE           8
+#define        QF_MCAST_MAX            64
+#define        QF_TX_OVERHEAD          2       // 2bytes header
+#define        QF_RX_OVERHEAD          7       // 3bytes header + 4crc tail
+
+/*----------------------------------------------------------------------------------------------*/