Hi!
Well, I've rummaged through my USB device collection and found this
smartNIC thingy based on the CATC NetMate chip. And I thought there
should be a driver for it now that we support all the ADMtek Pegasus and
KLSI devices.
So I took the old Donald Becker's driver and gave it a facelift and
ported it to 2.4.5. And it seems to work. It's still very buggy
(triggers some asserts in the net code on unload, frees skbs from
interrupts), but transfers packets quite reliably, at least on my OHCI.
Here is the patch. Perhaps adding it at least to Alan's kernel would
make sense.
Anyone feel free to fix the bugs in the driver. :)
--
Vojtech Pavlik
SuSE Labs
diff -urN linux-2.4.5-ac12/Documentation/Configure.help
linux/Documentation/Configure.help
--- linux-2.4.5-ac12/Documentation/Configure.help Sun Jun 10 11:08:46 2001
+++ linux/Documentation/Configure.help Sun Jun 10 23:17:39 2001
@@ -12006,7 +12006,7 @@
The module will be called pegasus.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
-USB KLSI KL5USB101-based Ethernet device support '
+USB KLSI KL5USB101-based Ethernet device support
CONFIG_USB_KAWETH
Say Y here if you want to use one of the following 10Mbps only
USB Ethernet adapters based on the KLSI KL5KUSB101B chipset:
@@ -12022,7 +12022,7 @@
Kingston Technology USB Ethernet Adapter
Linksys USB10T
Mobility USB-Ethernet Adapter
- NetGear EA-101
+ NetGear EA-101
Peracom Enet and Enet2
Portsmith Express Ethernet Adapter
Shark Pocket Adapter
@@ -12035,6 +12035,19 @@
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called kaweth.o. If you want to compile it as a
+ module, say M here and read <file:Documentation/modules.txt>.
+
+USB CATC NetMate-based Ethernet device support
+CONFIG_USB_CATC
+ Say Y if you want to use one of the following 10Mbps USB Ethernet
+ device based on the EL1210A chip. Supported devices are:
+ CATC NetMate
+ CATC NetMate II
+ smartBridges smartNIC
+
+ This code is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called pegasus.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
USB Kodak DC-2xx Camera support
diff -urN linux-2.4.5-ac12/drivers/usb/Config.in linux/drivers/usb/Config.in
--- linux-2.4.5-ac12/drivers/usb/Config.in Sun Jun 10 11:09:04 2001
+++ linux/drivers/usb/Config.in Sun Jun 10 22:03:52 2001
@@ -74,6 +74,7 @@
dep_tristate ' PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB
$CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
dep_tristate ' USB ADMtek Pegasus-based ethernet device support (EXPERIMENTAL)'
CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
dep_tristate ' USB KLSI KL5USB101-based ethernet device support (EXPERIMENTAL)'
CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
+ dep_tristate ' USB CATC NetMate-based Ethernet driver (EXPERIMENTAL)'
+CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
dep_tristate ' USB Communication Class Ethernet driver (EXPERIMENTAL)'
CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
dep_tristate ' USB-to-USB Networking (NetChip, Prolific, ...) (EXPERIMENTAL)'
CONFIG_USB_USBNET $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL
diff -urN linux-2.4.5-ac12/drivers/usb/Makefile linux/drivers/usb/Makefile
--- linux-2.4.5-ac12/drivers/usb/Makefile Sun Jun 10 11:09:04 2001
+++ linux/drivers/usb/Makefile Sun Jun 10 22:03:15 2001
@@ -75,6 +75,7 @@
# network drivers
+obj-$(CONFIG_USB_CATC) += catc.o
obj-$(CONFIG_USB_CDCETHER) += CDCEther.o
obj-$(CONFIG_USB_KAWETH) += kaweth.o
obj-$(CONFIG_USB_PEGASUS) += pegasus.o
diff -urN linux-2.4.5-ac12/drivers/usb/catc.c linux/drivers/usb/catc.c
--- linux-2.4.5-ac12/drivers/usb/catc.c Thu Jan 1 01:00:00 1970
+++ linux/drivers/usb/catc.c Sun Jun 10 23:18:12 2001
@@ -0,0 +1,772 @@
+/*
+ * $Id: catc.c,v 2.0 2001/06/10 22:15:26 vojtech Exp $
+ *
+ * Copyright (c) 1999-2000 Donald Becker
+ * Copyright (c) 2001 Vojtech Pavlik
+ *
+ * CATC NetMate USB Ethernet driver
+ *
+ * Sponsored by TurboLinux
+ * Sponsored by Scyld
+ * Sponsored by SuSE
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <[EMAIL PROTECTED]>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#undef DEBUG
+
+#include <linux/usb.h>
+
+/*
+ * Version information
+ */
+
+#define DRIVER_VERSION "v2.0"
+#define DRIVER_AUTHOR "Vojtech Pavlik <[EMAIL PROTECTED]>, Donald Becker
+<[EMAIL PROTECTED]>"
+#define DRIVER_DESC "CATC NetMate USB Ethernet driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+/*
+ * Some defines
+ */
+
+#define LOOPBACK 0 /* Enable loopback in the device for debugging
+*/
+#define MULTICAST_FILTER 32 /* Maximum number of multicast addresses to
+filter */
+#define TX_TIMEOUT (2*HZ) /* Time in jiffies before concluding the
+transmitter is hung. */
+#define STATS_UPDATE (5*HZ) /* Minimum time in jiffies between reading
+statisitcs registers. */
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+#define TX_Q 4
+#define RX_Q 4
+#define TX_QUEUE_LEN 1
+
+/*
+ * The on-chip registers accessible through vendor-specific commands.
+ */
+
+enum register_offsets {
+ TxBufCount = 0x20,
+ RxBufCount = 0x21,
+ OpModes = 0x22,
+ TxQed = 0x23,
+ RxQed = 0x24,
+ RxFilter = 0x60,
+ StationAddr5 = 0x62,
+ StationAddr0 = 0x67,
+ LEDCtrl = 0x81,
+ MulticastFilter = 0x7a80, /* Memory address. */
+};
+
+/*
+ * Configuration is both in EtherCtrl and EtherCtrl2.
+ */
+
+enum {
+ RxAllMulticast = 0x02,
+ RxPromiscuous = 0x04
+};
+
+/*
+ * Operating mode bits and Rx filter bit in OpModes/RxFilter registers.
+ */
+
+enum op_mode_bits {
+ OpLoopback = 0x80,
+ OpWin95bugfix = 0x40,
+ OpTxMerge = 0x20,
+ OpRxMerge = 0x10,
+ OpLenInclude = 0x08,
+ Op3MemWaits = 0x03,
+};
+
+enum rx_filter_bits {
+ RxEnable=0x01,
+ RxPolarity=0x02,
+};
+
+struct netdev_private {
+ struct net_device *next_module; /* Link for devices of this type. */
+ struct net_device_stats stats;
+ long last_stats;
+ struct usb_device *usb_dev;
+
+ struct urb *rx_urb0, *rx_urb1, *tx_urb, *irq_urb, *ctrl_urb;
+ void *usb_tx_xfer, *usb_rx_xfer;
+
+ struct tx_context {
+ struct net_device *netdev;
+ struct sk_buff *skb;
+ void *buffer;
+ struct urb *urb;
+ } txcontext[TX_Q];
+ struct urb tx_urbs[TX_Q];
+
+ struct rx_context {
+ struct net_device *netdev;
+ struct sk_buff *skb;
+ void *buffer;
+ struct urb *urb;
+ } rxcontext[RX_Q];
+ struct urb *rx_urbs[RX_Q];
+
+ unsigned int cur_tx, dirty_tx;
+
+ unsigned int ctrl_out_pipe, tx_out_pipe, rx_in_pipe, irq_pipe;
+ unsigned int irq_period;
+ void *rxbuffer;
+
+/*
+ * Local copy of the registers.
+ * Note endian problem.
+ */
+
+ unsigned char cur_ether_ctrl[4], cur_multicast[64];
+ unsigned char new_ether_ctrl[4], new_multicast[64];
+
+ unsigned char tx_full; /* The Tx queue is full. */
+
+ unsigned char ctrl_msg[8];
+ unsigned char intr_buffer[8]; /* Interrupt pipe message. */
+};
+
+/*
+ * The upper byte of VALUE is a mask. Set bits in the mask leave the original
+ * value intact.
+ */
+
+static int catc_set_register(struct usb_device *usbdev, int regnum, int value)
+{
+ return usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0xFA, 0x40, value,
+regnum, 0, 0, 10);
+}
+
+static int start_hardware(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct usb_device *usbdev = np->usb_dev;
+ int i;
+
+ dbg("%s: Initializing the hardware.", dev->name);
+
+/*
+ * Send a reset message.
+ */
+
+ usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0xF4, 0x40, 0, 0, 0, 0,
+10);
+
+/*
+ * Rewrite the station address.
+ */
+
+ for (i = 0; i < 6; i++)
+ catc_set_register(np->usb_dev, StationAddr0 - i, dev->dev_addr[i]);
+
+ dbg("%s: Set the station address, now enabling.", dev->name);
+
+ catc_set_register(np->usb_dev, 0x25, 1); /* Maximum burst */
+ catc_set_register(np->usb_dev, LEDCtrl, 0x08);
+ catc_set_register(np->usb_dev, TxBufCount, 4);
+ catc_set_register(np->usb_dev, RxBufCount, 16);
+ catc_set_register(np->usb_dev, OpModes, OpLenInclude | Op3MemWaits | (LOOPBACK
+? OpLoopback:0));
+ catc_set_register(np->usb_dev, RxFilter, 0x1f | RxEnable);
+
+ dbg("%s: Filling the multicast list.", dev->name);
+
+/*
+ * Configure the multicast list.
+ */
+
+ memset(np->new_multicast, 0xff, sizeof(np->new_multicast));
+ usb_control_msg(np->usb_dev, usb_sndctrlpipe(np->usb_dev, 0), 0xFC, 0x40, 0,
+0x7a80, np->new_multicast, 64, 10);
+
+ return 0;
+}
+
+/*
+ * Completion of the occasional control stream Rx mode change.
+ */
+
+static void ctrl_urb_done(struct urb *urb)
+{
+ struct net_device *dev = urb->context;
+ struct netdev_private *np = dev->priv;
+
+ dbg("%s: Control URB done.", dev->name);
+
+ if (urb->status)
+ return;
+
+ if (memcmp(np->cur_ether_ctrl, np->new_ether_ctrl, 4)) {
+
+ dbg("%s: USB setting Rx mode %2.2x..%2.2x.", dev->name,
+np->new_ether_ctrl[0], np->new_ether_ctrl[2]);
+ np->ctrl_msg[4] = RxFilter;
+ np->ctrl_msg[6] = 3;
+ np->ctrl_urb->transfer_buffer = np->new_ether_ctrl;
+ np->ctrl_urb->transfer_buffer_length = 3;
+ np->ctrl_urb->dev = np->usb_dev;
+ usb_submit_urb(np->ctrl_urb);
+ memcpy(np->cur_ether_ctrl, np->new_ether_ctrl, 4);
+
+ return;
+
+ }
+
+ if (memcmp(np->cur_multicast, np->new_multicast, 8)) {
+
+ dbg("%s: USB Setting Rx multicast filter.", dev->name);
+ np->ctrl_msg[4] = MulticastFilter;
+ np->ctrl_msg[6] = 64;
+ np->ctrl_urb->transfer_buffer = np->new_multicast;
+ np->ctrl_urb->transfer_buffer_length = 64;
+ memcpy(np->cur_multicast, np->new_multicast, 8);
+
+ return;
+ }
+}
+
+/*
+ * Deal with the periodic interrupt pipe status update.i
+ */
+
+static void irq_urb_done(struct urb *urb)
+{
+ struct net_device *dev = urb->context;
+ struct netdev_private *np = dev->priv;
+ unsigned char *data = urb->transfer_buffer;
+
+ /* The interrupt info is in the data buffer. */
+ dbg("%s: Interrupt time %ld status %d, data %2.2x %2.2x %2.2x %2.2x %2.2x
+%2.2x %2.2x %2.2x.",
+ dev->name, jiffies, urb->status,
+ data[0], data[1], data[2], data[3], data[4], data[5], data[6],
+data[7]);
+
+ if (urb->status)
+ return;
+
+ if (data[0] & 0xFC) {
+ np->stats.tx_errors++;
+ if (data[0] & 0x80) np->stats.tx_fifo_errors++;
+ if (data[0] & 0x44) np->stats.tx_aborted_errors++;
+ if (data[0] & 0x20) np->stats.tx_window_errors++;
+ if (data[0] & 0x18) np->stats.tx_carrier_errors++;
+ }
+
+/*
+ * Tx packet count in (data[1] & 0x0f)
+ * Hmmm, should simple missed packets be counted as an error?
+ */
+
+ if (data[2] & 0x01) np->stats.rx_over_errors++;
+ np->stats.rx_missed_errors += data[3] + (data[4]<<8);
+
+
+ if (memcmp(np->cur_ether_ctrl, np->new_ether_ctrl, 4)) {
+
+ dbg("%s: USB setting Rx mode %2.2x..%2.2x.",
+ dev->name, np->new_ether_ctrl[0], np->new_ether_ctrl[2]);
+
+ np->ctrl_msg[4] = RxFilter;
+ np->ctrl_msg[6] = 3;
+ np->ctrl_urb->transfer_buffer = np->new_ether_ctrl;
+ np->ctrl_urb->transfer_buffer_length = 3;
+ np->ctrl_urb->dev = np->usb_dev;
+ usb_submit_urb(np->ctrl_urb);
+
+ memcpy(np->cur_ether_ctrl, np->new_ether_ctrl, 4);
+ }
+}
+
+static void tx_urb_done(struct urb *urb)
+{
+ struct tx_context *txc = urb->context;
+ struct sk_buff *skb = txc->skb;
+ struct net_device *dev = skb->dev;
+ struct netdev_private *np = dev->priv;
+
+ dbg("%s: Tx packet transfer %d status %d, count %d.",
+ dev->name, np->dirty_tx, urb->status, urb->actual_length);
+
+ if (skb) {
+ np->stats.tx_packets++;
+ np->stats.tx_bytes += skb->len;
+ dev_kfree_skb(skb); /* FIXME - this is probably a bug */
+ }
+
+ np->dirty_tx++;
+ netif_wake_queue(dev);
+
+ if (np->tx_full) {
+ np->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+
+ return;
+}
+
+/*
+ * Arrruuuuggggg.
+ * This is the receive routine, rewritten for URB support. I still use only a
+ * single receive buffer. This should be fixed.
+ */
+
+static void rx_urb_done(struct urb *urb)
+{
+ struct net_device *dev = urb->context;
+ struct netdev_private *np = dev->priv;
+ void *pkt_start = urb->transfer_buffer + 2;
+ struct sk_buff *skb;
+ int status;
+ int pkt_len;
+
+ dbg("%s: rx_urb_done, status %d, count %d, buffer %p (%p).",
+ dev->name, urb->status, urb->actual_length,
+ urb->transfer_buffer, np->rxbuffer);
+
+ if (urb->status)
+ return;
+
+ pkt_len = le16_to_cpu(*(u16*)urb->transfer_buffer); /*Length omits CRC and
+status. */
+
+/*
+ * Check if the packet is long enough to accept without copying
+ * to a minimally-sized skbuff.
+ */
+
+ if ((skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* 16 byte align the IP header */
+ eth_copy_and_sum(skb, pkt_start, pkt_len, 0);
+ skb_put(skb, pkt_len);
+ }
+
+ /* You will want this info for the initial debug. */
+ dbg("%s: Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x
+%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x %d.%d.%d.%d.",
+ dev->name,
+ skb->data[0], skb->data[1], skb->data[2], skb->data[3],
+ skb->data[4], skb->data[5], skb->data[6], skb->data[7],
+ skb->data[8], skb->data[9], skb->data[10],
+ skb->data[11], skb->data[12], skb->data[13],
+ skb->data[14], skb->data[15], skb->data[16],
+ skb->data[17]);
+
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ np->stats.rx_packets++;
+ np->stats.rx_bytes += pkt_len;
+
+ urb->dev = np->usb_dev;
+ if ((status = usb_submit_urb(urb)))
+ err("%s: Problem submitting receive URB. (status %d)", dev->name,
+status);
+
+ return;
+}
+
+static int netdev_open(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct usb_device *usbdev = np->usb_dev;
+ int status, i;
+
+ dbg("%s: netdev_open (USB dev=%p)", dev->name, np->usb_dev);
+
+ np->dirty_tx = np->cur_tx = 0;
+
+ np->ctrl_out_pipe = usb_sndctrlpipe(usbdev, 0);
+ np->tx_out_pipe = usb_sndbulkpipe(usbdev, 1);
+ np->rx_in_pipe = usb_rcvbulkpipe(usbdev, 1);
+ np->irq_pipe = usb_rcvintpipe(usbdev, 2);
+
+ np->ctrl_urb = usb_alloc_urb(0);
+
+ FILL_CONTROL_URB(np->ctrl_urb, usbdev, np->ctrl_out_pipe, np->ctrl_msg, 0, 0,
+ctrl_urb_done, dev);
+
+ np->ctrl_msg[0] = 0x40;
+ np->ctrl_msg[1] = 0xFC;
+ np->ctrl_msg[2] = 0;
+ np->ctrl_msg[3] = 0;
+ np->ctrl_msg[4] = 0x80;
+ np->ctrl_msg[5] = 0x7a;
+ np->ctrl_msg[6] = 0;
+ np->ctrl_msg[7] = 0;
+
+ start_hardware(dev);
+
+ dev->if_port = 0;
+ netif_start_queue(dev);
+
+ for (i = 0; i < TX_Q; i++) {
+ struct tx_context *txc = &(np->txcontext[i]);
+ txc->netdev = dev;
+ txc->skb = 0;
+ txc->buffer = kmalloc(1536, GFP_KERNEL);
+ txc->urb = usb_alloc_urb(0);
+ FILL_BULK_URB(txc->urb, np->usb_dev, np->tx_out_pipe, txc->buffer, 0,
+tx_urb_done, txc);
+ }
+
+ for (i = 0; i < TX_Q; i++)
+ FILL_BULK_URB(&np->tx_urbs[i], np->usb_dev, np->tx_out_pipe, 0, 0,
+tx_urb_done, 0);
+
+ for (i = 0; i < RX_Q; i++) {
+
+ struct sk_buff *skb = dev_alloc_skb(1536);
+ struct urb *urb = usb_alloc_urb(0);
+
+ if (skb == NULL)
+ break;
+
+ skb->dev = dev;
+ np->rx_urbs[i] = urb;
+
+ FILL_BULK_URB(urb, np->usb_dev, np->rx_in_pipe, skb->tail, 1536,
+rx_urb_done, skb);
+ }
+
+ np->rxbuffer = kmalloc(3800, GFP_KERNEL);
+ memset(np->rxbuffer, 0x89, 3800);
+ np->rx_urb0 = usb_alloc_urb(0);
+ np->rx_urb1 = usb_alloc_urb(0);
+
+ FILL_BULK_URB(np->rx_urb0, np->usb_dev, np->rx_in_pipe, np->rxbuffer,
+ 1536, rx_urb_done, dev);
+ FILL_BULK_URB(np->rx_urb1, np->usb_dev, np->rx_in_pipe, np->rxbuffer+1536,
+ 1536, rx_urb_done, dev);
+
+ if ((status = usb_submit_urb(np->rx_urb0)) < 0)
+ err("%s: Problem submitting receive URB #0, status %d, pipe 0x%x.",
+ dev->name, status, np->rx_in_pipe);
+
+ if ((status = usb_submit_urb(np->rx_urb1)) < 0) {
+ err("%s: Problem submitting receive URB #1, status %d, pipe 0x%x.",
+ dev->name, status, np->rx_in_pipe);
+ }
+
+ if (np->irq_period && (np->irq_urb = usb_alloc_urb(0)) != NULL) {
+
+ dbg("%s: Adding an interrupt pipe with period %d.", dev->name,
+np->irq_period);
+
+ FILL_INT_URB(np->irq_urb, np->usb_dev, np->irq_pipe, np->intr_buffer,
+ sizeof(np->intr_buffer), irq_urb_done, dev, np->irq_period);
+
+ if ((status = usb_submit_urb(np->irq_urb)) < 0)
+ err("%s: Problem submitting interrupt URB, status %d, pipe
+0x%x.",
+ dev->name, status, np->irq_pipe);
+ }
+
+ dbg("%s: Done netdev_open().", dev->name);
+
+ return 0;
+}
+
+static void tx_timeout(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ unsigned char *d = np->intr_buffer; /* Short name for last irq status. */
+
+ warn("%s: Transmit timed out. Last status %2.2x %2.2x %2.2x %2.2x %2.2x.",
+ dev->name, d[0], d[1], d[2], d[3], d[4]);
+
+ /* Reload the adapter registers. */
+ start_hardware(dev);
+
+ /* Clear transmit demands. */
+ netif_wake_queue(dev);
+
+ if (np->tx_full) {
+ np->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+
+ dev->trans_start = jiffies;
+ np->stats.tx_errors++;
+ return;
+}
+
+static int start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct tx_context *txc;
+ int status;
+
+ netif_stop_queue(dev);
+
+/*
+ * The end of the packet is marked by a short (under 64 byte) transfer. For
+ * now we just pad a packet that would fit exactly. This is harmless using IP,
+ * but should be corrected.
+ */
+
+ txc = &(np->txcontext[np->cur_tx++ % TX_Q]);
+ ((u16*)txc->buffer)[0] = cpu_to_le16(skb->len);
+ memcpy(txc->buffer+2, skb->data, skb->len);
+
+ txc->skb = skb;
+ txc->urb->transfer_buffer_length = skb->len + 2;
+ txc->urb->dev = np->usb_dev;
+
+ if ((status = usb_submit_urb(txc->urb)) < 0)
+ err("%s: Problem submitting transmit URB, %d.", dev->name, status);
+
+ if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN) {
+ netif_wake_queue(dev);
+ } else {
+ np->tx_full = 1;
+ }
+
+ dev->trans_start = jiffies;
+
+ dbg("%s: Transmit frame len %d buf %p.", dev->name, skb->len, skb->data);
+
+ return 0;
+}
+
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ u16 crc_errs = 0, txcolls = 0;
+
+/*
+ * Getting statistics over USB is painfully slow. Limit the update rate.
+ */
+
+ if (jiffies - np->last_stats <= STATS_UPDATE)
+ return &np->stats;
+
+ usb_control_msg(np->usb_dev, usb_rcvctrlpipe(np->usb_dev, 0), 0xFB, 0xC0, 0,
+0x70, &crc_errs, 1, 10);
+ usb_control_msg(np->usb_dev, usb_rcvctrlpipe(np->usb_dev, 0), 0xFB, 0xC0, 0,
+0x6A, &txcolls, 1, 10);
+
+ np->stats.rx_crc_errors += crc_errs;
+ np->stats.collisions += txcolls;
+ np->last_stats = jiffies;
+
+ dbg("%s: Statistics %d, %d, %d.", dev->name, crc_errs, txcolls, 0);
+
+ return &np->stats;
+}
+
+/*
+ * The little-endian AUTODIN II ethernet CRC calculations.
+ */
+
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+ unsigned int crc = 0xffffffff; /* Initial value. */
+ while(--length >= 0) {
+ unsigned char current_octet = *data++;
+ int bit;
+ for (bit = 8; --bit >= 0; current_octet >>= 1) {
+ if ((crc ^ current_octet) & 1) {
+ crc >>= 1;
+ crc ^= 0xedb88320U;
+ } else
+ crc >>= 1;
+ }
+ }
+ return crc;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct dev_mc_list *mclist;
+ u32 mc_filter[2]; /* Multicast hash filter */
+ int i;
+
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ info("%s: Promiscuous mode enabled.", dev->name);
+ np->new_ether_ctrl[2] |= RxPromiscuous;
+ return;
+ }
+
+ if ((dev->mc_count > MULTICAST_FILTER) || (dev->flags & IFF_ALLMULTI)) {
+ np->new_ether_ctrl[0] |= RxAllMulticast;
+ np->new_ether_ctrl[2] &= ~RxPromiscuous;
+ return;
+ }
+
+ memset(mc_filter, 0, sizeof(mc_filter));
+
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist =
+mclist->next)
+ set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f, mc_filter);
+
+ np->new_ether_ctrl[0] &= ~RxAllMulticast;
+ np->new_ether_ctrl[2] &= ~RxPromiscuous;
+}
+
+static int netdev_close(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+
+ netif_stop_queue(dev);
+
+ dbg("%s: Shutting down ethercard.", dev->name);
+
+ /* Disable interrupt reports. */
+ if (np->rx_urb0) {
+ usb_unlink_urb(np->rx_urb0);
+ usb_free_urb(np->rx_urb0);
+ np->rx_urb0 = 0;
+ }
+
+ if (np->rx_urb1) {
+ usb_unlink_urb(np->rx_urb1);
+ usb_free_urb(np->rx_urb1);
+ np->rx_urb1 = 0;
+ }
+
+ if (np->irq_urb) { /* cf. np->irq_period
+*/
+ usb_unlink_urb(np->irq_urb);
+ usb_free_urb(np->irq_urb);
+ np->irq_urb = 0;
+ }
+
+ dbg("%s: Close completed.", dev->name);
+
+ return 0;
+}
+
+static void *catc_probe(struct usb_device *usbdev, unsigned int ifnum, const struct
+usb_device_id *id)
+{
+ struct net_device *dev;
+ struct netdev_private *np;
+ unsigned char eeprom[128];
+ int config_idx = usbdev->config[0].bConfigurationValue;
+ struct usb_interface_descriptor *if_setting =
+usbdev->actconfig->interface[ifnum].altsetting;
+ int i;
+
+ dev = init_etherdev(0, 0);
+
+ if (usb_set_configuration(usbdev, config_idx))
+ warn("%s: Setting CATC configuration to %d failed.", dev->name,
+config_idx);
+
+ usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0xF2, 0xC0, 0, 0,
+dev->dev_addr, 6, 10);
+ usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0xFD, 0xC0, 0, 0, eeprom,
+sizeof(eeprom), 50);
+
+/*
+ * All bogusness checks have been done at this point.
+ */
+
+ np = kmalloc(sizeof(struct netdev_private), GFP_KERNEL);
+ memset(np, 0, sizeof(struct netdev_private));
+
+ dev->base_addr = 0;
+ dev->irq = 0;
+ dev->priv = np;
+
+ np->usb_dev = usbdev;
+ np->irq_period = if_setting->bNumEndpoints > 2 ?
+if_setting->endpoint[2].bInterval : 0;
+
+/*
+ * Reset the adapter.
+ */
+
+ dbg("%s: Resetting the device, irq_period %d.", dev->name, np->irq_period);
+
+ usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0xF4, 0x40, 0, 0, 0, 0,
+10);
+
+/*
+ * Set the LED to flash slowly.
+ */
+
+ catc_set_register(np->usb_dev, LEDCtrl, 0x01);
+
+/*
+ * The chip-specific entries in the device structure.
+ */
+
+ dev->open = &netdev_open;
+ dev->hard_start_xmit = &start_tx;
+ dev->stop = &netdev_close;
+ dev->get_stats = &get_stats;
+ dev->set_multicast_list = &set_rx_mode;
+ dev->tx_timeout = tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+/*
+ * Print the detection message.
+ */
+
+ printk(KERN_INFO "%s: CATC Ethernet at usb%d:%d.%d, ",
+ dev->name, usbdev->bus->busnum, usbdev->devnum, ifnum);
+ for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]);
+ printk("%2.2x.\n", dev->dev_addr[i]);
+
+ return dev;
+}
+
+static void catc_disconnect(struct usb_device *usbdev, void *dev_ptr)
+{
+ struct net_device *dev = dev_ptr;
+ struct netdev_private *np= dev->priv;
+
+ dbg("%s: Disconnect for CATC device.", dev->name);
+
+ if (dev->flags & IFF_UP) {
+ warn("%s: USB interface was detached while still active.", dev->name);
+ dev_close(dev);
+ }
+
+ unregister_netdev(dev);
+
+ kfree(np);
+ kfree(dev);
+ return;
+}
+
+static struct usb_device_id catc_id_table [] = {
+ { USB_DEVICE(0x0423, 0xa) }, /* CATC Netmate */
+ { USB_DEVICE(0x0423, 0xc) }, /* CATC Netmate II */
+ { USB_DEVICE(0x08d1, 0x1) }, /* smartBridges smartNIC */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, catc_id_table);
+
+static struct usb_driver catc_driver = {
+ name: "catc",
+ probe: catc_probe,
+ disconnect: catc_disconnect,
+ id_table: catc_id_table,
+};
+
+static int __init catc_init(void)
+{
+ info(DRIVER_VERSION " " DRIVER_DESC);
+ usb_register(&catc_driver);
+ return 0;
+}
+
+static void __exit catc_exit(void)
+{
+ usb_deregister(&catc_driver);
+}
+
+module_init(catc_init);
+module_exit(catc_exit);