This is the mail archive of the
ecos-patches@sources.redhat.com
mailing list for the eCos project.
USB ethernet host driver support for 2.2 and 2.4 kernels
- From: David Smith <dsmith at redhat dot com>
- To: eCos Patches <ecos-patches at sources dot redhat dot com>, Bart Veer <bartv at ecoscentric dot com>, Clark Williams <williams at redhat dot com>
- Date: 19 Dec 2002 12:55:29 -0600
- Subject: 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)