diff options
author | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
commit | 871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch) | |
tree | 8718f573808810c2a1e8cb8fb6ac469093ca2784 /drivers/net/usb/ch9x00.c | |
parent | 9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff) | |
download | FOSSEE-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-x | drivers/net/usb/ch9x00.c | 852 |
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" ); |