[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#751106: Add support for CH9x00 USB to Network Adaptor (ch9x00)



Package: linux
Version: 3.2+46

I've recently ordered a USB to ethernet device. I discovered it was
not supported by the default debian linux kernel.:

$ dmesg
[...]
[1298859.872546] usb 4-1.3: USB disconnect, device number 83
[1298862.118668] usb 4-1.3: new full-speed USB device number 84 using ehci-pci
[1298862.213507] usb 4-1.3: New USB device found, idVendor=1a86, idProduct=e092
[1298862.213519] usb 4-1.3: New USB device strings: Mfr=1, Product=2,
SerialNumber=0
[1298862.213526] usb 4-1.3: Product: CH9200 USB Ethernet Adapter

But `ifconfig -a` did not show any new interface.

The device came hopefully with a mini CD containing a GPL driver for
it. Since the code is GPL (attached to this email), I was wondering if
it could be added to the next debian kernel (or forwarded upstream?).

As per upstream steps are:

[...]
Note:
1. Please run as root
2. Supported linux kernel range from 2.6.x to 3.8.x
3. CH9x00 module depends on mii and usbnet modules
4. If you want complied this module in kernel, refer to followed
a. # cp ch9x00.c ~/2.6.25/driver/net/usb/
b. # cd ~/2.6.25/driver/net/usb/
c. modified Makefile and Kconfig for ch9x00.c

Install:
# make
# make load

Uninstall:
# make unload
[...]




Thanks
/*
 * 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" );

Reply to: