/* ** Prolific: PL2302 USB-USB (12Mbps) Controller ** ** Copyright (c) 2001 Alex Tsao (alex@prolific.com.tw) ** ** ** ChangeLog: ** .... Most of the time spend reading sources & docs. */ /* * 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 */ #include #include #include #include #include #include #include #include #include #define BULK_IN_NUM 3 #define BULK_OUT_NUM 2 #define INT_NUM 1 #define CTRL_NUM 0 #define PL2302_MTU 1500 #define PL2302_MAX_MTU 1520 //1514+4(Len Header) #define PEER_EXIST 0x01 #define TX_REQUEST 0x02 #define RESET_IN 0x08 #define RESET_OUT 0x10 #define PL2302_TX_TIMEOUT (HZ*5) #define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) typedef unsigned char UCHAR; typedef __u16 USHORT; typedef __u32 ULONG; typedef struct pl2302 { struct usb_device *usb; struct net_device *net; struct net_device_stats stats; struct urb ctrl_urb, int_urb, rx_urb, tx_urb; devrequest dr; wait_queue_head_t ctrl_wait, int_wait; unsigned char ALIGN(rx_buff[PL2302_MAX_MTU]); unsigned char ALIGN(tx_buff[PL2302_MAX_MTU]); int int_status; //Interrupt int pid; //Thread ID } pl2302_t; static const char *version = __FILE__ ": v0.0.03 2001/05/02 (C) 2001 Alex Tsao (alex@prolific.com.tw)"; MODULE_AUTHOR("Alex Tsao "); MODULE_DESCRIPTION("PL2302 Prolific USB-USB driver"); static void int_callback( struct urb *urb ) { pl2302_t *pl2302 = urb->context; if( urb->status ) printk( "<1>Status:%x Int:%x\n", urb->status, pl2302->int_status ); } static void write_bulk_callback( struct urb *urb ) { pl2302_t *pl2302 = urb->context; if ( urb->status ) printk("<1>%s: TX status 0x%x", pl2302->net->name, urb->status); pl2302->net->trans_start = jiffies; netif_wake_queue( pl2302->net ); } static int rcv_routine( pl2302_t *pl2302 ) { __u32 iLen; struct sk_buff *skb; iLen = *((__u32*)pl2302->rx_buff); if( iLen > PL2302_MAX_MTU ) return 3; if( memcmp( &pl2302->rx_buff[10], pl2302->net->dev_addr, 6 ) == 0 ) { printk( "<1>Circular Packet\n" ); return 2; } if ( !(skb = dev_alloc_skb(iLen)) ) return 1; skb->dev = pl2302->net; eth_copy_and_sum(skb, &pl2302->rx_buff[4], iLen, 0); skb_put(skb, iLen); skb->protocol = eth_type_trans(skb, pl2302->net); netif_rx(skb); pl2302->stats.rx_packets++; pl2302->stats.rx_bytes += iLen; return 0; } static void read_bulk_callback( struct urb *urb ) { pl2302_t *pl2302 = urb->context; int count = urb->actual_length, res; if( !count ) return; switch ( urb->status ) { case USB_ST_NOERROR: if( count >= 42 ) rcv_routine( pl2302 ); break; default: printk( "<1>RX status 0x%x", urb->status ); return; } FILL_BULK_URB( &pl2302->rx_urb, pl2302->usb, usb_rcvbulkpipe(pl2302->usb, BULK_IN_NUM), pl2302->rx_buff, PL2302_MAX_MTU, read_bulk_callback, pl2302 ); if ( (res = usb_submit_urb(&pl2302->rx_urb)) ) printk( "<1>BulkInError:%d\n", res ); } static void ctrl_callback( urb_t *urb ) { pl2302_t *pl2302 = urb->context; if ( !pl2302 ) return; switch ( urb->status ) { case USB_ST_NOERROR: break; default: printk( "<1>Ctrl CallBack status 0x%x\n", urb->status); break; } wake_up_interruptible( &pl2302->ctrl_wait ); } static int set_ctrl_command( pl2302_t *pl2302, __u8 type, __u8 request, __u16 value, __u16 indx, __u16 len, __u8 *data ) { int ret; pl2302->dr.requesttype = type; pl2302->dr.request = request; pl2302->dr.value = cpu_to_le16(value); pl2302->dr.index = cpu_to_le16p(&indx); pl2302->dr.length = cpu_to_le16p(&len); pl2302->ctrl_urb.transfer_buffer_length = len; if( type & 0x80 ) //Rcv FILL_CONTROL_URB( &pl2302->ctrl_urb, pl2302->usb, usb_rcvctrlpipe(pl2302->usb,CTRL_NUM), (char *)&pl2302->dr, data, len, ctrl_callback, pl2302 ); else //Snd FILL_CONTROL_URB( &pl2302->ctrl_urb, pl2302->usb, usb_sndctrlpipe(pl2302->usb,CTRL_NUM), (char *)&pl2302->dr, data, len, ctrl_callback, pl2302 ); if ( (ret = usb_submit_urb( &pl2302->ctrl_urb )) ) { printk( "<1> BAD CTRL %d", ret); return ret; } interruptible_sleep_on( &pl2302->ctrl_wait ); return ret; } static int int_thread( void *context ) { pl2302_t *pl2302 = context; printk( "<1>Enter int_thread\n" ); while( pl2302->pid ) { FILL_INT_URB( &pl2302->int_urb, pl2302->usb, usb_rcvintpipe(pl2302->usb,INT_NUM), &pl2302->int_status, 1, int_callback, pl2302, 0 ); if( usb_submit_urb( &pl2302->int_urb ) != 0 ) printk( "<1>issue interrupt error\n" ); interruptible_sleep_on_timeout( &pl2302->int_wait, 2*HZ ); if( pl2302->int_status & TX_REQUEST ) set_ctrl_command( pl2302, 0x41, 0x01, TX_REQUEST, 0, 0, NULL ); if( pl2302->int_status & RESET_IN ) { set_ctrl_command( pl2302, 0x41, 0x01, RESET_IN, 0, 0, NULL ); usb_clear_halt( pl2302->usb, usb_rcvbulkpipe(pl2302->usb, BULK_IN_NUM) ); usb_unlink_urb( &pl2302->rx_urb ); FILL_BULK_URB( &pl2302->rx_urb, pl2302->usb, usb_rcvbulkpipe(pl2302->usb, BULK_IN_NUM), pl2302->rx_buff, PL2302_MAX_MTU, read_bulk_callback, pl2302 ); usb_submit_urb(&pl2302->rx_urb); } if( pl2302->int_status & RESET_OUT ) { set_ctrl_command( pl2302, 0x41, 0x01, RESET_OUT, 0, 0, NULL ); usb_clear_halt( pl2302->usb, usb_sndbulkpipe(pl2302->usb, BULK_OUT_NUM) ); usb_unlink_urb( &pl2302->tx_urb ); } } printk( "<1>Exit int_thread\n" ); return 0; } static int init_card( pl2302_t *pl2302 ) { __u8 mac[6]={0x00,0x50,0x77,0x01,0x00,0x00}; printk( "<1>init_card OK\n" ); // Do Reset set_ctrl_command( pl2302, 0x41, 0x03, TX_REQUEST, 0, 0, NULL ); set_ctrl_command( pl2302, 0x41, 0x03, RESET_IN, 0, 0, NULL ); set_ctrl_command( pl2302, 0x41, 0x03, RESET_OUT, 0, 0, NULL ); *((__u16*)&mac[4]) = jiffies; memcpy( pl2302->net->dev_addr, mac, 6 ); // Create Thread pl2302->pid = kernel_thread( int_thread, pl2302, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); printk( "<1>int_thread:%d\n", pl2302->pid ); mdelay( 50 ); set_ctrl_command( pl2302, 0x41, 0x03, PEER_EXIST, 0, 0, NULL ); return 0; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,48) static void pl2302_tx_timeout( struct net_device *net ) { pl2302_t *pl2302 = net->priv; if ( !pl2302 ) return; printk("<1>%s: Tx timed out.", net->name); pl2302->tx_urb.transfer_flags |= USB_ASYNC_UNLINK; usb_unlink_urb( &pl2302->tx_urb ); pl2302->stats.tx_errors++; } #endif static int write_queue( pl2302_t *pl2302, __u8 *buf, __u32 length ) { if( (length+4)%64 < 12 ) length += (12-(length+4)%64); // %64==0 padding *((__u32*)pl2302->tx_buff) = length; memcpy( &pl2302->tx_buff[4], buf, length ); FILL_BULK_URB( &pl2302->tx_urb, pl2302->usb, usb_sndbulkpipe(pl2302->usb, BULK_OUT_NUM), pl2302->tx_buff, PL2302_MAX_MTU, write_bulk_callback, pl2302 ); pl2302->tx_urb.transfer_buffer_length = 4+length; return usb_submit_urb(&pl2302->tx_urb); } static int pl2302_start_xmit( struct sk_buff *skb, struct net_device *net ) { pl2302_t *pl2302 = net->priv; int res; if( pl2302->int_status & PEER_EXIST ) { netif_stop_queue( net ); if( (res = write_queue( pl2302, skb->data, skb->len )) ) { printk("<1>failed tx_urb %d", res); pl2302->stats.tx_errors++; netif_start_queue( net ); } else { pl2302->stats.tx_packets++; pl2302->stats.tx_bytes += skb->len; net->trans_start = jiffies; } } dev_kfree_skb(skb); return 0; } static struct net_device_stats *pl2302_netdev_stats( struct net_device *dev ) { printk( "<1>Statistics\n" ); return &((pl2302_t *)dev->priv)->stats; } static int pl2302_open(struct net_device *net) { pl2302_t *pl2302 = (pl2302_t *)net->priv; __u8 arp[42]={ 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x50,0x77,0x01,0x00,0x00,0x08,0x06, 0x00,0x01,0x08,0x00,0x06,0x04,0x00,0x01,0x00,0x50,0x77,0x01,0x00,0x00, 0x64,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; __u32 addr; //IP address printk( "<1>Open\n" ); MOD_INC_USE_COUNT; // First Bulk In FILL_BULK_URB( &pl2302->rx_urb, pl2302->usb, usb_rcvbulkpipe(pl2302->usb, BULK_IN_NUM), pl2302->rx_buff, PL2302_MAX_MTU, read_bulk_callback, pl2302 ); usb_submit_urb(&pl2302->rx_urb); netif_start_queue( net ); addr = ((struct in_device*)net->ip_ptr)->ifa_list->ifa_address; printk( "<1>IP:0x%x\n", addr ); memcpy( &arp[6], net->dev_addr, 6 ); memcpy( &arp[22], net->dev_addr, 6 ); memcpy( &arp[28], &addr, 4 ); write_queue( pl2302, arp, sizeof(arp) ); return 0; } static int pl2302_close( struct net_device *net ) { pl2302_t *pl2302 = net->priv; printk( "<1>Close\n" ); netif_stop_queue( net ); usb_unlink_urb( &pl2302->rx_urb ); usb_unlink_urb( &pl2302->tx_urb ); usb_unlink_urb( &pl2302->int_urb ); usb_unlink_urb( &pl2302->ctrl_urb ); MOD_DEC_USE_COUNT; return 0; } static void pl2302_set_multicast( struct net_device *net ) { printk( "<1>Multicast Count:%d Flags:0x%x\n", net->mc_count, net->flags ); } static void* pl2302_probe( struct usb_device *dev, unsigned int ifnum ) { struct net_device *net; pl2302_t *pl2302; printk( "<1>Enter Probe\n" ); if (dev->descriptor.idVendor != 0x067b || dev->descriptor.idProduct != 0x0001) return NULL; if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { printk("<1>usb_set_configuration() failed"); return NULL; } if(!(pl2302 = kmalloc(sizeof(struct pl2302), GFP_KERNEL))) { printk("<1>out of memory allocating device structure"); return NULL; } usb_inc_dev_use( dev ); memset(pl2302, 0, sizeof(struct pl2302)); init_waitqueue_head( &pl2302->ctrl_wait ); init_waitqueue_head( &pl2302->int_wait ); net = init_etherdev( NULL, 0 ); if ( !net ) { kfree( pl2302 ); return NULL; } pl2302->usb = dev; pl2302->net = net; net->priv = pl2302; net->open = pl2302_open; net->stop = pl2302_close; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,48) net->watchdog_timeo = PL2302_TX_TIMEOUT; net->tx_timeout = pl2302_tx_timeout; #endif net->hard_start_xmit = pl2302_start_xmit; net->set_multicast_list = pl2302_set_multicast; net->get_stats = pl2302_netdev_stats; net->mtu = PL2302_MTU; init_card( pl2302 ); printk( "<1>Exit:Probe:%s\n", net->name ); return pl2302; } static void pl2302_disconnect( struct usb_device *dev, void *ptr ) { struct pl2302 *pl2302 = ptr; printk( "<1>Disconnect\n" ); if ( !pl2302 ) { printk("<1>unregistering non-existant device"); return; } pl2302->pid = 0; unregister_netdev( pl2302->net ); usb_dec_dev_use( dev ); kfree( pl2302 ); pl2302 = NULL; } static struct usb_driver pl2302_driver = { name: "pl2302", probe: pl2302_probe, disconnect: pl2302_disconnect, }; int __init pl2302_init(void) { printk( "<1>Init:%s\n", version ); return usb_register( &pl2302_driver ); } void __exit pl2302_exit(void) { printk( "<1>Exit\n" ); usb_deregister( &pl2302_driver ); } module_init( pl2302_init ); module_exit( pl2302_exit );