📄 net1080.c
字号:
/* * NetChip 1080 Driver (USB Host-to-Host Link) * Copyright (C) 2000 by David Brownell <dbrownell@users.sourceforge.net> *//* * This talks to the NetChip 1080, which can appear in "network cables" * and other designs. This driver interoperates with the Win32 network * drivers from NetChip, using the NetChip reference design. * * The IP-over-USB protocol here may be of interest. Embedded devices * could implement it at the cost of two bulk endpoints, and whatever * other system resources the desired IP-based applications need. * Some Linux palmtops could support that today. (Devices that don't * support the TTL-driven data mangling of the net1080 chip won't need * the header/trailer support though.) * * STATUS: * * 13-sept-2000 experimental, new * * This doesn't yet do any network hotplugging, and there's no matching * ifup policy script ... it should arrange bridging with "brctl", and * should handle static and dynamic ("pump") setups. * * RX/TX queue sizes currently fixed at one due to URB unlink problems. * * 10-oct-2000 * usb_device_id table created. * * 28-oct-2000 * misc fixes; mostly, discard more TTL-mangled rx packets. * * 01-nov-2000 * usb_device_id table support added by Adam J. Richter <adam@yggdrasil.com>. * *-------------------------------------------------------------------------*/#include <linux/config.h>#include <linux/module.h>#include <linux/kmod.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/random.h>#include <asm/unaligned.h>#define DEBUG // error path messages// #define VERBOSE // more; success messages#define USE_TTL // timeout our reads#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)# define DEBUG#endif#include <linux/usb.h>static const struct usb_device_id products [] = { // reference design { USB_DEVICE(0x1080, 0x525), driver_info: (unsigned long) "NetChip TurboCONNECT" }, // Belkin, ... { }, // END};MODULE_DEVICE_TABLE (usb, products);static u8 node_id [ETH_ALEN];/*------------------------------------------------------------------------- * * NetChip protocol: ethernet framing, and only use bulk endpoints (01/81; * not mailboxes 02/82 or status interrupt 83). Expects Ethernet bridging. * Odd USB length == always short read. * - nc_header * - payload, in Ethernet framing (14 byte header etc) * - (optional padding byte, if needed so length is odd) * - nc_trailer */struct nc_header { u16 hdr_len; // sizeof nc_header (LE, all) u16 packet_len; // packet size u16 packet_id; // detects dropped packets#define NC_MIN_HEADER 6 // all else is optional, and must start with: // u16 vendorId; // from usb-if // u16 productId;};#define NC_PAD_BYTE ((unsigned char)0xAC)struct nc_trailer { u16 packet_id;};// packetsize == f(mtu setting), with upper limit#define NC_MAX_PACKET(mtu) (sizeof (struct nc_header) \ + (mtu) \ + 1 \ + sizeof (struct nc_trailer))#define MAX_PACKET 8191// zero means no timeout; else, how long a 64 byte bulk// read may be queued before HW flushes it.#define NC_READ_TTL ((u8)255) // ms/*-------------------------------------------------------------------------*/// list of all devices we managestatic DECLARE_MUTEX (net1080_mutex);static LIST_HEAD (net1080_list);// Nineteen USB 1.1 max size bulk transactions per frame, max.#if 0#define RX_QLEN 4#define TX_QLEN 4#else// unlink_urbs() has probs on OHCI without test8-pre patches.#define RX_QLEN 1#define TX_QLEN 1#endifenum skb_state { illegal = 0, tx_start, tx_done, rx_start, rx_done, rx_cleanup};struct skb_data { // skb->cb is one of these struct urb *urb; struct net1080 *dev; enum skb_state state; size_t length;};struct net1080 { // housekeeping struct usb_device *udev; const struct usb_device_id *prod_info; struct semaphore mutex; struct list_head dev_list; wait_queue_head_t *wait; // protocol/interface state struct net_device net; struct net_device_stats stats; u16 packet_id; // various kinds of pending driver work struct sk_buff_head rxq; struct sk_buff_head txq; struct sk_buff_head done; struct tasklet_struct bh;};#define mutex_lock(x) down(x)#define mutex_unlock(x) up(x)static void defer_bh (struct net1080 *dev, struct sk_buff *skb){ unsigned long flags; skb_unlink (skb); spin_lock_irqsave (&dev->done.lock, flags); __skb_queue_tail (&dev->done, skb); if (dev->done.qlen == 1) tasklet_schedule (&dev->bh); spin_unlock_irqrestore (&dev->done.lock, flags);}/*------------------------------------------------------------------------- * * We ignore most registers and EEPROM contents. */#define REG_USBCTL ((u8)0x04)#define REG_TTL ((u8)0x10)#define REG_STATUS ((u8)0x11)/* * Vendor specific requests to read/write data */#define REQUEST_REGISTER ((u8)0x10)#define REQUEST_EEPROM ((u8)0x11)#define CONTROL_TIMEOUT (500) /* msec */static intvendor_read (struct net1080 *dev, u8 req, u8 regnum, u16 *retval_ptr){ int status = usb_control_msg (dev->udev, usb_rcvctrlpipe (dev->udev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, regnum, retval_ptr, sizeof *retval_ptr, CONTROL_TIMEOUT); if (status > 0) status = 0; if (!status) le16_to_cpus (retval_ptr); return status;}static inline intregister_read (struct net1080 *dev, u8 regnum, u16 *retval_ptr){ return vendor_read (dev, REQUEST_REGISTER, regnum, retval_ptr);}// without retval, this can become fully async (usable in_interrupt)static voidvendor_write (struct net1080 *dev, u8 req, u8 regnum, u16 value){ usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, regnum, 0, 0, // data is in setup packet CONTROL_TIMEOUT);}static inline voidregister_write (struct net1080 *dev, u8 regnum, u16 value){ vendor_write (dev, REQUEST_REGISTER, regnum, value);}#if 0static void dump_registers (struct net1080 *dev){ u8 reg; u16 value; dbg ("%s registers:", dev->net.name); for (reg = 0; reg < 0x20; reg++) { int retval; // reading some registers is trouble if (reg >= 0x08 && reg <= 0xf) continue; if (reg >= 0x12 && reg <= 0x1e) continue; retval = register_read (dev, reg, &value); if (retval < 0) dbg ("%s reg [0x%x] ==> error %d", dev->net.name, reg, retval); else dbg ("%s reg [0x%x] = 0x%x", dev->net.name, reg, value); }}#endif/*------------------------------------------------------------------------- * * Control register */#define USBCTL_WRITABLE_MASK 0x1f0f// bits 15-13 reserved, r/o#define USBCTL_ENABLE_LANG (1 << 12)#define USBCTL_ENABLE_MFGR (1 << 11)#define USBCTL_ENABLE_PROD (1 << 10)#define USBCTL_ENABLE_SERIAL (1 << 9)#define USBCTL_ENABLE_DEFAULTS (1 << 8)// bits 7-4 reserved, r/o#define USBCTL_FLUSH_OTHER (1 << 3)#define USBCTL_FLUSH_THIS (1 << 2)#define USBCTL_DISCONN_OTHER (1 << 1)#define USBCTL_DISCONN_THIS (1 << 0)#ifdef DEBUGstatic void dump_usbctl (struct net1080 *dev, u16 usbctl){ dbg ("%s: USB %d dev %d usbctl 0x%x:%s%s%s%s%s;" " this%s%s;" " other%s%s; r/o 0x%x", dev->net.name, dev->udev->bus->busnum, dev->udev->devnum, usbctl, (usbctl & USBCTL_ENABLE_LANG) ? " lang" : "", (usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "", (usbctl & USBCTL_ENABLE_PROD) ? " prod" : "", (usbctl & USBCTL_ENABLE_SERIAL) ? " serial" : "", (usbctl & USBCTL_ENABLE_DEFAULTS) ? " defaults" : "", (usbctl & USBCTL_FLUSH_OTHER) ? " FLUSH" : "", (usbctl & USBCTL_DISCONN_OTHER) ? " DIS" : "", (usbctl & USBCTL_FLUSH_THIS) ? " FLUSH" : "", (usbctl & USBCTL_DISCONN_THIS) ? " DIS" : "", usbctl & ~USBCTL_WRITABLE_MASK );}#elsestatic inline void dump_usbctl (struct net1080 *dev, u16 usbctl) {}#endif/*------------------------------------------------------------------------- * * Status register */#define STATUS_PORT_A (1 << 15)#define STATUS_CONN_OTHER (1 << 14)#define STATUS_SUSPEND_OTHER (1 << 13)#define STATUS_MAILBOX_OTHER (1 << 12)#define STATUS_PACKETS_OTHER(n) (((n) >> 8) && 0x03)#define STATUS_CONN_THIS (1 << 6)#define STATUS_SUSPEND_THIS (1 << 5)#define STATUS_MAILBOX_THIS (1 << 4)#define STATUS_PACKETS_THIS(n) (((n) >> 0) && 0x03)#define STATUS_UNSPEC_MASK 0x0c8c#define STATUS_NOISE_MASK ((u16)~(0x0303|STATUS_UNSPEC_MASK))#ifdef DEBUGstatic void dump_status (struct net1080 *dev, u16 status){ dbg ("%s: USB %d dev %d status 0x%x:" " this (%c) PKT=%d%s%s%s;" " other PKT=%d%s%s%s; unspec 0x%x", dev->net.name, dev->udev->bus->busnum, dev->udev->devnum, status, // XXX the packet counts don't seem right // (1 at reset, not 0); maybe UNSPEC too (status & STATUS_PORT_A) ? 'A' : 'B', STATUS_PACKETS_THIS (status), (status & STATUS_CONN_THIS) ? " CON" : "", (status & STATUS_SUSPEND_THIS) ? " SUS" : "", (status & STATUS_MAILBOX_THIS) ? " MBOX" : "", STATUS_PACKETS_OTHER (status), (status & STATUS_CONN_OTHER) ? " CON" : "", (status & STATUS_SUSPEND_OTHER) ? " SUS" : "", (status & STATUS_MAILBOX_OTHER) ? " MBOX" : "", status & STATUS_UNSPEC_MASK );}#elsestatic inline void dump_status (struct net1080 *dev, u16 status) {}#endif/*------------------------------------------------------------------------- * * TTL register */#define TTL_THIS(ttl) (0x00ff & ttl)#define TTL_OTHER(ttl) (0x00ff & (ttl >> 8))#define MK_TTL(this,other) ((u16)(((other)<<8)|(0x00ff&(this))))#ifdef DEBUGstatic void dump_ttl (struct net1080 *dev, u16 ttl){ dbg ("%s: USB %d dev %d ttl 0x%x this = %d, other = %d", dev->net.name, dev->udev->bus->busnum, dev->udev->devnum, ttl, TTL_THIS (ttl), TTL_OTHER (ttl) );}#elsestatic inline void dump_ttl (struct net1080 *dev, u16 ttl) {}#endif#define RUN_CONTEXT (in_irq () ? "in_irq" \ : (in_interrupt () ? "in_interrupt" : "can sleep"))/*-------------------------------------------------------------------------*/// ensure that the device is in a known state before using it.// preconditions:// caller owns the device mutex// caller has a process contextstatic int net1080_reset (struct net1080 *dev){ u16 usbctl, status, ttl; int retval; if ((retval = register_read (dev, REG_STATUS, &status)) < 0) { dbg ("can't read dev %d status: %d", dev->udev->devnum, retval); goto done; } dump_status (dev, status); if ((retval = register_read (dev, REG_USBCTL, &usbctl)) < 0) { dbg ("can't read USBCTL, %d", retval); goto done; } dump_usbctl (dev, usbctl); register_write (dev, REG_USBCTL, USBCTL_FLUSH_THIS | USBCTL_FLUSH_OTHER); if ((retval = register_read (dev, REG_TTL, &ttl)) < 0) { dbg ("can't read TTL, %d", retval); goto done; } dump_ttl (dev, ttl);#ifdef USE_TTL // Have the chip flush reads that seem to be starving for read // bandwidth ... or we're otherwise reading. Note, Win32 drivers // may change our read TTL for us. register_write (dev, REG_TTL, MK_TTL (NC_READ_TTL, TTL_OTHER (ttl)) ); dbg ("%s: assigned TTL, %d ms", dev->net.name, NC_READ_TTL);#endif info ("%s: %s, port %c on USB %d dev %d, peer %sconnected", dev->net.name, (char *) dev->prod_info->driver_info, (status & STATUS_PORT_A) ? 'A' : 'B', dev->udev->bus->busnum, dev->udev->devnum, (status & STATUS_CONN_OTHER) ? "" : "dis" ); retval = 0;done: return retval;}/*------------------------------------------------------------------------- * * Network Device Driver support (peer link to USB Host) * --------------------------------------------------------------------------*/static int net1080_change_mtu (struct net_device *net, int new_mtu){ if ((new_mtu < 0) || NC_MAX_PACKET (new_mtu) > MAX_PACKET) return -EINVAL; net->mtu = new_mtu; return 0;}/*-------------------------------------------------------------------------*/static struct net_device_stats *net1080_get_stats (struct net_device *net){ return &((struct net1080 *) net->priv)->stats;}/*-------------------------------------------------------------------------*/static void rx_complete (struct urb *urb);static void rx_submit (struct net1080 *dev, struct urb *urb, int flags){ struct sk_buff *skb; struct skb_data *entry; int retval = 0; unsigned long lockflags; if ((skb = alloc_skb (NC_MAX_PACKET (dev->net.mtu), flags)) == 0) { err ("no rx skb"); tasklet_schedule (&dev->bh); usb_free_urb (urb); return; } entry = (struct skb_data *) skb->cb; entry->urb = urb; entry->dev = dev; entry->state = rx_start; entry->length = 0; FILL_BULK_URB (urb, dev->udev, usb_rcvbulkpipe (dev->udev, 1), skb->data, skb->truesize, rx_complete, skb); urb->transfer_flags |= USB_QUEUE_BULK; spin_lock_irqsave (&dev->rxq.lock, lockflags); if (!netif_queue_stopped (&dev->net)) { if ((retval = usb_submit_urb (urb)) != 0) { err ("%s rx submit, %d", dev->net.name, retval); tasklet_schedule (&dev->bh); } else { __skb_queue_tail (&dev->rxq, skb); } } else { dbg ("rx: stopped"); retval = -ENOLINK; } spin_unlock_irqrestore (&dev->rxq.lock, lockflags); if (retval) { dev_kfree_skb_any (skb); usb_free_urb (urb); }}/*-------------------------------------------------------------------------*/static void rx_complete (struct urb *urb){ struct sk_buff *skb = (struct sk_buff *) urb->context; struct skb_data *entry = (struct skb_data *) skb->cb; struct net1080 *dev = entry->dev; int urb_status = urb->status; urb->dev = 0; skb->len = urb->actual_length; entry->state = rx_done; entry->urb = 0; if ((urb->transfer_flags & USB_ASYNC_UNLINK) != 0) { dbg ("rx ... shutting down"); usb_free_urb (urb); urb = 0; } switch (urb_status) { // success case 0: if (!(skb->len & 0x01)) { entry->state = rx_cleanup; dev->stats.rx_errors++; dev->stats.rx_length_errors++; dbg ("even rx len %d", skb->len); } else if (skb->len > MAX_PACKET) { entry->state = rx_cleanup; dev->stats.rx_errors++; dev->stats.rx_frame_errors++; dbg ("rx too big, %d", skb->len); } break; // hardware-reported interface shutdown ... which we // typically see before khubd calls disconnect() case -ETIMEDOUT: // usb-ohci case -EILSEQ: // *uhci ... "crc"/timeout error
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -