This is the mail archive of the ecos-patches@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

USB ethernet host driver support for 2.2 and 2.4 kernels


As promised a couple of months back, here's a patch where Clark Williams
and I took Bart's 2.4 kernel driver and merged it back with Bart's
original 2.2 kernel driver.

Index: ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos/packages/io/usb/eth/slave/current/ChangeLog,v
retrieving revision 1.8
diff -u -5 -p -r1.8 ChangeLog
--- ChangeLog	7 Aug 2002 19:19:11 -0000	1.8
+++ ChangeLog	19 Dec 2002 18:48:17 -0000
@@ -1,5 +1,15 @@
+2002-12-19  Bart Veer  <bartv@ecoscentric.com>
+2002-12-19  David Smith  <dsmith@redhat.com>
+2002-12-19  Clark Williams  <williams@redhat.com>
+
+	* host/ecos_usbeth.c:   Bart Veer had converted the USB host
+	driver to work with 2.4 kernels.  Clark Williams and David Smith
+	merged Bart's changes to create a USB host driver that will work
+	under a 2.2 kernel or a 2.4 kernel.  Tested under kernel versions
+	2.2.16-22 (RHL 7.0) and 2.4.18-18.7.x (RHL 7.3).
+
 2002-07-26  David Smith  <dsmith@redhat.com>
 
 	* host/ecos_usbeth.c (ecos_usbeth_probe):
 	Two changes to the code that checks to see if this is the correct
 	driver for the USB peripheral that was just loaded.  Fixed a bug
Index: host/ecos_usbeth.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/io/usb/eth/slave/current/host/ecos_usbeth.c,v
retrieving revision 1.6
diff -u -5 -p -r1.6 ecos_usbeth.c
--- host/ecos_usbeth.c	7 Aug 2002 19:19:22 -0000	1.6
+++ host/ecos_usbeth.c	19 Dec 2002 18:48:17 -0000
@@ -54,31 +54,49 @@
 #include <linux/etherdevice.h>
 
 #ifdef MODULE
 MODULE_AUTHOR("Bart Veer <bartv@redhat.com>");
 MODULE_DESCRIPTION("USB ethernet driver for eCos-based peripherals");
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,99)
+MODULE_LICENSE("GPL");
+#endif
 #endif
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,99)
 // This array identifies specific implementations of eCos USB-ethernet
 // devices. All implementations should add their vendor and device
 // details.
 
-typedef struct ecos_usbeth_impl {
+static struct usb_device_id ecos_usbeth_implementations[] = {
+    { USB_DEVICE(0x4242, 0x4242) },     // eCos example application,
+                                        // dummy vendor and product id's
+    { }                                 // Null terminator
+};
+
+MODULE_DEVICE_TABLE(usb, ecos_usbeth_implementations);
+#else
+typedef struct usb_device_id {
     const char* name;
     __u16       vendor;
     __u16       id;
-} ecos_usbeth_impl;
+} usb_device_id;
 
-const static ecos_usbeth_impl ecos_usbeth_implementations[] = {
+const static usb_device_id ecos_usbeth_implementations[] = {
     { "eCos ether",     0x4242, 0x4242 },
     { (const char*) 0,       0,      0 }
 };
 
+#endif
+
+typedef struct ecos_usbeth_hdr {
+    unsigned char	size[2];
+} ecos_usbeth_hdr __attribute__((packed));
 
 // Constants. These have to be kept in sync with the target-side
 // code.
-#define ECOS_USBETH_MAXTU                           1516
+#define ECOS_USBETH_HDR_SIZE                        2
+#define ECOS_USBETH_MAXTU                           1514 + ECOS_USBETH_HDR_SIZE
 #define ECOS_USBETH_MAX_CONTROL_TU                     8
 #define ECOS_USBETH_CONTROL_GET_MAC_ADDRESS         0x01
 #define ECOS_USBETH_CONTROL_SET_PROMISCUOUS_MODE    0x02
 
 // The main data structure. It keeps track of both the USB
@@ -96,29 +114,30 @@ typedef struct ecos_usbeth {
     struct usb_device*          usb_dev;
     struct net_device*          net_dev;
     struct net_device_stats     stats;
     struct urb                  rx_urb;
     struct urb                  tx_urb;
+    int                         tx_endpoint;
+    int                         rx_endpoint;
     unsigned char               rx_buffer[ECOS_USBETH_MAXTU];
     unsigned char               tx_buffer[ECOS_USBETH_MAXTU];
 } ecos_usbeth;
 
-
+static void ecos_usbeth_start_rx(ecos_usbeth* usbeth);
+    
 // open()
 // Invoked by the TCP/IP stack when the interface is brought up.
 // This just starts a receive operation.
 static int
 ecos_usbeth_open(struct net_device* net)
 {
     ecos_usbeth* usbeth = (ecos_usbeth*) net->priv;
-    int          res;
 
     netif_start_queue(net);
-    res = usb_submit_urb(&(usbeth->rx_urb));
-    if (0 != res) {
-        printk("ecos_usbeth: failed to start USB receives, %d\n", res);
-    }
+
+    ecos_usbeth_start_rx(usbeth);
+    
     MOD_INC_USE_COUNT;
     return 0;
 }
 
 // close()
@@ -130,22 +149,17 @@ ecos_usbeth_open(struct net_device* net)
 static int
 ecos_usbeth_close(struct net_device* net)
 {
     ecos_usbeth* usbeth = (ecos_usbeth*) net->priv;
 
-    if (0 != netif_running(net)) {
-        netif_stop_queue(net);
-        net->start = 0;
-
-        if (-EINPROGRESS == usbeth->rx_urb.status) {
-            usb_unlink_urb(&(usbeth->rx_urb));
-        }
-        if (-EINPROGRESS == usbeth->tx_urb.status) {
-            usb_unlink_urb(&(usbeth->tx_urb));
-        }
-        MOD_DEC_USE_COUNT;
-    }
+    netif_stop_queue(net);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+    net->start = 0;
+#endif
+    usb_unlink_urb(&(usbeth->rx_urb));
+    usb_unlink_urb(&(usbeth->tx_urb));
+    MOD_DEC_USE_COUNT;
     
     return 0;
 }
 
 // Reception.
@@ -157,11 +171,10 @@ ecos_usbeth_rx_callback(struct urb* urb)
 {
     ecos_usbeth*        usbeth  = (ecos_usbeth*) urb->context;
     struct net_device*  net     = usbeth->net_dev;
     struct sk_buff*     skb;
     int                 len;
-    int                 res;
 
     if (0 != urb->status) {
         // This happens numerous times during a disconnect. Do not
         // issue a warning, but do clear the status field or things
         // get confused when resubmitting.
@@ -176,18 +189,19 @@ ecos_usbeth_rx_callback(struct urb* urb)
     } else if (2 > urb->actual_length) {
         // With some hardware the target may have to send a bogus
         // first packet. Just ignore those.
 
     } else {
-        len = usbeth->rx_buffer[0] + (usbeth->rx_buffer[1] << 8);
-        if (len > (urb->actual_length - 2)) {
+	ecos_usbeth_hdr *usbeth_hdr = (ecos_usbeth_hdr *) usbeth->rx_buffer;
+        len = usbeth_hdr->size[0] + (usbeth_hdr->size[1] << 8);
+        if (len > (urb->actual_length - ECOS_USBETH_HDR_SIZE)) {
             usbeth->stats.rx_errors++;
             usbeth->stats.rx_length_errors++;
             printk("ecos_usbeth: warning, packet size mismatch, got %d bytes, expected %d\n",
                    urb->actual_length, len);
         } else {
-            skb = dev_alloc_skb(len + 2);
+            skb = dev_alloc_skb(len + ECOS_USBETH_HDR_SIZE);
             if ((struct sk_buff*)0 == skb) {
                 printk("ecos_usbeth: failed to alloc skb, dropping packet\n");
                 usbeth->stats.rx_dropped++;
             } else {
 #if 0
@@ -202,25 +216,35 @@ ecos_usbeth_rx_callback(struct urb* urb)
                     }
                     printk("--------------------------------------------------------------\n");
                 }
 #endif
                 skb->dev        = net;
-                eth_copy_and_sum(skb, &(usbeth->rx_buffer[2]), len, 0);
+                eth_copy_and_sum(skb, &(usbeth->rx_buffer[ECOS_USBETH_HDR_SIZE]), len, 0);
                 skb_put(skb, len);
                 skb->protocol   = eth_type_trans(skb, net);
                 netif_rx(skb);
                 usbeth->stats.rx_packets++;
                 usbeth->stats.rx_bytes += len;
             }
         }
     }
 
     if (0 != netif_running(net)) {
-        res = usb_submit_urb(&(usbeth->rx_urb));
-        if (0 != res) {
-            printk("ecos_usbeth: failed to restart USB receives after packet, %d\n", res);
-        }
+        ecos_usbeth_start_rx(usbeth);
+    }
+}
+
+static void
+ecos_usbeth_start_rx(ecos_usbeth* usbeth)
+{
+    int res;
+    
+    FILL_BULK_URB(&(usbeth->rx_urb), usbeth->usb_dev, usb_rcvbulkpipe(usbeth->usb_dev, usbeth->rx_endpoint),
+                  usbeth->rx_buffer, ECOS_USBETH_MAXTU, &ecos_usbeth_rx_callback, (void*) usbeth);
+    res = usb_submit_urb(&(usbeth->rx_urb));
+    if (0 != res) {
+        printk("ecos_usbeth: failed to restart USB receives after packet, %d\n", res);
     }
 }
 
 // start_tx().
 // Transmit a single packet. The relevant USB protocol requires a
@@ -242,64 +266,56 @@ ecos_usbeth_tx_callback(struct urb* urb)
 
 static int
 ecos_usbeth_start_tx(struct sk_buff* skb, struct net_device* net)
 {
     ecos_usbeth* usbeth = (ecos_usbeth*) net->priv;
+    ecos_usbeth_hdr* usbeth_hdr;
     int          res;
 
-    if ((skb->len + 2) > ECOS_USBETH_MAXTU) {
+    if ((skb->len + ECOS_USBETH_HDR_SIZE) > ECOS_USBETH_MAXTU) {
         printk("ecos_usbeth: oversized packet of %d bytes\n", skb->len);
         return 0;
     }
 
-    if (netif_queue_stopped(net)) {
-        // Another transmission already in progress.
-        // USB bulk operations should complete within 5s.
-        int current_delay = jiffies - net->trans_start;
-        if (current_delay < (5 * HZ)) {
-            return 1;
-        } else {
-            // There has been a timeout. Discard this message.
-            //printk("transmission timed out\n");
-            usbeth->stats.tx_errors++;
-            dev_kfree_skb(skb);
-            return 0;
-        }
-    }
+    netif_stop_queue(net);
 
     spin_lock(&usbeth->usb_lock);
-    usbeth->tx_buffer[0]        = skb->len & 0x00FF;
-    usbeth->tx_buffer[1]        = (skb->len >> 8) & 0x00FF;
-    memcpy(&(usbeth->tx_buffer[2]), skb->data, skb->len);
-    usbeth->tx_urb.transfer_buffer_length = skb->len + 2;
+    usbeth_hdr                 = (ecos_usbeth_hdr *)usbeth->tx_buffer;
+    usbeth_hdr->size[0]        = skb->len & 0x00FF;
+    usbeth_hdr->size[1]        = (skb->len >> 8) & 0x00FF;
+    memcpy(&(usbeth->tx_buffer[ECOS_USBETH_HDR_SIZE]), skb->data, skb->len);
+    FILL_BULK_URB(&(usbeth->tx_urb), usbeth->usb_dev, usb_sndbulkpipe(usbeth->usb_dev, usbeth->tx_endpoint),
+                  usbeth->tx_buffer, ECOS_USBETH_MAXTU, &ecos_usbeth_tx_callback, (void*) usbeth);
+    usbeth->tx_urb.transfer_buffer_length = skb->len + ECOS_USBETH_HDR_SIZE;
 
     // Some targets are unhappy about receiving 0-length packets, not
     // just sending them.
     if (0 == (usbeth->tx_urb.transfer_buffer_length % 64)) {
         usbeth->tx_urb.transfer_buffer_length++;
     }
+    
 #if 0
     {
         int i;
         printk("--------------------------------------------------------------\n");
-        printk("ecos_usbeth start_tx: len %d\n", skb->len + 2);
-        for (i = 0; (i < (skb->len + 2)) && (i < 128); i+= 8) {
+        printk("ecos_usbeth start_tx: len %d\n", skb->len + ECOS_USBETH_HDR_SIZE);
+        for (i = 0; (i < (skb->len + ECOS_USBETH_HDR_SIZE)) && (i < 128); i+= 8) {
             printk("tx  %x %x %x %x %x %x %x %x\n",
                    usbeth->tx_buffer[i], usbeth->tx_buffer[i+1], usbeth->tx_buffer[i+2], usbeth->tx_buffer[i+3],
                    usbeth->tx_buffer[i+4], usbeth->tx_buffer[i+5], usbeth->tx_buffer[i+6], usbeth->tx_buffer[i+7]);
         }
         printk("--------------------------------------------------------------\n");
     }
 #endif
     res = usb_submit_urb(&(usbeth->tx_urb));
     if (0 == res) {
-        netif_stop_queue(net);
         net->trans_start        = jiffies;
         usbeth->stats.tx_packets++;
         usbeth->stats.tx_bytes  += skb->len;
     } else {        
         printk("ecos_usbeth: failed to start USB packet transmission, %d\n", res);
+        netif_start_queue(net);
         usbeth->stats.tx_errors++;
     }
     
     spin_unlock(&usbeth->usb_lock);
     dev_kfree_skb(skb);
@@ -396,37 +412,36 @@ ecos_usbeth_ioctl(struct net_device* net
 // the disconnect function. Filling in the ecos_usbeth structure will,
 // amongst other things, register this as a network device driver.
 // The MAC address is obtained from the peripheral via a control
 // request.
 
+#if LINUX_KERNEL_CODE > KERNEL_VERSION(2,3,99)
+static void*
+ecos_usbeth_probe(struct usb_device* usbdev,
+                  unsigned int interface_id,
+                  const struct usb_device_id* id)
+#else
 static void*
-ecos_usbeth_probe(struct usb_device* usbdev, unsigned int interface_id)
+ecos_usbeth_probe(struct usb_device* usbdev,
+                  unsigned int interface_id)
+#endif
+
 {
     struct net_device*  net;
     ecos_usbeth*        usbeth;
     int                 res;
     unsigned char       MAC[6];
     unsigned char       dummy[1];
     int                 tx_endpoint = -1;
     int                 rx_endpoint = -1;
-    const ecos_usbeth_impl*   impl;
-    int                 found_impl = 0;
 
-    // See if this is the correct driver for this USB peripheral.
-    impl = ecos_usbeth_implementations;
-    while (impl->name != NULL) {
-        if ((usbdev->descriptor.idVendor  != impl->vendor) ||
-            (usbdev->descriptor.idProduct != impl->id)) {
-            found_impl = 1;
-            break;
-        }
-        impl++;
-    }
-    if (! found_impl) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+    if ((usbdev->descriptor.idVendor  != ecos_usbeth_implementations[1].vendor) ||
+        (usbdev->descriptor.idProduct != ecos_usbeth_implementations[1].id)) {
         return (void*) 0;
     }
-
+#endif
     // For now only support USB-ethernet peripherals consisting of a single
     // configuration, with a single interface, with two bulk endpoints.
     if ((1 != usbdev->descriptor.bNumConfigurations)  ||
         (1 != usbdev->config[0].bNumInterfaces) ||
         (2 != usbdev->config[0].interface[0].altsetting->bNumEndpoints)) {
@@ -449,10 +464,12 @@ ecos_usbeth_probe(struct usb_device* usb
     res = usb_set_configuration(usbdev, usbdev->config[0].bConfigurationValue);
     if (0 != res) {
         printk("ecos_usbeth: failed to set configuration, %d\n", res);
         return (void*) 0;
     }
+    usb_inc_dev_use(usbdev);
+    
     res = usb_control_msg(usbdev,
                           usb_rcvctrlpipe(usbdev, 0),
                           ECOS_USBETH_CONTROL_GET_MAC_ADDRESS,
                           USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN,
                           0,
@@ -460,10 +477,11 @@ ecos_usbeth_probe(struct usb_device* usb
                           (void*) MAC,
                           6,
                           5 * HZ);
     if (6 != res) {
         printk("ecos_usbeth: failed to get MAC address, %d\n", res);
+        usb_dec_dev_use(usbdev);
         return (void*) 0;
     }
     
     res = usb_control_msg(usbdev,
                           usb_sndctrlpipe(usbdev, 0),                           // pipe
@@ -479,28 +497,27 @@ ecos_usbeth_probe(struct usb_device* usb
     }
            
     usbeth = (ecos_usbeth*) kmalloc(sizeof(ecos_usbeth), GFP_KERNEL);
     if ((ecos_usbeth*)0 == usbeth) {
         printk("ecos_usbeth: failed to allocate memory for usbeth data structure\n");
+        usb_dec_dev_use(usbdev);
         return (void*) 0;
     }
     memset(usbeth, 0, sizeof(ecos_usbeth));
     
-    net                 = init_etherdev(0, 0);
+    net = init_etherdev(0, 0);
     if ((struct net_device*) 0 == net) {
+        usb_dec_dev_use(usbdev);
         kfree(usbeth);
         printk("ecos_usbeth: failed to allocate memory for net data structure\n");
         return (void*) 0;
     }
 
-    usbeth->usb_lock    = SPIN_LOCK_UNLOCKED;
-    usbeth->usb_dev     = usbdev;
-    FILL_BULK_URB(&(usbeth->tx_urb), usbdev, usb_sndbulkpipe(usbdev, tx_endpoint),
-                  usbeth->tx_buffer, ECOS_USBETH_MAXTU, &ecos_usbeth_tx_callback, (void*) usbeth);
-    FILL_BULK_URB(&(usbeth->rx_urb), usbdev, usb_rcvbulkpipe(usbdev, rx_endpoint),
-                  usbeth->rx_buffer, ECOS_USBETH_MAXTU, &ecos_usbeth_rx_callback, (void*) usbeth);
-    
+    usbeth->usb_lock            = SPIN_LOCK_UNLOCKED;
+    usbeth->usb_dev             = usbdev;
+    usbeth->tx_endpoint         = tx_endpoint;
+    usbeth->rx_endpoint         = rx_endpoint;
     usbeth->net_dev             = net;
     usbeth->target_promiscuous  = 0;
     
     net->priv                   = (void*) usbeth;
     net->open                   = &ecos_usbeth_open;
@@ -511,43 +528,38 @@ ecos_usbeth_probe(struct usb_device* usb
     net->get_stats              = &ecos_usbeth_netdev_stats;
     net->mtu                    = 1500; // ECOS_USBETH_MAXTU - 2;
     memcpy(net->dev_addr, MAC, 6);
     
     printk("eCos-based USB ethernet peripheral active at %s\n", net->name);
-    MOD_INC_USE_COUNT;
+    
     return (void*) usbeth;
 }
 
 // disconnect().
 // Invoked after probe() has recognized a device but that device
 // has gone away. 
 static void
 ecos_usbeth_disconnect(struct usb_device* usbdev, void* data)
 {
     ecos_usbeth* usbeth = (ecos_usbeth*) data;
+
     if (!usbeth) {
         printk("ecos_usbeth: warning, disconnecting unconnected device\n");
         return;
     }
-    if (0 != netif_running(usbeth->net_dev)) {
-        ecos_usbeth_close(usbeth->net_dev);
-    }
     unregister_netdev(usbeth->net_dev);
-    if (-EINPROGRESS == usbeth->rx_urb.status) {
-        usb_unlink_urb(&(usbeth->rx_urb));
-    }
-    if (-EINPROGRESS == usbeth->tx_urb.status) {
-        usb_unlink_urb(&(usbeth->tx_urb));
-    }
+    usb_dec_dev_use(usbdev);
     kfree(usbeth);
-    MOD_DEC_USE_COUNT;
 }
 
 static struct usb_driver ecos_usbeth_driver = {
     name:       "ecos_usbeth",
     probe:      ecos_usbeth_probe,
     disconnect: ecos_usbeth_disconnect,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,99)
+    id_table:   ecos_usbeth_implementations
+#endif
 };
 
 // init()
 // Called when the module is loaded. It just registers the device with
 // the generic USB code. Nothing else can really be done until


-- 
David Smith
dsmith@redhat.com
Red Hat, Inc.
http://www.redhat.com
256.217.0141 (direct)
256.837.0057 (fax)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]