summaryrefslogtreecommitdiff
path: root/drivers/net/usb/ch9x00.c
diff options
context:
space:
mode:
authorSrikant Patnaik2015-01-11 12:28:04 +0530
committerSrikant Patnaik2015-01-11 12:28:04 +0530
commit871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch)
tree8718f573808810c2a1e8cb8fb6ac469093ca2784 /drivers/net/usb/ch9x00.c
parent9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff)
downloadFOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz
FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2
FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized. Changes are basically to make it look like kernel structure.
Diffstat (limited to 'drivers/net/usb/ch9x00.c')
-rwxr-xr-xdrivers/net/usb/ch9x00.c852
1 files changed, 852 insertions, 0 deletions
diff --git a/drivers/net/usb/ch9x00.c b/drivers/net/usb/ch9x00.c
new file mode 100755
index 00000000..11ac3d50
--- /dev/null
+++ b/drivers/net/usb/ch9x00.c
@@ -0,0 +1,852 @@
+/*
+ * USB 10M/100M ethernet adapter
+ *
+ * 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, whther express or implied
+ */
+
+#include <linux/kernel.h>
+#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 <linux/slab.h>
+
+
+#define CH9x00_VID 0x1A86
+#define CH9x00_PID_8339 0x8339
+#define CH9x00_PID_E091 0xE091
+#define CH9x00_PID_E092 0xE092
+
+#define DRIVER_VERSION "29-May-2013"
+
+#define DEBUG_PRT //for debug
+#undef DEBUG_PRT
+
+#ifdef DEBUG_PRT
+#define dbg_prt(format, arg...) printk(KERN_DEBUG format "\n", ## arg)
+#else
+#define dbg_prt(format, arg...) do {} while (0)
+#endif
+
+/**** Reg and CMD definition for CH9x00_PID_E091 and CH9x00_PID_8339****/
+#define DEVICE_SPEED_10M 0x80
+#define DEVICE_MEDIA_CONNECTED 0x40
+#define DEVICE_DUPLEX_FULL 0x20
+#define DEVICE_PHY 0x10
+
+#define REQ_RD_REG (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE)
+#define REQ_WR_REG (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE)
+#define CTRL_TIMEOUT_MS 1000
+
+#define MCAST_MAX 0x40
+#define TX_OVERHEAD 0x04
+#define RX_OVERHEAD 0x04
+
+#define GET_MAC_ADDRESS 0x00
+#define SET_HASH_TABLE 0x04
+#define SET_PACKAGE_FILTER 0x05
+ #define RECEIVE_ALL 0x08
+ #define MULTIPKT_EN 0x04
+ #define BROADPKT_EN 0x02
+ #define HASH_MULTI_EN 0x01
+ #define CTRL_CLOSE 0x00
+ #define CTRL_OPEN 0x01
+#define SET_SPEED_DUPLEX 0x06
+ #define HALF_DUPLEX 0x00
+ #define FULL_DUPLEX 0x01
+ #define SPEED_10M 0x00
+ #define SPEED_100M 0x02
+ #define MT 0x00
+ #define AT 0x04
+#define SET_MAC_ADDRESS 0x07
+#define SET_MAC_WAKEUP_FRAME 0x08
+#define SET_WAKEUP_ENABLE 0x09
+ #define LINKCHG0_EN 0x01
+ #define LINKCHG1_EN 0x02
+ #define MAGICPKT_EN 0x04
+ #define WAKEUP1_EN 0x08
+ #define WAKEUP2_EN 0x10
+ #define WAKEUP3_EN 0x20
+ #define WAKEUP4_EN 0x40
+#define SET_FULL_DUPLEX_FLOW_CONTROL 0x0A
+#define SET_HALF_DUPLEX_FLOW_CONTROL 0x0B
+
+#define TEST_GET_MAC_ADDRESS
+#define TEST_SET_MAC_ADDRESS
+//#define TEST_SET_HASH
+//#define TEST_SET_PACKAGE_FILTER
+#define TEST_SET_SPEED_DUPLEX
+//#define TEST_SET_WAKEUP_ENABLE
+ #if defined(TEST_SET_WAKEUP_ENABLE)
+ #define WKE1_EN
+ #define WKE2_EN
+ #define WKE3_EN
+ #define WKE4_EN
+ #define MAGIC_EN
+ #define LIKCHG1_EN
+ #define LIKCHG0_EN
+ #endif
+
+
+/**** Reg and CMD definition for CH9x00_PID_E092 ****/
+// === constant define
+#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+
+#define MIN_PACKET sizeof(struct ethhdr)
+#define MAX_PACKET 32768
+
+#define TX_TIMEOUT_JIFFIES (5 * HZ)
+#define THROTTLE_JIFFIES (HZ / 8)
+#define UNLINK_TIMEOUT_MS 3
+
+// for vendor-specific control operations
+#define CONTROL_TIMEOUT_MS 1000
+
+// request
+#define REQUEST_READ 0x0E
+#define REQUEST_WRITE 0x0F
+
+#define REQUEST_TYPE_READ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER)
+#define REQUEST_TYPE_WRITE (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER)
+
+// address space
+// addr 00---63 : mii addr
+// addr 64---128 : mac addr
+// note : read/write must be in word (16 bit)
+
+#define MAC_REG_CTRL 64
+#define MAC_REG_STATUS 66
+#define MAC_REG_INTERRUPT_MASK 68
+#define MAC_REG_PHY_COMMAND 70
+#define MAC_REG_PHY_DATA 72
+#define MAC_REG_STATION_L 74
+#define MAC_REG_STATION_M 76
+#define MAC_REG_STATION_H 78
+#define MAC_REG_HASH_L 80
+#define MAC_REG_HASH_M1 82
+#define MAC_REG_HASH_M2 84
+#define MAC_REG_HASH_H 86
+#define MAC_REG_THRESHOLD 88
+#define MAC_REG_FIFO_DEPTH 90
+#define MAC_REG_PAUSE 92
+#define MAC_REG_FLOW_CONTROL 94
+
+// BIT
+// control register bit15 and bit13 reserve
+#define LOOPBACK (0x01 << 14)
+#define BASE100X (0x01 << 12)
+#define MBPS_10 (0x01 << 11)
+#define DUPLEX_MODE (0x01 << 10)
+#define PAUSE_FRAME (0x01 << 9)
+#define PROMISCUOUS (0x01 << 8)
+#define MULTICAST (0x01 << 7)
+#define BROADCAST (0x01 << 6)
+#define HASH (0x01 << 5)
+#define APPEND_PAD (0x01 << 4)
+#define APPEND_CRC (0x01 << 3)
+#define TRANSMITTER_ACTION (0x01 << 2)
+#define RECEIVER_ACTION (0x01 << 1)
+#define DMA_ACTION (0x01 << 0)
+
+// status register bit15-bit7 reserve
+#define ALIGNMENT (0x01 << 6)
+#define FIFO_OVER_RUN (0x01 << 5)
+#define FIFO_UNDER_RUN (0x01 << 4)
+#define RX_ERROR (0x01 << 3)
+#define RX_COMPLETE (0x01 << 2)
+#define TX_ERROR (0x01 << 1)
+#define TX_COMPLETE (0x01 << 0)
+
+// fifo depth register bit14 and bit6 reserve
+#define ETH_TXBD (0x01 << 15)
+#define ETN_TX_FIFO_DEPTH // bit13:8
+#define ETH_RXBD (0x01 << 7)// bit
+#define ETH_RX_FIFO_DEPTH // bit5:0
+
+
+// **************************************************
+int speed_status;
+int link_status;
+int duplex_status;
+int phy_status;
+
+
+static void ch9x00_async_cmd_callback( struct urb *urb )
+{
+ struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+
+ if( urb->status < 0 )
+ printk( KERN_DEBUG "%s() failed with %d\n", __FUNCTION__, urb->status);
+
+ kfree(req);
+ usb_free_urb(urb);
+}
+
+
+static void ch9x00_set_reg_async(
+ struct usbnet *dev,
+ unsigned char request,
+ unsigned char requesttype,
+ unsigned short value,
+ unsigned short index,
+ unsigned short size,
+ void *data )
+{
+ struct usb_ctrlrequest *req;
+ int ret;
+ struct urb *urb;
+
+ urb = usb_alloc_urb( 0, GFP_ATOMIC);
+ if(!urb) {
+ dev_dbg( &dev->udev->dev,
+ "Error allocation URB in write_cmd_async!\n");
+ return;
+ }
+
+ req = kmalloc( sizeof *req, GFP_ATOMIC);
+ if(!req) {
+ dev_err( &dev->udev->dev,
+ "Failed to allocate memory for control request\n");
+ goto out;
+ }
+
+ req->bRequestType = requesttype;
+ req->bRequest = request;
+ req->wValue = cpu_to_le16(value);
+ req->wIndex = cpu_to_le16(index);
+ req->wLength = cpu_to_le16(size);
+
+ usb_fill_control_urb( urb, dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ (void *)req, data, size, ch9x00_async_cmd_callback,req);
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if( ret < 0 ) {
+ dev_err( &dev->udev->dev,
+ "Error submitting the control message, ret:%d\n", ret );
+ goto out;
+ }
+ return;
+
+out:
+ kfree(req);
+ usb_free_urb(urb);
+}
+
+
+static void set_speed_duplex( struct usbnet *dev, unsigned char value_l )
+{
+ unsigned short value;
+ unsigned char value_low = 0x00;
+ unsigned char value_high = 0x00;
+
+ value_low = value_l;
+ value = value_low | (value_high << 8 );
+ ch9x00_set_reg_async( dev, SET_SPEED_DUPLEX, REQ_WR_REG, value, 0, 0, NULL);
+
+ return;
+}
+
+
+static int control_read( struct usbnet *dev,
+ unsigned char request, unsigned char requesttype,
+ unsigned short value, unsigned short index,
+ void *data, unsigned short size, int timeout )
+{
+ unsigned char *buf = NULL;
+ int err = 0;
+
+ dbg_prt("\n--> Control_read() index=0x%02x size=%d\n", index, size );
+
+ buf = kmalloc( size, GFP_KERNEL );
+ if( !buf ) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ err = usb_control_msg( dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ request, requesttype,
+ value, index,
+ buf, size, timeout);
+ if( err == size )
+ memcpy( data, buf, size );
+ else if( err >= 0 )
+ err = -EINVAL;
+ kfree(buf);
+
+ return err;
+
+err_out:
+ return err;
+}
+
+
+static int control_write( struct usbnet *dev,
+ unsigned char request, unsigned char requesttype,
+ unsigned short value, unsigned short index,
+ void *data, unsigned short size, int timeout )
+{
+ unsigned char *buf = NULL;
+ int err = 0;
+
+ dbg_prt("\n--> Control_write() index=0x%02x size=%d\n", index, size );
+
+ if( data ) {
+ buf = kmalloc( size, GFP_KERNEL );
+ if( !buf ) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ memcpy( buf, data, size );
+ }
+
+ err = usb_control_msg( dev->udev,
+ usb_sndctrlpipe( dev->udev, 0 ),
+ request, requesttype,
+ value, index,
+ buf, size, timeout );
+ if( err >= 0 && err < size )
+ err = -EINVAL;
+ kfree( buf );
+
+ return 0;
+
+err_out:
+ return err;
+}
+
+static int ch9x00_mdio_read( struct net_device *netdev,
+ int phy_id, int loc )
+{
+ struct usbnet *dev = netdev_priv(netdev);
+ int product_id = dev->udev->descriptor.idProduct;
+ __le16 res = 0x00; //for E091
+ unsigned char buff[2]; //for E092
+
+ dbg_prt("ch9x00_mdio_read phy_id:%02x loc:%02x\n", phy_id, loc);
+
+ if( product_id == CH9x00_PID_E091 ||
+ product_id == CH9x00_PID_8339 ) {
+ mutex_lock( &dev->phy_mutex );
+ if( phy_id == 0x00 ) {
+ switch(loc) {
+ case MII_BMCR: //Basic mode control register
+ {
+ if(speed_status == DEVICE_SPEED_10M){
+ //Do nothing here
+ }
+ else
+ res |= BMCR_SPEED100;
+
+ if(duplex_status == DEVICE_DUPLEX_FULL)
+ res |= BMCR_FULLDPLX;
+ else {
+ //Do nothing here
+ }
+
+ res |= BMCR_ANENABLE;
+ break;
+ }
+ case MII_BMSR: //Basic mode status register
+ {
+ if(link_status == DEVICE_MEDIA_CONNECTED) //up
+ res |= BMSR_LSTATUS;
+ else { //down
+ //Do nothing
+ }
+
+ if(speed_status == DEVICE_SPEED_10M) {
+ if(duplex_status == DEVICE_DUPLEX_FULL)
+ res |= BMSR_10FULL;
+ else
+ res |= BMSR_10HALF;
+ }
+ else {
+ if(duplex_status == DEVICE_DUPLEX_FULL)
+ res |= BMSR_100FULL;
+ else
+ res |= BMSR_100HALF;
+ }
+
+ break;
+ }
+ case MII_ADVERTISE: //Advertisement control reg
+ {
+ if(speed_status == DEVICE_SPEED_10M)
+ res |= ADVERTISE_10FULL;
+ else
+ res |= ADVERTISE_10HALF;
+
+ res |= 0x01; //IEEE 802.3
+ }
+ case MII_LPA: //Link partner ability reg
+ {
+ if(speed_status == DEVICE_SPEED_10M) {
+ if(duplex_status == DEVICE_DUPLEX_FULL)
+ res |= LPA_10FULL;
+ else
+ res |= LPA_10HALF;
+ }
+ else {
+ if(duplex_status == DEVICE_DUPLEX_FULL)
+ res |= LPA_100FULL;
+ else
+ res |= LPA_100HALF;
+ }
+
+ res |= 0x01; //IEEE 802.3
+ break;
+ }
+ case MII_EXPANSION: //Expansion register
+ break;
+ default:
+ break;
+ }
+ }
+ mutex_unlock( &dev->phy_mutex);
+ return le16_to_cpu(res);
+ }
+ else if( product_id == CH9x00_PID_E092 ) {
+ if( phy_id )
+ return 0;
+
+ control_read( dev, REQUEST_READ, REQUEST_TYPE_READ,
+ 0, loc*2, buff, 0x02, CONTROL_TIMEOUT_MS );
+
+ return ( buff[0] | buff[1] << 8 );
+ }
+
+ return 0;
+}
+
+
+static void ch9x00_mdio_write( struct net_device *netdev,
+ int phy_id, int loc, int val )
+{
+ struct usbnet *dev = netdev_priv(netdev);
+ int product_id = dev->udev->descriptor.idProduct;
+ unsigned char value_l = 0; //for E091
+ unsigned char buff[2]; //for E092
+
+ dbg_prt("ch9x00_mdio_write() phy_id=%02x loc:%02x\n", phy_id, loc);
+
+ if( product_id == CH9x00_PID_E091 ||
+ product_id == CH9x00_PID_8339 ) {
+ mutex_lock( &dev->phy_mutex );
+ if(phy_id == 0x00) {
+ switch(loc) {
+ case MII_BMCR: //Base mode control register
+ {
+ if( val & BMCR_ANRESTART ) {
+ //Do nothing
+ }
+
+ if( val & BMCR_ANENABLE ) {
+ value_l |= AT;
+ goto set;
+ }
+
+ if( val & BMCR_SPEED100 ) {
+ value_l |= SPEED_100M;
+ }
+
+ // bit 8 duplex mode 1 = full-duplex
+ if( val & BMCR_FULLDPLX ) {
+ value_l |= FULL_DUPLEX;
+ }
+set:
+ set_speed_duplex(dev, value_l);
+ break;
+ }
+ case MII_BMSR: //Basic mode status register
+ break;
+ case MII_ADVERTISE: //Advertisement control reg
+ break;
+ case MII_LPA: //Link partner ability reg
+ break;
+ case MII_EXPANSION: //Expansion register
+ break;
+ default:
+ break;
+ }
+ }
+ mutex_unlock( &dev->phy_mutex );
+ return;
+ }
+ else if( product_id == CH9x00_PID_E092 ) {
+ buff[0] = (unsigned char)val;
+ buff[1] = (unsigned char)(val >> 8);
+
+ if(phy_id)
+ return;
+
+ control_write( dev, REQUEST_WRITE, REQUEST_TYPE_WRITE,
+ 0, loc*2, buff, 0x02, CONTROL_TIMEOUT_MS );
+
+ }
+
+}
+
+
+static int ch9x00_link_reset( struct usbnet *dev )
+{
+ struct ethtool_cmd ecmd;
+
+ mii_check_media( &dev->mii, 1, 1 );
+ mii_ethtool_gset( &dev->mii, &ecmd );
+
+ dbg_prt("\nlink_reset() speed:%d duplex:%d \n", ecmd.speed, ecmd.duplex );
+
+ return 0;
+}
+
+
+static void ch9x00_status( struct usbnet *dev, struct urb *urb )
+{
+ int link;
+ unsigned char *buf;
+ int product_id = dev->udev->descriptor.idProduct;
+
+ if( product_id == CH9x00_PID_E091 ||
+ product_id == CH9x00_PID_8339 ) {
+ if( urb->actual_length < 8 )
+ return;
+
+ buf = urb->transfer_buffer;
+ link = !!(buf[0] & DEVICE_MEDIA_CONNECTED );
+ link_status = buf[0] & DEVICE_MEDIA_CONNECTED;
+ speed_status = buf[0] & DEVICE_SPEED_10M;
+ duplex_status = buf[0] & DEVICE_DUPLEX_FULL;
+ phy_status = buf[0] & DEVICE_PHY;
+
+ 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);
+ }
+ }
+ }
+ else if( product_id == CH9x00_PID_E092 ) {
+ if( urb->actual_length < 16 )
+ return;
+
+ buf = urb->transfer_buffer;
+ link = !!(buf[0] & 0x01);
+
+ if( link ) {
+ netif_carrier_on(dev->net);
+ usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+ }
+ else {
+ netif_carrier_off(dev->net);
+ }
+ }
+
+
+ return;
+}
+
+
+static struct sk_buff *ch9x00_tx_fixup(
+ struct usbnet *dev, struct sk_buff *skb, gfp_t flags )
+{
+ int i = 0;
+ int len = 0;
+ int tx_overhead = 0;
+ int product_id = dev->udev->descriptor.idProduct;
+
+ if( product_id == CH9x00_PID_E091 ||
+ product_id == CH9x00_PID_8339 )
+ tx_overhead = TX_OVERHEAD; //TX_OVERHEAD: 0x04
+ else if( product_id == CH9x00_PID_E092 )
+ tx_overhead = 0x40; //64
+
+ len = skb->len;
+ if( skb_headroom(skb) < tx_overhead ) {
+ struct sk_buff *skb2;
+
+ skb2 = skb_copy_expand(skb, tx_overhead, 0, flags);
+ dev_kfree_skb_any(skb);
+ skb = skb2;
+ if( !skb )
+ return NULL;
+ }
+
+ __skb_push(skb, 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++;
+ }
+
+ if( product_id == CH9x00_PID_E091 ||
+ product_id == CH9x00_PID_8339 ) {
+ skb->data[0] = len;
+ skb->data[1] = len >> 8;
+ skb->data[2] = 0x00;
+ skb->data[3] = 0x00;
+ }
+ else if( product_id == CH9x00_PID_E092 ) {
+ skb->data[0] = len;
+ skb->data[1] = len >> 8;
+ skb->data[2] = 0x00;
+ skb->data[3] = 0x80;
+
+ for( i = 4; i < 48; i++ )
+ skb->data[i] = 0x00;
+
+ skb->data[48] = len;
+ skb->data[49] = len >> 8;
+ skb->data[50] = 0x00;
+ skb->data[51] = 0x80;
+
+ for( i = 52; i < 64; i++ )
+ skb->data[i] = 0x00;
+ }
+
+ return skb;
+}
+
+
+static int ch9x00_rx_fixup( struct usbnet *dev, struct sk_buff *skb )
+{
+ int len = 0;
+ int rx_overhead = 0;
+ int product_id = dev->udev->descriptor.idProduct;
+
+ if( product_id == CH9x00_PID_E091 ||
+ product_id == CH9x00_PID_8339 ) {
+ rx_overhead = RX_OVERHEAD;
+
+ if( unlikely(skb->len < rx_overhead) ) {
+ dev_err( &dev->udev->dev, "unexpected tiny rx frame\n");
+ return 0;
+ }
+
+ len = (skb->data[0] | skb->data[1] << 8 );
+
+ skb_pull(skb, rx_overhead);
+ skb_trim(skb, len);
+ }
+ else if( product_id == CH9x00_PID_E092 ) {
+ // Do nothing here
+ rx_overhead = 64;
+
+ if( unlikely(skb->len < rx_overhead) ) {
+ dev_err( &dev->udev->dev, "unexpected tiny rx frame\n");
+ return 0;
+ }
+
+ len = (skb->data[skb->len - 16] | skb->data[skb->len - 15] << 8 );
+ /*printk("rx_fixup skb->len=%d, len=%d\n", skb->len, len );*/
+
+ /*skb_pull(skb, rx_overhead);*/
+ skb_trim(skb, len);
+ }
+
+ return 1;
+}
+
+
+static int get_mac_address( struct usbnet *dev, unsigned char *data )
+{
+ int err = 0;
+ //for E092
+ unsigned char mac_addr[0x06];
+ int rd_mac_len = 0;
+
+ dbg_prt("\n--> get_mac_address:\n\tusbnet VID:%0x PID:%0x\n",
+ dev->udev->descriptor.idVendor,
+ dev->udev->descriptor.idProduct);
+
+ if( dev->udev->descriptor.idProduct == CH9x00_PID_E091 ||
+ dev->udev->descriptor.idProduct == CH9x00_PID_8339 )
+ err = control_read( dev, GET_MAC_ADDRESS, REQ_RD_REG,
+ 0, 0, data, 0x06, CTRL_TIMEOUT_MS );
+ else if( dev->udev->descriptor.idProduct == CH9x00_PID_E092 ) {
+ memset( mac_addr, 0, sizeof(mac_addr) );
+ rd_mac_len = control_read( dev, REQUEST_READ, REQUEST_TYPE_READ,
+ 0, MAC_REG_STATION_L, mac_addr, 0x02, CONTROL_TIMEOUT_MS );
+ rd_mac_len += control_read( dev, REQUEST_READ, REQUEST_TYPE_READ,
+ 0, MAC_REG_STATION_M, mac_addr+2, 0x02, CONTROL_TIMEOUT_MS );
+ rd_mac_len += control_read( dev, REQUEST_READ, REQUEST_TYPE_READ,
+ 0, MAC_REG_STATION_H, mac_addr+4, 0x02, CONTROL_TIMEOUT_MS );
+ if( rd_mac_len != ETH_ALEN )
+ err = -EINVAL;
+ //Set MAC
+ data[0] = mac_addr[5];
+ data[1] = mac_addr[4];
+ data[2] = mac_addr[3];
+ data[3] = mac_addr[2];
+ data[4] = mac_addr[1];
+ data[5] = mac_addr[0];
+ }
+
+ if( err < 0 )
+ goto err_out;
+
+ return 0;
+
+err_out:
+ return err;
+}
+
+
+static int ch9x00_bind( struct usbnet *dev, struct usb_interface *intf )
+{
+ int retval = 0;
+ unsigned char data[2];
+
+ //int vendor_id = dev->udev->descriptor.idVendor;
+ int product_id = dev->udev->descriptor.idProduct;
+
+ retval = usbnet_get_endpoints(dev, intf);
+ if(retval)
+ goto err_out;
+
+ // compatibility of E091 and E092
+ if( product_id == CH9x00_PID_E091 ||
+ product_id == CH9x00_PID_8339 ) {
+ if( (retval = get_mac_address(dev, dev->net->dev_addr)) < 0 ) {
+ return retval;
+ goto err_out;
+ }
+ }
+
+ // Initialize MII structure for Setting Speed Duplex
+ dev->mii.dev = dev->net;
+ dev->mii.mdio_read = ch9x00_mdio_read;
+ dev->mii.mdio_write = ch9x00_mdio_write;
+ dev->mii.reg_num_mask = 0x1f;
+
+ if( product_id == CH9x00_PID_E091 ||
+ product_id == CH9x00_PID_8339 ) {
+ dev->mii.phy_id_mask = 0x3f;
+ /*dev->rx_urb_size = dev->net->mtu + 14 + RX_OVERHEAD;*/
+ // Note: Max package length=1518 (MTU + ETH_HELN + RX_OVERHEAD)
+ dev->rx_urb_size = dev->net->mtu + ETH_HLEN + RX_OVERHEAD;
+ // Adaptor can restart, when driver rmmoded or insmoded
+ mii_nway_restart( &dev->mii );
+ }
+ else if( product_id == CH9x00_PID_E092 ) {
+ dev->mii.phy_id_mask = 0x1f;
+
+ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
+ // Note: Max package legth= 24*64 + 16
+ dev->rx_urb_size = 24*64 + 16;
+ // Adaptor can restart, when driver rmmoded or insmoded
+ mii_nway_restart( &dev->mii );
+
+ // Initilization hardware
+ data[0] = 0x01;
+ data[1] = 0x0F;
+ retval = control_write( dev, REQUEST_WRITE, REQUEST_TYPE_WRITE,
+ 0, 88, data, 0x02, CONTROL_TIMEOUT_MS );
+
+ data[0] = 0xA0;
+ data[1] = 0x90;
+ retval = control_write( dev, REQUEST_WRITE, REQUEST_TYPE_WRITE,
+ 0, 90, data, 0x02, CONTROL_TIMEOUT_MS );
+
+ data[0] = 0x30;
+ data[1] = 0x00;
+ retval = control_write( dev, REQUEST_WRITE, REQUEST_TYPE_WRITE,
+ 0, 92, data, 0x02, CONTROL_TIMEOUT_MS );
+
+ data[0] = 0x17;
+ data[1] = 0xD8;
+ retval = control_write( dev, REQUEST_WRITE, REQUEST_TYPE_WRITE,
+ 0, 94, data, 0x02, CONTROL_TIMEOUT_MS );
+
+ data[0] = 0x01;
+ data[1] = 0x00;
+ retval = control_write( dev, REQUEST_WRITE, REQUEST_TYPE_WRITE,
+ 0, 254, data, 0x02, CONTROL_TIMEOUT_MS );
+
+ data[0] = 0x5F; // 0x0101 1111
+ data[1] = 0x0D; // 0x0000 1101
+ // Control Register Setting
+ retval = control_write( dev, REQUEST_WRITE, REQUEST_TYPE_WRITE,
+ 0, 64, data, 0x02, CONTROL_TIMEOUT_MS );
+
+ if( (retval = get_mac_address(dev, dev->net->dev_addr)) < 0 ) {
+ return retval;
+ goto err_out;
+ }
+ }
+ if( retval < 0 )
+ goto err_out;
+
+ return 0;
+
+err_out:
+ return retval;
+}
+
+
+static const struct driver_info ch9x00_info = {
+ .description = "CH9x00 USB to Network Adaptor",
+ .flags = FLAG_ETHER,
+ .bind = ch9x00_bind,
+ .rx_fixup = ch9x00_rx_fixup,
+ .tx_fixup = ch9x00_tx_fixup,
+ .status = ch9x00_status,
+ .link_reset = ch9x00_link_reset,
+ .reset = ch9x00_link_reset,
+};
+
+static const struct usb_device_id ch9x00_products[] = {
+ {
+ USB_DEVICE( 0x1A86, 0xE091 ),
+ .driver_info = (unsigned long)&ch9x00_info,
+ },
+ {
+ USB_DEVICE( 0x1A86, 0xE092 ),
+ .driver_info = (unsigned long)&ch9x00_info,
+ },
+ { USB_DEVICE( 0x1A86, 0x8339 ),
+ .driver_info = (unsigned long)&ch9x00_info,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE( usb, ch9x00_products );
+
+static struct usb_driver ch9x00_driver = {
+ .name = "ch9x00",
+ .id_table = ch9x00_products,
+ .probe = usbnet_probe,
+ .disconnect = usbnet_disconnect,
+ .suspend = usbnet_suspend,
+ .resume = usbnet_resume,
+};
+
+static int __init ch9x00_init( void )
+{
+ printk( KERN_INFO "\tCH9x00 Driver Version:%s\n", DRIVER_VERSION);
+ return usb_register( &ch9x00_driver );
+}
+
+static void __exit ch9x00_exit( void )
+{
+ usb_deregister( &ch9x00_driver );
+}
+
+module_init( ch9x00_init );
+module_exit( ch9x00_exit );
+
+MODULE_DESCRIPTION( "USB to Network adapter CH9x00" );
+MODULE_LICENSE( "GPL" );