--- linux/drivers/usb/Config.in.orig 2003-09-21 17:41:38.000000000 -0400 +++ linux/drivers/usb/Config.in 2003-09-21 17:01:24.000000000 -0400 @@ -101,6 +101,13 @@ dep_tristate ' USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL dep_tristate ' USB Communication Class Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL dep_tristate ' USB-to-USB Networking cables, Linux PDAs, ... (EXPERIMENTAL)' CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USBD Network (Encapsulated) Host-to-Host Link (EXPERIMENTAL)' CONFIG_USB_USBDNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + if [ ! "$CONFIG_USB_USBDNET" = "n" ]; then + hex ' USBD Network idVendor' CONFIG_USB_USBDNET_VENDOR "0000" + hex ' USBD Network idProduct' CONFIG_USB_USBDNET_PRODUCT "0000" + hex ' USBD Network Class' CONFIG_USB_USBDNET_CLASS "0000" + hex ' USBD Network SubClass' CONFIG_USB_USBDNET_SUBCLASS "0000" + fi fi comment 'USB port drivers' --- linux/drivers/usb/Makefile.orig 2003-09-21 17:41:53.000000000 -0400 +++ linux/drivers/usb/Makefile 2003-09-21 16:51:09.000000000 -0400 @@ -94,6 +94,7 @@ obj-$(CONFIG_USB_HPUSBSCSI) += hpusbscsi.o obj-$(CONFIG_USB_BLUETOOTH) += bluetooth.o obj-$(CONFIG_USB_USBNET) += usbnet.o +obj-$(CONFIG_USB_USBDNET) += usbdnet.o obj-$(CONFIG_USB_AUERSWALD) += auerswald.o obj-$(CONFIG_USB_BRLVGER) += brlvger.o obj-$(CONFIG_USB_LCD) += usblcd.o --- linux/drivers/usb/usbdnet.c.orig 2003-09-21 17:42:20.000000000 -0400 +++ linux/drivers/usb/usbdnet.c 2003-09-21 17:07:21.000000000 -0400 @@ -0,0 +1,1515 @@ +/* + * USB Host to USB Device Network Function Driver + * + * Copyright (c) 2000, 2001 Lineo + * Copyright(c) 2001 Hewlett-Packard + * + * 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. + * + * By: + * Stuart Lynne , Tom Rushworth + * + * Some algorithms adopted from usbnet.c: + * + * Copyright (C) 2000-2001 by David Brownell + * + */ + +#ifdef MODULE +#include +#else +#error MODULE not defined +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define MIN(a,b) (((a) < (b))?(a):(b)) +#define MAX(a,b) (((a) > (b))?(a):(b)) + +#define mutex_lock(x) down(x) +#define mutex_unlock(x) up(x) + + +#define DRIVER_VERSION "v0.4b" +#define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com" +#define DRIVER_DESC "USB Host to Device Network - for Linux USB Devices using MDLM/CDC" + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); + +/* Module Parameters ************************************************************************* */ + +#define MAX_INTERFACES 1 + +#define MAX_RCV_SKBS 10 +#define TIMEOUT_JIFFIES (4*HZ) +#define MAX_PACKET 32768 +#define MIN_PACKET sizeof(struct ethhdr) + + +#define TX_QLEN 3 +#define RX_QLEN 2 + +static DECLARE_MUTEX(usbd_mutex); // lock for global changes +static LIST_HEAD(usbd_list); // a list for all active devices + +/* struct private + * + * This structure contains the network interface and additional per USB device information. + * + * A pointer to this structure is used in two three places: + * + * net->priv + * urb->context + * skb->cb.priv + */ +struct private { + + // general + struct usb_device *usbdev; // usb core layer provides this for the probed device + struct semaphore mutex; // lock for changes to this structure + struct list_head list; // to maintain a list of these devices () + wait_queue_head_t *wait; + + struct tasklet_struct bh; + struct tq_struct ctrl_task; + struct tq_struct reset_task; + + // network + struct net_device net; + struct net_device_stats stats; + unsigned char dev_addr[ETH_ALEN]; + + // queues + struct sk_buff_head rxq; + struct sk_buff_head txq; + struct sk_buff_head unlink; + struct sk_buff_head done; + + // + int tx_endpoint; // transmit (OUT) endpoint + int rx_endpoint; // receive (IN) endpoint + int tx_pktsize; // transmit (OUT) size + int rx_pktsize; // receive (IN) size + + // + int crc32; // append and check for appended 32bit CRC + int padded; // pad bulk transfers such that (urb->transfer_buffer_length % tx_pktsize) == 1 + int addr_set; // set_address + + int timeouts; +}; + +/* struct skb_cb + * + * This defines how we use the skb->cb data area. It allows us to get back to the private + * data structure and track the current state of skb in our done queue. There is a pointer + * to the active urb so that it can be cancelled (e.g. tx_timeout). + * + * skb->cb + */ +typedef enum skb_state { + unknown = 0, + tx_start, // an skb in priv->txq + tx_done, // a transmitted skb in priv->done + rx_start, // an skb in priv->rxq + rx_done, // a received skb in priv->done + rx_cleanup, // a received skb being thrown out due to an error condition +} skb_state_t; + +struct skb_cb { + struct private *priv; + struct urb *urb; + skb_state_t state; + unsigned long int jiffies; +}; + +/* + * default MAC address to use + */ +static unsigned char default_addr[] = { + 0x40, 0x00, 0x00, 0x00, 0xff, 0x01 +}; + +/* Module Parameters ************************************************************************* */ + +#define VENDOR_SPECIFIC_CLASS 0xff +#define VENDOR_SPECIFIC_SUBCLASS 0xff +#define VENDOR_SPECIFIC_PROTOCOL 0xff + +#define MTU 1500+100 + + +#if defined(CONFIG_USBD_USBDNET_VENDOR) && !defined(CONFIG_USBD_USBDNET_PRODUCT) +#abort "USBDNET_VENDOR defined without USBDNET_PRODUCT" +#endif + +#define CDC_DEVICE_CLASS 0x02 // Device descriptor Class + +#define CDC_INTERFACE_CLASS 0x02 // CDC interface descriptor Class +#define CDC_INTERFACE_SUBCLASS 0x06 // CDC interface descriptor SubClass +#define MDLM_INTERFACE_SUBCLASS 0x0a // CDC interface descriptor SubClass + +#define DATA_INTERFACE_CLASS 0x0a // Data interface descriptor Class + +#define LINEO_INTERFACE_CLASS 0xff // Lineo private interface descriptor Class + +#define LINEO_INTERFACE_SUBCLASS_SAFENET 0x01 // Lineo private interface descriptor SubClass +#define LINEO_INTERFACE_SUBCLASS_SAFESERIAL 0x02 + +#define LINEO_SAFENET_CRC 0x01 // Lineo private interface descriptor Protocol +#define LINEO_SAFENET_CRC_PADDED 0x02 // Lineo private interface descriptor Protocol + +#define LINEO_SAFESERIAL_CRC 0x01 +#define LINEO_SAFESERIAL_CRC_PADDED 0x02 + +#if ! defined(CONFIG_USBD_USBDNET_VENDOR) +static __u16 vendor; // no default +static __u16 product; // no default +static __u16 class = LINEO_INTERFACE_CLASS; +static __u16 subclass = LINEO_INTERFACE_SUBCLASS_SAFENET; + +static __u16 echo_fcs; // no default +static __u16 noisy_fcs; // no default + +MODULE_PARM_DESC(vendor, "User specified USB idVendor)"); +MODULE_PARM_DESC(product, "User specified USB idProduct"); +MODULE_PARM_DESC(class, "User specified USB Class"); +MODULE_PARM_DESC(subclass, "User specified USB SubClass"); +MODULE_PARM(vendor, "i"); +MODULE_PARM(product, "i"); +MODULE_PARM(class, "i"); +MODULE_PARM(subclass, "i"); + +MODULE_PARM_DESC(echo_tx, "echo TX urbs"); +MODULE_PARM_DESC(echo_rx, "echo RCV urbs"); +MODULE_PARM_DESC(echo_fcs, "BAD FCS"); +MODULE_PARM_DESC(noisy_fcs, "BAD FCS info"); +MODULE_PARM(echo_tx, "i"); +MODULE_PARM(echo_rx, "i"); +MODULE_PARM(echo_fcs, "i"); +MODULE_PARM(noisy_fcs, "i"); +#endif + +#define MY_USB_DEVICE(vend,prod,dc,ic,isc) \ + match_flags: USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_CLASS | \ + USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS, \ + idVendor: (vend), \ + idProduct: (prod),\ + bDeviceClass: (dc),\ + bInterfaceClass: (ic), \ + bInterfaceSubClass: (isc), + + +static __devinitdata struct usb_device_id id_table [] = { + + // CDC devices Vend Prod bDeviceClass bInterfaceClass bInterfaceSubClass + { MY_USB_DEVICE( 0x49f, 0xffff, CDC_DEVICE_CLASS, CDC_INTERFACE_CLASS, CDC_INTERFACE_SUBCLASS) }, // Itsy + { MY_USB_DEVICE( 0x3f0, 0x2101, CDC_DEVICE_CLASS, CDC_INTERFACE_CLASS, CDC_INTERFACE_SUBCLASS) }, // Calypso + { MY_USB_DEVICE( 0x4dd, 0x8001, CDC_DEVICE_CLASS, CDC_INTERFACE_CLASS, CDC_INTERFACE_SUBCLASS) }, // Iris + { MY_USB_DEVICE( 0x4dd, 0x8002, CDC_DEVICE_CLASS, CDC_INTERFACE_CLASS, CDC_INTERFACE_SUBCLASS) }, // Iris + { MY_USB_DEVICE( 0x4dd, 0x8004, CDC_DEVICE_CLASS, CDC_INTERFACE_CLASS, CDC_INTERFACE_SUBCLASS) }, // Collie + + // MDLM devices Vend Prod bDeviceClass bInterfaceClass bInterfaceSubClass + { MY_USB_DEVICE( 0x4dd, 0x8003, CDC_DEVICE_CLASS, CDC_INTERFACE_CLASS, MDLM_INTERFACE_SUBCLASS) }, // Iris + + // Lineo Vend Prod bDeviceClass bInterfaceClass bInterfaceSubClass + { MY_USB_DEVICE( 0x49f, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFENET) }, // Itsy + { MY_USB_DEVICE( 0x3f0, 0x2101, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFENET) }, // Calypso + { MY_USB_DEVICE( 0x4dd, 0x8001, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFENET) }, // Iris + { MY_USB_DEVICE( 0x4dd, 0x8002, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFENET) }, // Iris + { MY_USB_DEVICE( 0x4dd, 0x8003, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFENET) }, // Iris + { MY_USB_DEVICE( 0x4dd, 0x8004, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFENET) }, // Collie + + + { MY_USB_DEVICE( 0x49f, 0x505e, 0xff, 0xff, 0xff) }, // Old IRIS + { MY_USB_DEVICE( 0x3f0, 0x2101, CDC_DEVICE_CLASS, 0, 0) }, // Calypso + +#if defined(CONFIG_USB_USBDNET_VENDOR) && CONFIG_USB_USBDNET_VENDOR > 0 + // A configured driver + { MY_USB_DEVICE( CONFIG_USB_USBDNET_VENDOR, CONFIG_USB_USBDNET_PRODUCT, CDC_DEVICE_CLASS, + CONFIG_USB_USBDNET_CLASS, CONFIG_USB_USBDNET_SUBCLASS) }, +#endif + + // extra null entry for module vendor/produc parameters + { MY_USB_DEVICE( 0, 0, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFENET) }, + + // terminating entry + { }, +}; + +MODULE_DEVICE_TABLE (usb, id_table); + +#define ECHO_FCS +#define ECHO_RCV + +#undef ECHO_TX_SKB +#define ECHO_TX_URB + +__u32 crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +#define CRC32_INITFCS 0xffffffff // Initial FCS value +#define CRC32_GOODFCS 0xdebb20e3 // Good final FCS value + +#define CRC32_FCS(fcs, c) (((fcs) >> 8) ^ crc32_table[((fcs) ^ (c)) & 0xff]) + +/* fcs_memcpy32 - memcpy and calculate fcs + * Perform a memcpy and calculate fcs using ppp 32bit CRC algorithm. + */ +static __u32 __inline__ fcs_memcpy32(unsigned char *dp, unsigned char *sp, int len, __u32 fcs) +{ + for (;len-- > 0; fcs = CRC32_FCS(fcs, *dp++ = *sp++)); + return fcs; +} + +/* fcs_pad32 - pad and calculate fcs + * Pad and calculate fcs using ppp 32bit CRC algorithm. + */ +static __u32 __inline__ fcs_pad32(unsigned char *dp, int len, __u32 fcs) +{ + for (;len-- > 0; fcs = CRC32_FCS(fcs, *dp++ = '\0')); + return fcs; +} + +/* fcs_compute32 - memcpy and calculate fcs + * Perform a memcpy and calculate fcs using ppp 32bit CRC algorithm. + */ +static __u32 __inline__ fcs_compute32(unsigned char *sp, int len, __u32 fcs) +{ + for (;len-- > 0; fcs = CRC32_FCS(fcs, *sp++)); + return fcs; +} + +void wait_for_sync(struct tq_struct *tq) +{ + + // wait for pending bottom halfs to exit + while (tq->sync) { + schedule_timeout(HZ); + } + + tq->data = 0; + + while (tq->sync) { + schedule_timeout(HZ); + } +} + + +#define RETRYTIME 2 + +void skb_bad_crc(struct sk_buff *skb, __u32 fcs) { +#ifdef ECHO_FCS + if (noisy_fcs) { + printk("skb_bad_crc: BAD FCS len: %4d crc: %08x last: %02x %02x %02x %02x\n", + skb->len, fcs, skb->data[skb->len-4], + skb->data[skb->len-3], skb->data[skb->len-2], skb->data[skb->len-1] + ); + } + if (echo_fcs) { + int i; + unsigned char *cp = skb->data; + printk(KERN_DEBUG "skb_bad_crc: FAILED skb: %p head: %p data: %p tail: %p len: %d", + skb, skb->head, skb->data, skb->tail, skb->len); + for (i = 0; i < skb->len; i++) { + if ((i%32)==0) { + printk("\nrcv[%02x]: ",i); + } + printk("%02x ", cp[i]); + } + printk("\n"); + printk("\n"); + } +#endif +} +#if 0 +static void dump_skb(struct sk_buff *skb, char *msg) +{ + int i; + unsigned char *cp = skb->data; + + printk(KERN_DEBUG"\n%s", msg); + for (i = 0; i < skb->len; i++) { + if (!(i%32)) { + printk("\n[%02x] ", i); + } + printk("%02x ", cp[i]); + } + printk("\n"); +} +#endif + +/* ********************************************************************************************* */ + +/* ********************************************************************************************* */ + +static void defer_skb(struct private *priv, struct sk_buff *skb, struct skb_cb *cb, skb_state_t state); +static int unlink_urbs(struct sk_buff_head *q); +static void urb_tx_complete(struct urb *urb); +static void urb_dead_complete(struct urb *urb); +static void urb_rx_complete(struct urb *urb); + + +/* ********************************************************************************************* */ +/* Network Support Functions - these are called by the network layer *************************** */ + +/* net_get_stats - network device get stats function + * Retreive network device stats structure. + */ +static struct net_device_stats * net_get_stats(struct net_device *net) +{ + struct private *priv = (struct private *) net->priv; + return &priv->stats; +} + +/* net_set_mac_addr - network device set mac address function + */ +static int net_set_mac_address(struct net_device *net, void *p) +{ + struct private *priv = (struct private *) net->priv; + struct sockaddr *addr = p; + + if (netif_running(net)) { + return -EBUSY; + } + memcpy(net->dev_addr, addr->sa_data, net->addr_len); + priv->addr_set = 1; + return 0; +} + +/* net_change_mtu - network device set config function + * Set MTU, if running we can only change it to something less + * than or equal to MTU when PVC opened. + */ +static int net_change_mtu (struct net_device *net, int mtu) +{ + printk(KERN_DEBUG"net_change_mtu:\n"); + if ((mtu < sizeof(struct ethhdr)) || (mtu > (MAX_PACKET - 4))) { + return -EINVAL; + } + if (netif_running(net)) { + if (mtu > net->mtu) { + return -EBUSY; + } + } + net->mtu = mtu; + return 0; +} + +/* net_open - called by network layer to open network interface + */ +static int net_open(struct net_device *net) +{ + struct private *priv = (struct private *) net->priv; + + mutex_lock(&priv->mutex); + + // tell the network layer to enable transmit queue + netif_start_queue(net); + + // call the bottom half to schedule some receive urbs + tasklet_schedule(&priv->bh); + + mutex_unlock(&priv->mutex); + return 0; +} + +/* net_stop - called by network layer to stop network interface + */ +static int net_stop(struct net_device *net) +{ + struct private *priv = (struct private *) net->priv; + + DECLARE_WAIT_QUEUE_HEAD(unlink_wakeup); + DECLARE_WAITQUEUE(wait, current); + + mutex_lock(&priv->mutex); + + // tell the network layer to disable the transmit queue + netif_stop_queue(net); + + // setup a wait queue - this also acts as a flag to prevent bottom half from allocating more urbs + add_wait_queue(&unlink_wakeup, &wait); + priv->wait = &unlink_wakeup; + + // move the tx and rx urbs into the done queue + unlink_urbs(&priv->txq); + unlink_urbs(&priv->rxq); + + // wait for done queue to empty + while( skb_queue_len(&priv->rxq) || skb_queue_len(&priv->txq) || skb_queue_len(&priv->done)) { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(TIMEOUT_JIFFIES*1000); + } + priv->wait = 0; + + // cleanup + remove_wait_queue(&unlink_wakeup, &wait); + + mutex_unlock(&priv->mutex); + + return 0; +} + + +/* net_tx_timeout - called by network layer to cancel outstanding skbs + */ +static void net_tx_timeout(struct net_device *net) +{ + struct private *priv = (struct private *) net->priv; + unsigned char *cp; + struct urb *urb; + + unsigned char deadbeef[] = "DEADBEEF"; + + unlink_urbs(&priv->txq); + tasklet_schedule(&priv->bh); + + // Attempt to send a short usb packet to the device, this will ensure that + // any partially completed bulk transfer will be terminated. + + if (!(cp = kmalloc(sizeof(deadbeef),GFP_KERNEL))) { + return; + } + if (!(urb = usb_alloc_urb(0))) { + kfree(cp); + return; + } + memcpy(cp, deadbeef, sizeof(deadbeef)); + + FILL_BULK_URB(urb, priv->usbdev, usb_sndbulkpipe(priv->usbdev, priv->tx_endpoint), + cp, sizeof(deadbeef), urb_dead_complete, NULL); + + urb->transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK | USB_NO_FSBR; + + //usb_endpoint_running(priv->usbdev, usb_pipeendpoint(priv->tx_endpoint), usb_pipeout(priv->tx_endpoint)); + //usb_settoggle(priv->usbdev, usb_pipeendpoint(priv->tx_endpoint), usb_pipeout(priv->tx_endpoint), 0); + + if (usb_submit_urb(urb)) { + kfree(cp); + urb->transfer_buffer = NULL; + usb_free_urb(urb); + } +} + +/* net_hard_start_xmit - called by network layer to transmit skb + */ +static int net_hard_start_xmit(struct sk_buff *skb, struct net_device *net) +{ + struct private *priv = (struct private *) net->priv; + struct urb *urb; + struct skb_cb *cb; + struct sk_buff *skb2; + int flags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; + __u32 fcs; + int length; + int pad; + + // allocate urb + if (priv->wait || !(urb = usb_alloc_urb(0))) { + dev_kfree_skb_any(skb); + printk(KERN_DEBUG"net_hard_start_xmit: DROP (wait or alloc)\n"); + return NET_XMIT_DROP; + } + + // calculate length required + if (priv->padded) { + // we need to pad so that after appending the CRC we have a multiple of packetsize less one + length = priv->tx_pktsize * ( ((skb->len + 4) / priv->tx_pktsize) + 1) - 1; + } + else { + // require a minimum of one full packet + length = MAX(priv->tx_pktsize, skb->len + 4); + } + + // allocate a new skb, copy data to it computing FCS, + // the extra bytes are for the CRC and optional pad byte + if (!(skb2 = alloc_skb(length + 1, flags))) { + usb_free_urb(urb); + dev_kfree_skb_any(skb); + printk(KERN_DEBUG"net_hard_start_xmit: DROP (alloc)\n"); + return NET_XMIT_DROP; + } + fcs = fcs_memcpy32(skb_put(skb2, skb->len), skb->data, skb->len, CRC32_INITFCS); + + //dump_skb(skb, "skb"); + dev_kfree_skb_any(skb); + skb = skb2; + + if ((pad = length - skb->len -4) > 0) { + // pad to required length less four (CRC), copy fcs and append pad byte if required + fcs = fcs_pad32(skb_put(skb, pad), pad, fcs); + } + + fcs = ~fcs; + *skb_put(skb, 1) = fcs&0xff; + *skb_put(skb, 1) = (fcs>>8)&0xff; + *skb_put(skb, 1) = (fcs>>16)&0xff; + *skb_put(skb, 1) = (fcs>>24)&0xff; + + //dump_skb(skb, "skb with CRC"); + + // append a byte if required, we overallocated by one to allow for this + if (!(skb->len % priv->tx_pktsize)) { + *skb_put(skb, 1) = 0; + } + + // hand urb off to usb layer + + cb = (struct skb_cb *) skb->cb; + cb->urb = urb; + cb->priv = priv; + cb->state = tx_start; + cb->jiffies = jiffies; + + // urb->context ends up with pointer to skb + FILL_BULK_URB(urb, priv->usbdev, usb_sndbulkpipe(priv->usbdev, priv->tx_endpoint), skb->data, skb->len, urb_tx_complete, skb); + + urb->transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK | USB_NO_FSBR; + urb->timeout = 8; + + + // submit the urb and restart (or not) the network device queue + netif_stop_queue(net); + if (usb_submit_urb(urb)) { + netif_start_queue(net); + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + usb_free_urb(urb); + } + skb_queue_tail(&priv->txq, skb); + if (priv->txq.qlen < TX_QLEN) { + netif_start_queue(net); + } + else { + net->trans_start = jiffies; + } + return NET_XMIT_SUCCESS; +} + + +/* Receive Related ***************************************************************************** */ + +/* rx_submit - queue an urb to receive data + */ +static void rx_submit(struct private *priv, struct urb *urb, int gpf) +{ + struct sk_buff *skb; + struct skb_cb *cb; + unsigned long flags; + int size = ((priv->net.mtu + 14+ 4 + priv->rx_pktsize) / priv->rx_pktsize) * priv->rx_pktsize; + + if (!(skb = alloc_skb (size, gpf))) { + dbg ("no rx skb"); + tasklet_schedule (&priv->bh); + usb_free_urb (urb); + return; + } + + cb = (struct skb_cb *) skb->cb; + cb->priv = priv; + cb->urb = urb; + cb->state = rx_start; + + // urb->context ends up with pointer to skb + FILL_BULK_URB (urb, priv->usbdev, usb_rcvbulkpipe (priv->usbdev, priv->rx_endpoint), skb->data, size, urb_rx_complete, skb); + + urb->transfer_flags |= USB_QUEUE_BULK; + + spin_lock_irqsave (&priv->rxq.lock, flags); + if (netif_running (&priv->net)) { + + if (usb_submit_urb (urb)) { + spin_unlock_irqrestore (&priv->rxq.lock, flags); + dbg ("%s rx submit, %d", priv->net.name, retval); + tasklet_schedule (&priv->bh); + dev_kfree_skb_any (skb); + usb_free_urb (urb); + } else { + __skb_queue_tail (&priv->rxq, skb); + } + spin_unlock_irqrestore (&priv->rxq.lock, flags); + } + else { + spin_unlock_irqrestore (&priv->rxq.lock, flags); + printk(KERN_DEBUG"rx_submit: stopped freeing urbg: %p\n", urb); + dbg ("rx: stopped"); + dev_kfree_skb_any (skb); + usb_free_urb (urb); + } +} + +/* urb_rx_complete - called by usb core layer when urb has been received + */ +static void urb_rx_complete(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct skb_cb *cb = (struct skb_cb *) skb->cb; + struct private *priv = cb->priv; + + + switch (urb->status) { + case 0: + priv->timeouts = 0; + + if ((MIN_PACKET < urb->actual_length) && (urb->actual_length < MAX_PACKET)) { + cb->urb = NULL; + skb_put(skb, urb->actual_length); + defer_skb(priv, skb, cb, rx_done); + if (netif_running(&priv->net)) { + rx_submit(priv, urb, GFP_ATOMIC); + } + break; + } + + /* FALLTHROUGH */ + + case -EOVERFLOW: + priv->stats.rx_over_errors++; + case -EILSEQ: + case -ECONNABORTED: + case -ETIMEDOUT: + priv->timeouts++; + priv->stats.rx_dropped++; + //printk(KERN_DEBUG"urb_rx_complete: RX_CLEANUP urb->status: %d timeout: %d\n", urb->status, priv->timeouts); + defer_skb(priv, skb, cb, rx_cleanup); + + // XXX provisional, this will attempt to force a reset for a device that + // there have been multiple receive timeouts. This is a host + // that is no longer responding to IN with a NAK. Typically this is + // due to a device that has stopped operation without dropping the + // usb control resistor to tell us. + + if (priv->timeouts > 20) { + priv->timeouts = 0; + printk(KERN_DEBUG"urb_rx_complete: scheduling reset task\n"); + + if (priv->reset_task.sync == 0) { + schedule_task(&priv->reset_task); + } + } + break; + } +} + + +/* bh_rx_process - called by bottom half to process received skb + */ +static inline void bh_rx_process(struct private *priv, struct sk_buff *skb) +{ + __u32 fcs; + +#if 0 + if (skb->len > (priv->net.mtu + 16 + 4 + 1 + 100)) { + printk(KERN_DEBUG"bh_rx_process: URB too large\n"); + priv->stats.rx_length_errors++; + dev_kfree_skb(skb); + priv->stats.rx_errors++; + return; + } +#endif + + // check if we need to check for extra byte + if ((skb->len % priv->rx_pktsize) == 1) { + + // check fcs across length minus one bytes + if ((fcs = fcs_compute32(skb->data, skb->len - 1, CRC32_INITFCS)) == CRC32_GOODFCS) { + // success, trim extra byte and fall through + skb_trim(skb, skb->len-1); + } + // failed, check additional byte + else if ((fcs = fcs_compute32(skb->data+skb->len - 1, 1, fcs)) != CRC32_GOODFCS) { + // failed + printk(KERN_DEBUG"bh_rx_process: CRC fail on extra byte\n"); + dev_kfree_skb(skb); + priv->stats.rx_errors++; + return; + } + // success fall through, possibly with corrected length + } + // normal check across full frame + else if ((fcs = fcs_compute32(skb->data, skb->len, CRC32_INITFCS)) != CRC32_GOODFCS) { + printk(KERN_DEBUG"bh_rx_process: CRC fail len: %d\n", skb->len); + dev_kfree_skb(skb); + priv->stats.rx_errors++; + return; + } + + // trim fcs + skb_trim(skb, skb->len-4); + + // push the skb up + memset(skb->cb, 0, sizeof(struct skb_cb)); + skb->dev = &priv->net; + skb->protocol = eth_type_trans(skb, &priv->net); + skb->pkt_type = PACKET_HOST; + skb->ip_summed = CHECKSUM_UNNECESSARY; + priv->stats.rx_packets++; + priv->stats.rx_bytes += skb->len; + if (netif_rx(skb)) { + printk(KERN_DEBUG "sal100_recv_urb: submitting skb failed\n"); + } +} + + +/* Transmit Related **************************************************************************** */ + +/* This is a version of usb_clear_halt() that doesn't read the status from + * the device -- this is because some devices crash their internal firmware + * when the status is requested after a halt + */ +static int local_clear_halt(struct usb_device *dev, int pipe) +{ + int result; + int endp = usb_pipeendpoint(pipe) | (usb_pipein(pipe) << 7); + + if ((result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, HZ * 3))) + { + return result; + } + + // reset the toggles and endpoint flags + // usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + // usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0); + + return 0; +} + +/* ctrl_task - called as kernel task to send clear halt message + */ +static void ctrl_task(void *data) +{ + struct private *priv = (struct private *) data; + + local_clear_halt(priv->usbdev, usb_sndbulkpipe(priv->usbdev, priv->tx_endpoint)); + netif_wake_queue(&priv->net); +} + +/* reset_task - called as kernel task to send reset the device + */ +static void reset_task(void *data) +{ + struct private *priv = (struct private *) data; + + if (usb_reset_device(priv->usbdev)) { + printk(KERN_DEBUG"reset_task: reset failed\n"); + } +} + +/* urb_tx_complete - called by usb core layer when network skb urb has been transmitted + */ +static void urb_tx_complete(struct urb *urb) +{ + struct sk_buff *skb; + + if (urb->status) { + printk(KERN_DEBUG"urb_tx_complete: urb: %p status: %d\n", urb, urb->status); + } + + urb->dev = 0; + + if ((skb = urb->context)) { + struct skb_cb *cb = (struct skb_cb *) skb->cb; + struct private *priv = cb->priv; + + if (urb->status == USB_ST_STALL) { + if (priv->ctrl_task.sync == 0) { + schedule_task(&priv->ctrl_task); + } + } + defer_skb(priv, skb, cb, tx_done); + } +} + +/* urb_dead_complete - called by usb core layer when deadbeef urb has been transmitted + */ +static void urb_dead_complete(struct urb *urb) +{ + urb->dev = 0; + + if (urb->transfer_buffer) { + kfree(urb->transfer_buffer); + urb->transfer_buffer = NULL; + } + usb_free_urb(urb); +} + + +/* Bottom Half ********************************************************************************* */ + +/* defer_skb - put an skb on done list and schedule bottom half if necessary + */ +static void defer_skb(struct private *priv, struct sk_buff *skb, struct skb_cb *cb, skb_state_t state) +{ + struct sk_buff_head *list = skb->list; + unsigned long flags; + + cb->state = state; + + // unlink from current queue + spin_lock_irqsave(&list->lock, flags); + __skb_unlink(skb, list); + spin_unlock(&list->lock); + + // link to done queue + spin_lock(&priv->done.lock); + __skb_queue_tail(&priv->done, skb); + + if (priv->done.qlen == 1) { + tasklet_schedule(&priv->bh); + } + spin_unlock_irqrestore(&priv->done.lock, flags); +} + + +/* unlink_urbs - tell usb core layer that we want it to abandon attempts to send/receive urbs + */ +static int unlink_urbs(struct sk_buff_head *q) +{ + struct sk_buff *skb; + int count = 0; + + // move from the current queue to the unlink queue + while((skb = skb_dequeue(q))) { + struct skb_cb *cb = (struct skb_cb *) skb->cb; + struct urb *urb = cb->urb; + struct private *priv = cb->priv; + int retval; + + // place them here until they can be processed after unlinking + skb_queue_tail(&priv->unlink, skb); + + printk(KERN_DEBUG"unlink_urbs: unlinking skb: %p len: %d jiffs: %ld\n", skb, skb->len, jiffies - cb->jiffies); + + // usb core layer will call rx_complete() with appropriate status so that we can remove + urb->transfer_flags |= USB_ASYNC_UNLINK; + if ( (retval = usb_unlink_urb (urb)) < 0) { + dbg ("unlink urb err, %d", retval); + } + else { + count++; + } + } + return count; +} + + +/* bh - bottom half + */ +static void bh(unsigned long data) +{ + struct private *priv = (struct private *)data; + struct sk_buff *skb; + + // process all skb's on the done queue + while((skb = skb_dequeue(&priv->done))) { + + struct skb_cb *cb = (struct skb_cb *) skb->cb; + struct urb *urb = cb->urb; + + switch(cb->state) { + case rx_done: + bh_rx_process(priv, skb); + break; + + case tx_done: + if (cb->urb->status) { + priv->stats.tx_errors++; + printk(KERN_DEBUG"usbdnet: bh skb: %p status: %d\n", skb, cb->urb->status); + } + else { + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + } + usb_free_urb(urb); + dev_kfree_skb(skb); + break; + + case rx_cleanup: + if (urb) { + usb_free_urb(urb); + } + dev_kfree_skb(skb); + break; + + case unknown: + case tx_start: + case rx_start: + printk(KERN_DEBUG"bh: UNKNOWN\n"); + printk(KERN_DEBUG"bh: inconsistant cb state: %d\n", cb->state); + break; + } + } + + // are we waiting for pending urbs to complete? + if (priv->wait) { + if (!(priv->txq.qlen + priv->rxq.qlen + priv->done.qlen + priv->unlink.qlen)) { + printk(KERN_DEBUG"bh: wakeup\n"); + wake_up(priv->wait); + } + } + + // do we need to queue up receive urbs? + else if (netif_running(&priv->net)) { + + while ((priv->rxq.qlen < RX_QLEN) && (priv->timeouts < 4)) { + struct urb *urb; + + // allocate an urb and use rx_submit to prepare and add it to the rxq + if ((urb = usb_alloc_urb(0))) { + rx_submit(priv, urb, GFP_ATOMIC); + } + else { + // we failed, schedule another run to try again + tasklet_schedule(&priv->bh); + } + } + + if (priv->txq.qlen < TX_QLEN) { + netif_wake_queue(&priv->net); + } + } +} + + +/* USB Functions - Probe and Disconnect ******************************************************** */ + +/* create_private - create private data structure and initialize network interface + */ +static struct private *create_private(int devnum) +{ + struct private *priv; + struct net_device *net; + + if (!(priv = kmalloc(sizeof(struct private),GFP_KERNEL))) { + return NULL; + } + + memset(priv, 0, sizeof(struct private)); + + net = &priv->net; + SET_MODULE_OWNER(net); + net->priv = priv; + strcpy(net->name, "usb%d"); + memcpy(priv->dev_addr, default_addr, ETH_ALEN); + + priv->dev_addr[ETH_ALEN-1] = (unsigned char) devnum; + + memcpy(net->dev_addr, default_addr, sizeof(default_addr)); + net->dev_addr[ETH_ALEN-1] = devnum; + + ether_setup(net); + + net->set_mac_address = net_set_mac_address; + net->hard_start_xmit = net_hard_start_xmit; + net->get_stats = net_get_stats; + net->change_mtu = net_change_mtu; + net->open = net_open; + net->stop = net_stop; + net->tx_timeout = net_tx_timeout; + net->watchdog_timeo = TIMEOUT_JIFFIES; + + if (register_netdev(&priv->net)) { + printk(KERN_DEBUG"create_private: register_netdef failed\n"); + kfree(priv); + return NULL; + } + + return priv; +} + +static struct usb_device_id *idp_search(int config, int idVendor, int idProduct, + int bDeviceClass, int bInterfaceClass, int bInterfaceSubClass) +{ + struct usb_device_id *idp; + + // search id_table for a match + for (idp = id_table; ; idp++) { + + // Vend Prod bDeviceClass bInterfaceClass bInterfaceSubClass + //{ MY_USB_DEVICE( 0x49f, 0xffff, CDC_DEVICE_CLASS, CDC_INTERFACE_CLASS, CDC_INTERFACE_SUBCLASS) }, // Itsy + + + printk(KERN_DEBUG "idp_search[%d]: " + "idVendor: %04x idProduct: %04x bDeviceClass %02x " + "bInterfaceClass: %02x bInterfaceSubclass: %02x\n", + config, idp->idVendor, idp->idProduct, idp->bDeviceClass, + idp->bInterfaceClass, idp->bInterfaceSubClass); + + // end of table + if ( !idp->idVendor && !idp->idProduct /*&& !idp->bDeviceClass */) { + break; + } + + // check for match + if ( (idp->idVendor == idVendor) && + (idp->idProduct == idProduct) && + (idp->bDeviceClass == bDeviceClass) && + (idp->bInterfaceClass == bInterfaceClass) && + (idp->bInterfaceSubClass == bInterfaceSubClass)) + { + printk(KERN_DEBUG"idp_search[%d]: MATCH\n", config); + return idp; + } + } + return NULL; +} + +// find first data interface in CDC configuration +static struct usb_interface_descriptor *interface_search(int configuration, struct usb_config_descriptor *config, + int classtype, int endpoints) +{ + int j; + + //printk(KERN_DEBUG"interface_search[%d]: bNumInterfaces: %d\n", configuration, config->bNumInterfaces); + + // iterate across interfaces to find data interface with endpoints + for (j = 0; j < config->bNumInterfaces; j++) { + + int k; + + for (k = 0; k < config->interface[j].num_altsetting; k++) { + struct usb_interface_descriptor *interface; + + if ((interface = config->interface[j].altsetting + k)) { + + printk(KERN_DEBUG"interface_search[%d:%d:%d]: bInterfaceClass: %d:%d bNumEndpoints: %d:%d\n", + configuration, j, k, classtype, interface->bInterfaceClass, endpoints, interface->bNumEndpoints + ); + + if ((interface->bInterfaceClass == classtype) && (interface->bNumEndpoints >= endpoints)) { + printk(KERN_DEBUG"interface_search[%d:%d:%d]: OK\n", configuration, j, k); + return interface; + } + } + } + } + return NULL; +} + +static struct usb_driver driver; + +static void *probe(struct usb_device *usbdev, unsigned int ifnum, const struct usb_device_id *id) +{ + int configuration; + int j; + struct private *priv; + struct usb_device_descriptor *device; + struct usb_config_descriptor *config = NULL; + + struct usb_endpoint_descriptor *rx_ep = NULL; + struct usb_endpoint_descriptor *tx_ep = NULL; + + struct usb_interface_descriptor *data_interface = NULL; + struct usb_interface_descriptor *cdc_interface = NULL; + struct usb_interface_descriptor *mdlm_interface = NULL; + struct usb_interface_descriptor *interface = NULL; + + int padded = 0; + int crc32 = 0; + + + printk(KERN_DEBUG"usbdnet: probe\n"); + + if ((device = &usbdev->descriptor)==NULL) { + printk(KERN_DEBUG "probe: device descriptor error\n"); + return NULL; + } + + // iterate across configurations looking for one we can use + for (configuration = 0; (configuration < device->bNumConfigurations); configuration++) { + struct usb_device_id *idp; + + mdlm_interface = cdc_interface = data_interface = NULL; + rx_ep = tx_ep = NULL; + + if (!(config = usbdev->config + configuration)) { + printk(KERN_DEBUG "probe[%d]: bad configuration\n", configuration); + continue; + } + + if ((interface = config->interface[0].altsetting)==NULL /*|| (interface->bNumEndpoints) > 2*/) { + printk(KERN_DEBUG "probe[%d]: bad interface\n", configuration); + continue; + } + + printk(KERN_DEBUG"probe: -> idVendor: %04x idProduct: %04x InterfaceClass %02x InterfaceSubclass: %02x\n", + device->idVendor, device->idProduct, interface->bInterfaceClass, interface->bInterfaceSubClass); + + if (!(idp = idp_search(configuration, device->idVendor, device->idProduct, device->bDeviceClass, + interface->bInterfaceClass, interface->bInterfaceSubClass))) + { + continue; + } + + //printk(KERN_DEBUG"probe: MATCHED\n"); + + // CDC Device? + if ( (device->bDeviceClass == CDC_DEVICE_CLASS) && (interface->bInterfaceClass == CDC_INTERFACE_CLASS) && + (interface->bInterfaceSubClass == CDC_INTERFACE_SUBCLASS)) + { + + //printk(KERN_DEBUG"probe[%d]: CDC Device\n", configuration); + if (!(cdc_interface = interface_search(configuration, config, CDC_INTERFACE_CLASS, 0))) { + continue; + } + if (!(data_interface = interface_search(configuration, config, DATA_INTERFACE_CLASS, 2))) { + continue; + } + printk(KERN_DEBUG"probe[%d]: CDC Device cdc_interface: %p data_interface %p\n", + configuration, cdc_interface, data_interface); + } + // MDLM Device? + else if ( (device->bDeviceClass == CDC_DEVICE_CLASS) && (interface->bInterfaceClass == CDC_INTERFACE_CLASS) && + (interface->bInterfaceSubClass == MDLM_INTERFACE_SUBCLASS)) + { + mdlm_interface = interface; + data_interface = interface; + printk(KERN_DEBUG"probe[%d]: MDLM Device data_interface %p\n", configuration, data_interface); + } + // Lineo private network device + else if ( (device->bDeviceClass == CDC_DEVICE_CLASS) && + (interface->bInterfaceClass == LINEO_INTERFACE_CLASS) && + (interface->bInterfaceSubClass == LINEO_INTERFACE_SUBCLASS_SAFENET) + ) + { + // do nothing + printk(KERN_DEBUG"probe[%d]: Safe Device\n", configuration); + data_interface = interface; + } + + // Old style Lineo private network device + else if ( (device->bDeviceClass == CDC_DEVICE_CLASS) && + (interface->bInterfaceClass == LINEO_INTERFACE_CLASS) && + (interface->bInterfaceSubClass == 0) + ) + { + // do nothing + printk(KERN_DEBUG"probe[%d]: Old Device\n", configuration); + data_interface = interface; + } + else { + // XXX even though we matched I don't think we can support this + printk(KERN_DEBUG"probe[%d]: Unknown Device\n", configuration); + data_interface = interface; + //continue; + } + + + // check for protocol + switch(data_interface->bInterfaceClass) { + + case DATA_INTERFACE_CLASS: + printk(KERN_DEBUG"usbdnet: CRC\n"); + crc32 = 1; + break; + + case CDC_INTERFACE_CLASS: + printk(KERN_DEBUG"usbdnet: padded\n"); + crc32 = 1; + padded = 1; + // XXX need to find and look at the MDLM descriptor + // to get crc/padding info + break; + + default: + printk(KERN_DEBUG"usbdnet: Default\n"); + crc32 = 1; + switch (data_interface->bInterfaceProtocol) { + case LINEO_SAFENET_CRC: + // XXX we should optionally do CRC + crc32 = 1; + printk(KERN_DEBUG"usbdnet: Safe CRC\n"); + break; + case LINEO_SAFENET_CRC_PADDED: + crc32 = 1; + padded = 1; + printk(KERN_DEBUG"usbdnet: Safe CRC_PADDED\n"); + break; + default: + printk(KERN_DEBUG "probe[%d]: incorrect bInterfaceProtocol: %02x\n", + configuration, data_interface->bInterfaceProtocol); + continue; + } + break; + } + + + // check for endpoint descriptions + for (j = 0; j < data_interface->bNumEndpoints; j++) { + struct usb_endpoint_descriptor *endpoint; + endpoint = data_interface->endpoint + j; + + printk(KERN_DEBUG"probe[%d:%d]: endpoint bmAttributes: %02x address: %02x\n", + configuration,j, endpoint->bmAttributes, endpoint->bEndpointAddress); + + switch (endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + break; + case USB_ENDPOINT_XFER_ISOC: + break; + case USB_ENDPOINT_XFER_BULK: + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)) { // RCV + rx_ep = endpoint; + printk(KERN_DEBUG"probe[%d:%d]: rx ep\n", configuration, j); + } + else { // TX + tx_ep = endpoint; + printk(KERN_DEBUG"probe[%d:%d]: tx ep\n", configuration, j); + } + break; + case USB_ENDPOINT_XFER_INT: + break; + } + } + + // verify that we found tx and rcv endpoints + if (rx_ep && tx_ep) { + printk(KERN_DEBUG "probe[%d]: FOUND MATCH data_interface: %p\n", configuration, data_interface); + break; + } + data_interface = NULL; + } + + printk(KERN_DEBUG"cdc_interface: %p mdlm_interface: %p data_interface: %p tx_ep: %p rx_ep: %p\n", + cdc_interface, mdlm_interface, data_interface, tx_ep, rx_ep); + + // see if we found suitable interface and endpoints + if (!data_interface || !tx_ep || !rx_ep) { + printk(KERN_DEBUG "probe[%d]: incorrect endpoint values\n", configuration); + return NULL; + } + + if (!(priv = create_private(usbdev->devnum))) { + printk(KERN_DEBUG "probe[%d]: failed to create private data structure\n", configuration); + return NULL; + } + priv->crc32 = crc32; + priv->padded = padded; + + printk(KERN_DEBUG"probe[%d]: setting configuration %d\n", configuration, config->bConfigurationValue); + + if (/*!padded &&*/ usb_set_configuration(usbdev, config->bConfigurationValue) < 0) { + printk(KERN_ERR "probe[%d]: set configuration failed\n", configuration); + // destroy the network interface and free the private storage + unregister_netdev(&priv->net); + kfree(priv); + return NULL; + } + + // keep us from being removed and claim the interface + usb_inc_dev_use(usbdev); + + //if (cdc_interface) { + // usb_driver_claim_interface(&driver, cdc_interface, priv); + //} + + + if (cdc_interface && usb_set_interface(usbdev, cdc_interface->bInterfaceNumber, cdc_interface->bAlternateSetting)) { + printk(KERN_ERR "probe[%d]: set cdc interface failed\n", configuration); + // destroy the network interface and free the private storage + unregister_netdev(&priv->net); + kfree(priv); + return NULL; + } + + if (usb_set_interface(usbdev, data_interface->bInterfaceNumber, data_interface->bAlternateSetting)) { + printk(KERN_ERR "probe[%d]: set data interface failed\n", configuration); + // destroy the network interface and free the private storage + unregister_netdev(&priv->net); + kfree(priv); + return NULL; + } + + printk(KERN_DEBUG"probe[%d]: claiming interface(s)\n", configuration); + + usb_driver_claim_interface(&driver, config->interface, priv); + + printk(KERN_DEBUG"probe[%d]: setting interface %d alternate: %d\n", + configuration, data_interface->bInterfaceNumber, data_interface->bAlternateSetting); + + skb_queue_head_init(&priv->rxq); + skb_queue_head_init(&priv->txq); + skb_queue_head_init(&priv->unlink); + skb_queue_head_init(&priv->done); + + priv->tx_pktsize = tx_ep->wMaxPacketSize; + priv->rx_pktsize = rx_ep->wMaxPacketSize; + priv->tx_endpoint = tx_ep->bEndpointAddress; + priv->rx_endpoint = rx_ep->bEndpointAddress; + + printk(KERN_DEBUG "probe[%d]: rx_size: %3d tx_size: %3d\n", configuration, priv->rx_pktsize, priv->tx_pktsize); + printk(KERN_DEBUG "probe[%d]: rx_ep : %3x tx_ep : %3x\n", configuration, priv->rx_endpoint, priv->tx_endpoint); + + priv->usbdev = usbdev; + + // ctrl task for clear halt operation + priv->ctrl_task.routine = ctrl_task; + priv->ctrl_task.data = priv; + priv->ctrl_task.sync = 0; + + // reset task for reseting device + priv->reset_task.routine = reset_task; + priv->reset_task.data = priv; + priv->reset_task.sync = 0; + + // bottom half processing tasklet + priv->bh.func = bh; + priv->bh.data = (unsigned long)priv; + + // init mutex and list, add to device list + init_MUTEX_LOCKED(&priv->mutex); + INIT_LIST_HEAD(&priv->list); + + mutex_lock(&usbd_mutex); + list_add(&priv->list, &usbd_list); + mutex_unlock(&priv->mutex); + mutex_unlock(&usbd_mutex); + + printk(KERN_DEBUG "probe[%d]: success %s\n", configuration, DRIVER_VERSION); + + // start as if the link is up + netif_device_attach (&priv->net); + + return priv; +} + + +static void disconnect(struct usb_device *usbdev, void *ptr) +{ + struct private *priv = (struct private *) ptr; + + // unregister the network interface + unregister_netdev(&priv->net); + + // remove from device list + mutex_lock(&usbd_mutex); + mutex_lock(&priv->mutex); + list_del(&priv->list); + mutex_unlock(&usbd_mutex); + + // destroy the network interface and free the private storage + kfree(priv); + + // disable + usb_dec_dev_use(usbdev); + +} + +static struct usb_driver driver = { + name: "usbdnet", + probe: probe, + disconnect: disconnect, + id_table: id_table, +}; + + +/* Module loading functions - modinit and modexit*********************************************** */ + +/* + * modinit - module init + * + */ +static int __init modinit(void) +{ + printk(KERN_INFO DRIVER_VERSION " " DRIVER_AUTHOR "\n"); + + info(DRIVER_VERSION " " DRIVER_AUTHOR); + info(DRIVER_DESC); + + // if we have vendor / product parameters patch them into id list + if (vendor || product) { + int i; + printk(KERN_INFO"net: vendor: %x product: %x\n", vendor, product); + + for (i = 0; i < (sizeof(id_table) / sizeof(struct usb_device_id)); i++) { + if (!id_table[i].idVendor && !id_table[i].idProduct) { + id_table[i].idVendor = vendor; + id_table[i].idProduct = product; + id_table[i].bDeviceClass = class; + id_table[i].bDeviceSubClass = subclass; + break; + } + } + } + + // register us with the usb core layer + if (usb_register(&driver)) { + return -EINVAL; + } + + return 0; +} + + +/* + * function_exit - module cleanup + * + */ +static void __exit modexit(void) +{ + // de-register from the usb core layer + usb_deregister(&driver); +} + +module_init(modinit); +module_exit(modexit); + + --- linux/drivers/usb/serial/Config.in.orig 2003-09-21 17:42:44.000000000 -0400 +++ linux/drivers/usb/serial/Config.in 2003-09-21 16:51:09.000000000 -0400 @@ -39,6 +39,12 @@ dep_tristate ' USB REINER SCT cyberJack pinpad/e-com chipcard reader (EXPERIMENTAL)' CONFIG_USB_SERIAL_CYBERJACK $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL dep_tristate ' USB Xircom / Entregra Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_XIRCOM $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL dep_tristate ' USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL +dep_tristate ' USB Safe Serial (Encapsulated) Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_SAFE $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL + if [ ! "$CONFIG_USB_SERIAL_SAFE" = "n" ]; then + bool ' USB Secure Encapsulated Driver - Padded (EXPERIMENTAL)' CONFIG_USB_SERIAL_SAFE_PADDED + hex ' Safe Serial idVendor' CONFIG_USB_SAFE_SERIAL_VENDOR "0000" + hex ' Safe Serial idProduct' CONFIG_USB_SAFE_SERIAL_PRODUCT "0000" + fi fi endmenu --- linux/drivers/usb/serial/Makefile.orig 2003-09-21 17:42:58.000000000 -0400 +++ linux/drivers/usb/serial/Makefile 2003-09-21 16:55:55.000000000 -0400 @@ -26,9 +26,10 @@ obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o obj-$(CONFIG_USB_SERIAL_KOBIL_SCT) += kobil_sct.o +obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o # Objects that export symbols. -export-objs := usbserial.o +export-objs := usbserial.o safe_serial.o include $(TOPDIR)/Rules.make --- linux/drivers/usb/serial/safe_serial.c.orig 2003-09-21 17:43:15.000000000 -0400 +++ linux/drivers/usb/serial/safe_serial.c 2003-09-21 16:51:09.000000000 -0400 @@ -0,0 +1,467 @@ +/* + * Safe Encapsulated USB Serial Driver + * + * Copyright (c) 2001 Lineo + * Copyright(c) 2001 Hewlett-Packard + * + * 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. + * + * By: + * Stuart Lynne , Tom Rushworth + */ + +/* + * The encapsultaion is designed to overcome difficulties with some USB hardware. + * + * While the USB protocol has a CRC over the data while in transit, i.e. while + * being carried over the bus, there is no end to end protection. If the hardware + * has any problems getting the data into or out of the USB transmit and receive + * FIFO's then data can be lost. + * + * This protocol adds a two byte trailer to each USB packet to specify the number + * of bytes of valid data and a 10 bit CRC that will allow the receiver to verify + * that the entire USB packet was received without error. + * + * Because in this case the sender and receiver are the class and function drivers + * there is now end to end protection. + * + * There is an additional option that can be used to force all transmitted packets + * to be padded to the maximum packet size. This provides a work around for some + * devices which have problems with small USB packets. + * + * Assuming a packetsize of N: + * + * 0..N-2 data and optional padding + * + * N-2 bits 7-2 - number of bytes of valid data + * bits 1-0 top two bits of 10 bit CRC + * N-1 bottom 8 bits of 10 bit CRC + * + * + * | Data Length | 10 bit CRC | + * + 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 | 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 + + * + * The 10 bit CRC is computed across the sent data, followed by the trailer with + * the length set and the CRC set to zero. The CRC is then OR'd into the trailer. + * + * When received a 10 bit CRC is computed over the entire frame including the trailer + * and should be equal to zero. + * + * Two module parameters are used to control the encapsulation, if both are + * turned of the module works as a simple serial device with NO + * encapsulation. + * + * See linux/drivers/usbd/serial_fd for a device function driver + * implementation of this. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef CONFIG_USB_SERIAL_DEBUG +#define CONFIG_USB_SERIAL_DEBUG 0 +#endif +#ifndef CONFIG_USB_SAFE_PADDED +#define CONFIG_USB_SAFE_PADDED 0 +#endif + +static int debug = CONFIG_USB_SERIAL_DEBUG; +#include "usb-serial.h" // must follow the declaration of debug + +static int safe = 1; +static int padded = CONFIG_USB_SAFE_PADDED; + +#define DRIVER_VERSION "v0.0b" +#define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com" +#define DRIVER_DESC "USB Safe Encapsulated Serial" + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); + +#if defined(CONFIG_USBD_SAFE_SERIAL_VENDOR) && !defined(CONFIG_USBD_SAFE_SERIAL_PRODUCT) +#abort "SAFE_SERIAL_VENDOR defined without SAFE_SERIAL_PRODUCT" +#endif + +#if ! defined(CONFIG_USBD_SAFE_SERIAL_VENDOR) +static __u16 vendor; // no default +static __u16 product; // no default +MODULE_PARM(vendor, "i"); +MODULE_PARM(product, "i"); +MODULE_PARM_DESC(vendor, "User specified USB idVendor (required)"); +MODULE_PARM_DESC(product, "User specified USB idProduct (required)"); +#endif + +MODULE_PARM(debug, "i"); +MODULE_PARM(safe, "i"); +MODULE_PARM(padded, "i"); + +MODULE_PARM_DESC(debug, "Debug enabled or not"); +MODULE_PARM_DESC(safe, "Turn Safe Encapsulation On/Off"); +MODULE_PARM_DESC(padded, "Pad to full wMaxPacketSize On/Off"); + +#define CDC_DEVICE_CLASS 0x02 + +#define CDC_INTERFACE_CLASS 0x02 +#define CDC_INTERFACE_SUBCLASS 0x06 + +#define LINEO_INTERFACE_CLASS 0xff + +#define LINEO_INTERFACE_SUBCLASS_SAFENET 0x01 +#define LINEO_SAFENET_CRC 0x01 +#define LINEO_SAFENET_CRC_PADDED 0x02 + +#define LINEO_INTERFACE_SUBCLASS_SAFESERIAL 0x02 +#define LINEO_SAFESERIAL_CRC 0x01 +#define LINEO_SAFESERIAL_CRC_PADDED 0x02 + + +#define MY_USB_DEVICE(vend,prod,dc,ic,isc) \ + match_flags: USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_CLASS | \ + USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS, \ + idVendor: (vend), \ + idProduct: (prod),\ + bDeviceClass: (dc),\ + bInterfaceClass: (ic), \ + bInterfaceSubClass: (isc), + +static __devinitdata struct usb_device_id id_table [] = { + { MY_USB_DEVICE( 0x49f, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL) }, // Itsy + { MY_USB_DEVICE( 0x3f0, 0x2101, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL) }, // Calypso + { MY_USB_DEVICE( 0x4dd, 0x8001, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL) }, // Iris + { MY_USB_DEVICE( 0x4dd, 0x8002, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL) }, // Collie + { MY_USB_DEVICE( 0x4dd, 0x8003, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL) }, // Collie + { MY_USB_DEVICE( 0x4dd, 0x8004, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL) }, // Collie + { MY_USB_DEVICE( 0x5f9, 0xffff, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL) }, // Sharp tmp +#if defined(CONFIG_USB_SAFE_SERIAL_VENDOR) + { MY_USB_DEVICE( CONFIG_USB_SAFE_SERIAL_VENDOR, CONFIG_USB_SAFE_SERIAL_PRODUCT, CDC_DEVICE_CLASS, + LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL) }, +#endif + // extra null entry for module + // vendor/produc parameters + { MY_USB_DEVICE( 0, 0, CDC_DEVICE_CLASS, LINEO_INTERFACE_CLASS, LINEO_INTERFACE_SUBCLASS_SAFESERIAL) }, + { } // terminating entry +}; + +MODULE_DEVICE_TABLE (usb, id_table); + +__u16 crc10_table[256] = { + 0x000, 0x233, 0x255, 0x066, 0x299, 0x0aa, 0x0cc, 0x2ff, 0x301, 0x132, 0x154, 0x367, 0x198, 0x3ab, 0x3cd, 0x1fe, + 0x031, 0x202, 0x264, 0x057, 0x2a8, 0x09b, 0x0fd, 0x2ce, 0x330, 0x103, 0x165, 0x356, 0x1a9, 0x39a, 0x3fc, 0x1cf, + 0x062, 0x251, 0x237, 0x004, 0x2fb, 0x0c8, 0x0ae, 0x29d, 0x363, 0x150, 0x136, 0x305, 0x1fa, 0x3c9, 0x3af, 0x19c, + 0x053, 0x260, 0x206, 0x035, 0x2ca, 0x0f9, 0x09f, 0x2ac, 0x352, 0x161, 0x107, 0x334, 0x1cb, 0x3f8, 0x39e, 0x1ad, + 0x0c4, 0x2f7, 0x291, 0x0a2, 0x25d, 0x06e, 0x008, 0x23b, 0x3c5, 0x1f6, 0x190, 0x3a3, 0x15c, 0x36f, 0x309, 0x13a, + 0x0f5, 0x2c6, 0x2a0, 0x093, 0x26c, 0x05f, 0x039, 0x20a, 0x3f4, 0x1c7, 0x1a1, 0x392, 0x16d, 0x35e, 0x338, 0x10b, + 0x0a6, 0x295, 0x2f3, 0x0c0, 0x23f, 0x00c, 0x06a, 0x259, 0x3a7, 0x194, 0x1f2, 0x3c1, 0x13e, 0x30d, 0x36b, 0x158, + 0x097, 0x2a4, 0x2c2, 0x0f1, 0x20e, 0x03d, 0x05b, 0x268, 0x396, 0x1a5, 0x1c3, 0x3f0, 0x10f, 0x33c, 0x35a, 0x169, + 0x188, 0x3bb, 0x3dd, 0x1ee, 0x311, 0x122, 0x144, 0x377, 0x289, 0x0ba, 0x0dc, 0x2ef, 0x010, 0x223, 0x245, 0x076, + 0x1b9, 0x38a, 0x3ec, 0x1df, 0x320, 0x113, 0x175, 0x346, 0x2b8, 0x08b, 0x0ed, 0x2de, 0x021, 0x212, 0x274, 0x047, + 0x1ea, 0x3d9, 0x3bf, 0x18c, 0x373, 0x140, 0x126, 0x315, 0x2eb, 0x0d8, 0x0be, 0x28d, 0x072, 0x241, 0x227, 0x014, + 0x1db, 0x3e8, 0x38e, 0x1bd, 0x342, 0x171, 0x117, 0x324, 0x2da, 0x0e9, 0x08f, 0x2bc, 0x043, 0x270, 0x216, 0x025, + 0x14c, 0x37f, 0x319, 0x12a, 0x3d5, 0x1e6, 0x180, 0x3b3, 0x24d, 0x07e, 0x018, 0x22b, 0x0d4, 0x2e7, 0x281, 0x0b2, + 0x17d, 0x34e, 0x328, 0x11b, 0x3e4, 0x1d7, 0x1b1, 0x382, 0x27c, 0x04f, 0x029, 0x21a, 0x0e5, 0x2d6, 0x2b0, 0x083, + 0x12e, 0x31d, 0x37b, 0x148, 0x3b7, 0x184, 0x1e2, 0x3d1, 0x22f, 0x01c, 0x07a, 0x249, 0x0b6, 0x285, 0x2e3, 0x0d0, + 0x11f, 0x32c, 0x34a, 0x179, 0x386, 0x1b5, 0x1d3, 0x3e0, 0x21e, 0x02d, 0x04b, 0x278, 0x087, 0x2b4, 0x2d2, 0x0e1, + +}; + +#define CRC10_INITFCS 0x000 // Initial FCS value +#define CRC10_GOODFCS 0x000 // Good final FCS value +#define CRC10_FCS(fcs, c) ( (((fcs) << 8) & 0x3ff) ^ crc10_table[((fcs) >> 2) & 0xff] ^ (c)) + +/** + * fcs_compute10 - memcpy and calculate 10 bit CRC across buffer + * @sp: pointer to buffer + * @len: number of bytes + * @fcs: starting FCS + * + * Perform a memcpy and calculate fcs using ppp 10bit CRC algorithm. Return + * new 10 bit FCS. + */ +static __u16 __inline__ fcs_compute10(unsigned char *sp, int len, __u16 fcs) +{ + for (;len-->0; fcs = CRC10_FCS(fcs, *sp++)); + return fcs; +} + + +static void safe_read_bulk_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); + unsigned char *data = urb->transfer_buffer; + unsigned char length = urb->actual_length; + int i; + int result; + + //printk(KERN_DEBUG"safe_read_bulk_callback:\n"); + + if (!serial) { + dbg(__FUNCTION__ " - bad serial pointer, exiting"); + return; + } + + if (urb->status) { + dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status); + return; + } + + dbg("safe_read_bulk_callback length: %d", port->read_urb->actual_length); +#ifdef ECHO_RCV + { + int i; + unsigned char *cp = port->read_urb->transfer_buffer; + for (i = 0; i < port->read_urb->actual_length; i++) { + if ((i % 32) == 0) { + printk("\nru[%02x] ", i); + } + printk("%02x ", *cp++); + } + printk("\n"); + } +#endif + if (safe) { + __u16 fcs; + if (!(fcs = fcs_compute10(data, length, CRC10_INITFCS))) { + + int actual_length = data[length-2] >> 2; + + if (actual_length <= (length - 2)) { + + info(__FUNCTION__ " - actual: %d", actual_length); + + for (i = 0; i < actual_length; i++) { + tty_insert_flip_char(port->tty, data[i], 0); + } + tty_flip_buffer_push(port->tty); + } + else { + err(__FUNCTION__ " - inconsistant lengths %d:%d", actual_length, length); + } + } + else { + err(__FUNCTION__ " - bad CRC %x", fcs); + } + } + else { + for (i = 0; i < length; i++) { + tty_insert_flip_char(port->tty, data[i], 0); + } + tty_flip_buffer_push(port->tty); + } + + /* Continue trying to always read */ + FILL_BULK_URB(urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + urb->transfer_buffer, urb->transfer_buffer_length, + safe_read_bulk_callback, port); + + if ((result = usb_submit_urb(urb))) { + err(__FUNCTION__ " - failed resubmitting read urb, error %d", result); + } +} + +static int safe_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) +{ + struct usb_serial *serial = port->serial; + unsigned char *data; + unsigned long flags; + int result; + int i; + int packet_length; + + //printk(KERN_DEBUG"safe_write:\n"); + + dbg("safe_write port: %p %d urb: %p count: %d", port, port->number, port->write_urb, count); + + if (!port->write_urb) { + dbg(__FUNCTION__" - write urb NULL"); + return (0); + } + + dbg("safe_write write_urb: %d transfer_buffer_length", port->write_urb->transfer_buffer_length); + + if (!port->write_urb->transfer_buffer_length) { + dbg(__FUNCTION__" - write urb transfer_buffer_length zero"); + return (0); + } + if (count == 0) { + dbg(__FUNCTION__" - write request of 0 bytes"); + return (0); + } + if (port->write_urb->status == -EINPROGRESS) { + dbg (__FUNCTION__" - already writing"); + return (0); + } + + packet_length = port->bulk_out_size; // get max packetsize + + //spin_lock_irqsave (&port->port_lock, flags); // lock out interrupts + + i = packet_length - (safe ? 2 : 0); // get bytes to send + count = (count > i) ? i : count; + + + // get the data into the transfer buffer + data = port->write_urb->transfer_buffer; + memset(data, '0', packet_length); + + if (from_user) { + copy_from_user(data, buf, count); + } + else { + memcpy (data, buf, count); + } + + if (safe) { + __u16 fcs; + + // pad if necessary + if (!padded) { + packet_length = count + 2; + } + + // set count + data[packet_length - 2] = count << 2; + data[packet_length - 1] = 0; + + // compute fcs and insert into trailer + fcs = fcs_compute10(data, packet_length, CRC10_INITFCS); + data[packet_length - 2] |= fcs >> 8; + data[packet_length - 1] |= fcs & 0xff; + + // set length to send + port->write_urb->transfer_buffer_length = packet_length; + } + else { + port->write_urb->transfer_buffer_length = count; + } + + usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer); +#ifdef ECHO_TX + { + int i; + unsigned char *cp = port->write_urb->transfer_buffer; + for (i = 0; i < port->write_urb->transfer_buffer_length; i++) { + if ((i % 32) == 0) { + printk("\nsu[%02x] ", i); + } + printk("%02x ", *cp++); + } + printk("\n"); + } +#endif + port->write_urb->dev = serial->dev; + if ((result = usb_submit_urb(port->write_urb))) { + err(__FUNCTION__ " - failed submitting write urb, error %d", result); + //spin_unlock_irqrestore (&port->port_lock, flags); + return 0; + } + dbg(__FUNCTION__ " urb: %p submitted", port->write_urb); + + //spin_unlock_irqrestore (&port->port_lock, flags); + return (count); +} + + +static int safe_write_room (struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + + int room = 0; // Default: no room + + //printk(KERN_DEBUG"safe_write_room:\n"); + + if (port->write_urb->status != -EINPROGRESS) + room = port->bulk_out_size - (safe ? 2 : 0); + + if (room) { + dbg("safe_write_room returns %d", room); + } + + return (room); +} + +static int safe_startup (struct usb_serial *serial) +{ + //switch(serial->interface->descriptor.bInterfaceProtocol) { + switch(serial->interface->altsetting->bInterfaceProtocol) { + case LINEO_SAFESERIAL_CRC: + break; + case LINEO_SAFESERIAL_CRC_PADDED: + padded = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +struct usb_serial_device_type safe_device = { + name: "Safe", + id_table: id_table, + needs_interrupt_in: DONT_CARE, + needs_bulk_in: DONT_CARE, + needs_bulk_out: DONT_CARE, + num_interrupt_in: NUM_DONT_CARE, + num_bulk_in: NUM_DONT_CARE, + num_bulk_out: NUM_DONT_CARE, + num_ports: 1, + write: safe_write, + write_room: safe_write_room, + read_bulk_callback: safe_read_bulk_callback, + startup: safe_startup, +}; + +static int __init safe_init (void) +{ + int i; + + info(DRIVER_VERSION " " DRIVER_AUTHOR); + info(DRIVER_DESC); + printk(KERN_INFO"safe: vendor: %x product: %x safe: %d padded: %d\n", vendor, product, safe, padded); + + // if we have vendor / product parameters patch them into id list + if (vendor || product) { + printk(KERN_INFO"safe_net: vendor: %x product: %x\n", vendor, product); + + for (i = 0; i < (sizeof(id_table) / sizeof(struct usb_device_id)); i++) { + if (!id_table[i].idVendor && !id_table[i].idProduct) { + id_table[i].idVendor = vendor; + id_table[i].idProduct = product; + break; + } + } + } + + usb_serial_register (&safe_device); + + return 0; +} + +static void __exit safe_exit (void) +{ + usb_serial_deregister (&safe_device); +} + +module_init(safe_init); +module_exit(safe_exit); +