patch-2.4.10 linux/drivers/usb/plusb.c

Next file: linux/drivers/usb/printer.c
Previous file: linux/drivers/usb/pegasus.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.9/linux/drivers/usb/plusb.c linux/drivers/usb/plusb.c
@@ -1,1060 +0,0 @@
-/*****************************************************************************/
-
-/*
- *      plusb.c  --  prolific pl-2301/pl-2302 driver.
- *
- *      Copyright (C) 2000  Deti Fliegl (deti@fliegl.de)
- *      Copyright (C) 2000  Pavel Machek (pavel@suse.cz)
- *      Copyright (C) 2000  Eric Z. Ayers (eric@compgen.com)
- *
- *      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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- *  This driver creates a network interface (plusb0, plusb1, ...) that will
- *  send messages over a USB host-host cable based on the Prolific ASIC.
- *  It works a lot like plip or PP over an RS-232C null modem cable.
- *
- *  Expect speeds of around 330Kbytes/second over a UHCI host controller.
- *  OHCI should be faster.  Increase the MTU for faster transfers of large
- *  files (up-to 800Kbytes/second).  (16384 is a good size)
- *
- *  $Id: plusb.c,v 1.18 2000/02/14 10:38:58 fliegl Exp $
- *
- *  Changelog:
- *
- *    v0.1                deti
- *        Original Version of driver.
- *    v0.2     15 Sep 2000  pavel
- *        Patches to decrease latency by rescheduling the bottom half of
- *          interrupt code.
- *    v0.3     10 Oct 2000  eric
- *        Patches to work in v2.2 backport (v2.4 changes the way net_dev.name
- *          is allocated)
- *    v0.4     19 Oct 2000  eric
- *        Some more performance fixes.  Lock re-submitting urbs.
- *          Lower the number of sk_buff's to queue.
- *    v0.5     25 Oct 2000 eric
- *        Removed use of usb_bulk_msg() all together.  This caused
- *          the driver to block in an interrupt context.
- *        Consolidate read urb submission into read_urb_submit().
- *        Performance is the same as v0.4.
- *    v0.5.1   27 Oct 2000 eric
- *        Extra debugging messages to help diagnose problem with uchi.o stack.
- *    v0.5.2   27 Oct 2000 eric
- *        Set the 'start' flag for the network device in plusb_net_start()
- *         and plusb_net_stop() (doesn't help)
- *    v0.5.3   27 Oct 2000 pavel
- *        Commented out handlers when -EPIPE is received,
- *         (remove calls to usb_clear_halt()) Since the callback is in
- *         an interrupt context, it doesn't help, it just panics
- *         the kernel. (what do we do?)
- *        Under high load, dev_alloc_skb() fails, the read URB must
- *         be re-submitted.
- *        Added plusb_change_mtu() and increased the size of _BULK_DATA_LEN
- *    v0.5.4   31 Oct 2000 eric
- *        Fix race between plusb_net_xmit() and plusb_bulk_write_complete()
- *    v0.5.5    1 Nov 2000 eric
- *        Remove dev->start field, otherwise, it won't compile in 2.4
- *        Use dev_kfree_skb_any(). (important in 2.4 kernel)
- *    v0.5.6   2 Nov 2000 pavel,eric
- *        Add calls to netif_stop_queue() and netif_start_queue()
- *        Drop packets that come in while the free list is empty.
- *        (This version is being submitted after the release of 2.4-test10)
- *    v0.5.7   6 Nov 2000
- *        Fix to not re-submit the urb on error to help when cables
- *          are yanked (not tested)
- *
- *
- * KNOWN PROBLEMS: (Any suggestions greatfully accepted!)
- *
- *     2 Nov 2000
- *      - The shutdown for this may not be entirely clean.  Sometimes, the
- *         kernel will Oops when the cable is unplugged, or
- *         if the plusb module is removed.
- *      - If you ifdown a device and then ifup it again, the link will not
- *         always work.  You have to 'rmmod plusb ; modprobe plusb' on
- *         both machines to get it to work again.  Something must be wrong with
- *         plusb_net_open() and plusb_net_start() ?  Maybe
- *         the 'suspend' and 'resume' entry points need to be
- *         implemented?
- *      - Needs to handle -EPIPE correctly in bulk complete handlers.
- *         (replace usb_clear_halt() function with async urbs?)
- *      - I think this code relies too much on one spinlock and does
- *         too much in the interrupt handler.  The net1080 code is
- *         much more elegant, and should work for this chip.  Its
- *         only drawback is that it is going to be tough to backport
- *         it to v2.2.
- *      - Occasionally the device will hang under the 'uhci.o'
- *         driver.   The workaround is to ifdown the device and
- *         remove the modules, then re-insert them.  You may have
- *         better luck with the 'usb-uhci.o' driver.
- *      - After using ifconfig down ; ifconfig up, sometimes packets
- *         continue to be received, but there is a framing problem.
- *
- * FUTURE DIRECTIONS:
- *
- *     - Fix the known problems.
- *     - There isn't much functional difference between the net1080
- *        driver and this one.  It would be neat if the same driver
- *        could handle both types of chips.  Or if both drivers
- *        could handle both types of chips - this one is easier to
- *        backport to the 2.2 kernel.
- *     - Get rid of plusb_add_buf_tail and the single spinlock.
- *        Use a separate spinlock for the 2 lists, and use atomic
- *        operators for writeurb_submitted and readurb_submitted members.
- *
- *
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/socket.h>
-#include <linux/miscdevice.h>
-#include <linux/list.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <asm/uaccess.h>
-#include <asm/atomic.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-//#define DEBUG 1
-#include <linux/usb.h>
-
-#if (LINUX_VERSION_CODE < 0x020300)
-#define dev_kfree_skb_any dev_kfree_skb
-#endif
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v0.5.7"
-#define DRIVER_AUTHOR "Deti Fliegl, deti@fliegl.de"
-#define DRIVER_DESC "PL-2302 USB Interface Driver for Linux (c)2000"
-
-/* Definitions formerly in plusb.h relocated. No need to export them -EZA */
-
-#define _PLUSB_INTPIPE		0x1
-#define _PLUSB_BULKOUTPIPE	0x2
-#define _PLUSB_BULKINPIPE	0x3
-
-#define _SKB_NUM		32
-
-/* increase size of BULK_DATA_LEN so we can use bigger MTU's*/
-#define _BULK_DATA_LEN		32768
-
-
-typedef struct
-{
-	int connected;   /* indicates if this structure is active */
-	struct usb_device *usbdev;
-					 /* keep track of USB structure */
-	int status;      /* Prolific status byte returned from interrupt */
-	int in_bh;       /* flag to indicate that we are in the bulk handler */
-	int opened;      /* flag to indicate that network dev is open    */
-
-	spinlock_t lock; /* Lock for the buffer list. re-used for
-						locking around submitting the readurb member.
-					 */
-	urb_t *inturb;   /* Read buffer for the interrupt callback */
-	unsigned char *		interrupt_in_buffer;
-			 /* holds data for the inturb*/
-	urb_t *readurb;  /* Read buffer for the bulk data callback */
-	unsigned char *		bulk_in_buffer;
-					 /* kmalloc'ed data for the readurb */
-	int readurb_submitted;
-					 /* Flag to indicate that readurb already sent */
-	urb_t *writeurb; /* Write buffer for the bulk data callback */
-	int writeurb_submitted;
-					 /* Flag to indicate that writeurb already sent */
-	
-	struct list_head tx_skb_list;
-		 			 /* sk_buff's read from net device */
-	struct list_head free_skb_list;
-					/* free sk_buff list */
-	struct net_device net_dev;
-					/* handle to linux network device */
-	struct net_device_stats net_stats;
-					/* stats to return for ifconfig output */
-} plusb_t,*pplusb_t;
-
-/*
- * skb_list - queue of packets from the network driver to be delivered to USB
- */
-typedef struct
-{
-	struct list_head skb_list;
-	struct sk_buff *skb;
-	int state;
-	plusb_t *s;
-} skb_list_t,*pskb_list_t;
-
-
-/* --------------------------------------------------------------------- */
-
-#define NRPLUSB 4
-
-/*
- * Interrupt endpoint status byte, from Prolific PL-2301 docs
- * Check the 'download' link at www.prolifictech.com
- */
-#define _PL_INT_RES1    0x80 /* reserved              */
-#define _PL_INT_RES2    0x40 /* reserved              */
-#define _PL_INT_RXD	_PL_INT_RES2  /* Read data ready - Not documented by Prolific, but seems to work! */
-#define _PL_INT_TX_RDY	0x20 /* OK to transmit data   */
-#define _PL_INT_RESET_O	0x10 /* reset output pipe     */
-#define _PL_INT_RESET_I 0x08 /* reset input pipe      */
-#define _PL_INT_TX_C    0x04 /* transmission complete */
-#define _PL_INT_TX_REQ  0x02 /* transmission received */
-#define _PL_INT_PEER_E  0x01 /* peer exists           */
-
-/*-------------------------------------------------------------------*/
-
-static plusb_t plusb[NRPLUSB];
-
-static void plusb_write_bulk_complete(urb_t *purb);
-static void plusb_read_bulk_complete(urb_t *purb);
-static void plusb_int_complete(urb_t *purb);
-
-/* --------------------------------------------------------------------- */
-
-/*
- * plusb_add_buf_tail - Take the head of the src list and append it to
- *                      the tail of the dest list
- */
-static int plusb_add_buf_tail (plusb_t *s, struct list_head *dst, struct list_head *src)
-{
-	unsigned long flags = 0;
-	struct list_head *tmp;
-	int ret = 0;
-
-	spin_lock_irqsave (&s->lock, flags);
-
-	if (list_empty (src)) {
-		// no elements in source buffer
-		ret = -1;
-		goto err;
-	}
-	tmp = src->next;
-	list_del (tmp);
-	list_add_tail (tmp, dst);
-
-  err:	spin_unlock_irqrestore (&s->lock, flags);
-	return ret;
-}
-/*-------------------------------------------------------------------*/
-
-/*
- * dequeue_next_skb - submit the first thing on the tx_skb_list to the
- * USB stack.  This function should be called each time we get a new
- * message to send to the other host, or each time a message is successfully
- * sent.
- */
-static void dequeue_next_skb(char * func, plusb_t * s)
-{
-	skb_list_t * skb_list;
-	unsigned long flags = 0;
-
-	if (!s->connected)
-		return;
-	
-	spin_lock_irqsave (&s->lock, flags);
-	
-	if (!list_empty (&s->tx_skb_list) && !s->writeurb_submitted) {
-		int submit_ret;
-		skb_list = list_entry (s->tx_skb_list.next, skb_list_t, skb_list);
-
-		if (skb_list->skb) {
-			s->writeurb_submitted = 1;
-		
-			/* Use the buffer inside the sk_buff directly. why copy? */
-			FILL_BULK_URB_TO(s->writeurb, s->usbdev,
-							 usb_sndbulkpipe(s->usbdev, _PLUSB_BULKOUTPIPE),
-							 skb_list->skb->data, skb_list->skb->len,
-							 plusb_write_bulk_complete, skb_list, 500);
-			
-			dbg ("%s: %s: submitting urb. skb_list %p", s->net_dev.name, func, skb_list);
-		
-			submit_ret = usb_submit_urb(s->writeurb);
-			if (submit_ret) {
-				s->writeurb_submitted = 0;
-				printk (KERN_CRIT "%s: %s: can't submit writeurb: %d\n",
-						s->net_dev.name, func, submit_ret);
-			}
-		} /* end if the skb value has been filled in */
-	}
-	
-	spin_unlock_irqrestore (&s->lock, flags);	
-}
-
-/*
- * submit_read_urb - re-submit the read URB to the stack
- */
-void submit_read_urb(char * func, plusb_t * s)
-{
-	unsigned long flags=0;
-
-	if (!s->connected)
-		return;
-	
-	spin_lock_irqsave (&s->lock, flags);
-	
-	if (!s->readurb_submitted) {
-		int ret;
-		s->readurb_submitted=1;
-		s->readurb->dev=s->usbdev;
-		ret = usb_submit_urb(s->readurb);
-		if (ret) {
-			printk (KERN_CRIT "%s: %s: error %d submitting read URB\n",
-				   s->net_dev.name, func, ret);
-			s->readurb_submitted=0;
-		}
-	}
-
-	spin_unlock_irqrestore (&s->lock, flags);
-	
-}
-/* --------------------------------------------------------------------- */
-
-/*
- * plusb_net_xmit - callback from the network device driver for outgoing data
- *
- * Data has arrived to the network device from the local machine and needs
- * to be sent over the USB cable.  This is in an interrupt, so we don't
- * want to spend too much time in this function.
- *
- */
-static int plusb_net_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	plusb_t *s=dev->priv;
-	skb_list_t *skb_list;
-	unsigned int flags;
-
-	dbg("plusb_net_xmit: len:%d i:%d",skb->len,in_interrupt());
-
-	if(!s->connected || !s->opened) {
-		/*
-		  NOTE: If we get to this point, you'll return the error
-		  kernel: virtual device plusb0 asks to queue packet
-
-		  Other things we could do:
-		  1) just drop this packet
-		  2) drop other packets in the queue
-		*/
-		return 1;
-	}
-
-	spin_lock_irqsave (&s->lock, flags);
-
-	if  (list_empty(&s->free_skb_list)
-	|| plusb_add_buf_tail (s, &s->tx_skb_list, &s->free_skb_list)) {
-		/* The buffers on this side are full. DROP the packet
-		   I think that this shouldn't happen with the correct
-		   use of the netif_XXX functions -EZA
-		 */
-		dbg ("plusb: Free list is empty.");
-		kfree_skb(skb);
-		s->net_stats.tx_dropped++;
-		spin_unlock_irqrestore (&s->lock, flags);
-		return 0;
-	}
-	
-	skb_list = list_entry (s->tx_skb_list.prev, skb_list_t, skb_list);
-	skb_list->skb=skb;
-	skb_list->state=1;
-	skb_list->s=s;
-
-	if (list_empty(&s->free_skb_list)) {
-		/* apply "backpressure". Tell the net layer to stop sending
-		   the driver packets.
-		*/
-		netif_stop_queue(dev);
-	}
-	
-	spin_unlock_irqrestore (&s->lock, flags);
-	
-	/* If there is no write urb outstanding, pull the first thing
-	   off of the list and submit it to the USB stack
-	*/
-	dequeue_next_skb("plusb_net_xmit", s);
-	
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * plusb_write_bulk_complete () - callback after the data has been
- *   sent to the USB device, or a timeout occurred.
- */
-static void plusb_write_bulk_complete(urb_t *purb)
-{
-	skb_list_t * skb_list=purb->context;
-	plusb_t *s=skb_list->s;
-
-	dbg ("%s: plusb_write_bulk_complete: status:%d skb_list:%p\n",
-		 s->net_dev.name, purb->status, skb_list);
-
-	skb_list->state=0;
-
-	if( purb->status == -EPIPE )
-		printk(KERN_CRIT "%s: plusb_write_bulk_complete: got -EPIPE and don't know what to do!\n",
-		     s->net_dev.name);
-		
-	if(!purb->status) {
-		s->net_stats.tx_packets++;
-		s->net_stats.tx_bytes+=skb_list->skb->len;
-	}
-	else {
-		err ("%s: plusb_write_bulk_complete: returned ERROR status:%d\n",
-			 s->net_dev.name, purb->status);
-
-		s->net_stats.tx_errors++;
-		s->net_stats.tx_aborted_errors++;
-	}
-	
-	dbg("plusb_bh: dev_kfree_skb");
-
-	/* NOTE: In 2.4 it's a problem to call dev_kfree_skb() in a hard IRQ:
-	   Oct 28 23:42:14 bug kernel: Warning: kfree_skb on hard IRQ c023329a
-	*/
-	dev_kfree_skb_any(skb_list->skb);
-	
-	skb_list->skb = NULL;
-	if (plusb_add_buf_tail (s, &s->free_skb_list, &s->tx_skb_list)) {
-		err ("plusb: tx list empty. This shouldn't happen.");
-	}
-
-	purb->status = 0;
-	s->writeurb_submitted = 0;
-	
-    netif_wake_queue((&s->net_dev));
-	
-	dequeue_next_skb("plusb_write_bulk_complete", s);
-
-	
-}
-
-/*
- * plusb_read_bulk_complete - Callback for data arriving from the USB device
- *
- * This gets called back when a full 'urb' is received from the remote system.
- * This urb was allocated by this driver and is kept in the member: s->readurb
- *
- */
-static void plusb_read_bulk_complete(urb_t *purb)
-{
-	
-	plusb_t *s=purb->context;
-
-	dbg("plusb_read_bulk_complete: status:%d length:%d", purb->status,purb->actual_length);
-	
-	if(!s->connected)
-		return;
-
-	if( purb->status == -EPIPE )
-		printk(KERN_CRIT "%s: plusb_read_bulk_complete: got -EPIPE and I don't know what to do!\n",
-		     s->net_dev.name);
-	else if (!purb->status) {
-		struct sk_buff *skb;
-		unsigned char *dst;
-		int len=purb->transfer_buffer_length;
-		struct net_device_stats *stats=&s->net_stats;
-
-		skb=dev_alloc_skb(len);
-
-		if(!skb) {
-			printk (KERN_CRIT "%s: plusb_read_bulk_complete: dev_alloc_skb(%d)=NULL, dropping frame\n", s->net_dev.name, len);
-			stats->rx_dropped++;
-		} else {
-			dst=(char *)skb_put(skb, len);
-			memcpy( dst, purb->transfer_buffer, len);
-			
-			skb->dev=&s->net_dev;
-			skb->protocol=eth_type_trans(skb, skb->dev);
-			stats->rx_packets++;
-			stats->rx_bytes+=len;
-			netif_rx(skb);
-		}
-		
-	}
-	
-	s->readurb_submitted = 0;
-	
-	if (purb->status) {
-		/* Give the system a chance to "catch its breath". Shortcut
-		   re-submitting the read URB>  It will be re-submitted if
-		   another interrupt comes back.  The problem scenario is that
-		   the plub is pulled and the read returns an error.
-		   You don't want to resumbit in this case.
-		*/
-		err ("%s: plusb_read_bulk_complete: returned status %d\n",
-			 s->net_dev.name, purb->status);
-		return;
-	}
-
-
-	purb->status=0;
-
-	/* Keep it coming! resubmit the URB for reading.. Make sure
-	   we aren't in contention with the interrupt callback.
-	*/
-	submit_read_urb("plusb_read_bulk_complete", s);
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * plusb_int_complete - USB driver callback for interrupt msg from the device
- *
- * Interrupts are scheduled to go off on a periodic basis (see FILL_INT_URB)
- * For the prolific device, this is basically just returning a register
- * filled with bits.  See the macro definitions for _PL_INT_XXX above.
- * Most of these bits are for implementing a machine-machine protocol
- * and can be set with a special message (described as the "Quicklink"
- * feature in the prolific documentation.)
- *
- * I don't think we need any of that to work as a network device. If a
- * message is lost, big deal - that's what UNIX networking expects from
- * the physical layer.
- *
- */
-static void plusb_int_complete(urb_t *purb)
-{
-	plusb_t *s=purb->context;
-	s->status=((unsigned char*)purb->transfer_buffer)[0]&255;
-	
-#if 0
-	/* This isn't right because 0x20 is TX_RDY and
-	   sometimes will not be set
-	*/
-	if((s->status&0x3f)!=0x20) {
-		warn("invalid device status %02X", s->status);
-		return;
-	}
-#endif	
-	if(!s->connected)
-		return;
-
-	/* Don't turn this on unless you want to see the log flooded. */
-#if 0
-	printk("plusb_int_complete: PEER_E:%d TX_REQ:%d TX_C:%d RESET_IN:%d RESET_O: %d TX_RDY:%d RES1:%d RES2:%d\n",
-	    s->status & _PL_INT_PEER_E  ? 1 : 0,
-	    s->status & _PL_INT_TX_REQ  ? 1 : 0,
-	    s->status & _PL_INT_TX_C    ? 1 : 0,
-	    s->status & _PL_INT_RESET_I ? 1 : 0,
-	    s->status & _PL_INT_RESET_O ? 1 : 0,
-	    s->status & _PL_INT_TX_RDY  ? 1 : 0,
-	    s->status & _PL_INT_RES1    ? 1 : 0,
-	    s->status & _PL_INT_RES2    ? 1 : 0);
-#endif
-
-#if 1
-	/* At first glance, this logic appears to not really be needed, but
-	   it can help recover from intermittent problems where the
-	   usb_submit_urb() fails in the read callback. -EZA
-	*/
-	
-	/* Try to submit the read URB again. Make sure
-	   we aren't in contention with the bulk read callback
-	*/
-	submit_read_urb ("plusb_int_complete", s);
-	
-	/* While we are at it, why not check to see if the
-	   write urb should be re-submitted?
-	*/
-	dequeue_next_skb("plusb_int_complete", s);	
-	
-#endif
-
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * plusb_free_all - deallocate all memory kept for an instance of the device.
- */
-static void plusb_free_all(plusb_t *s)
-{
-	struct list_head *skb;
-	skb_list_t *skb_list;
-	
-	dbg("plusb_free_all");
-	
-	/* set a flag to tell all callbacks to cease and desist */
-	s->connected = 0;
-
-	/* If the interrupt handler is about to fire, let it finish up */
-	run_task_queue(&tq_immediate);	
-
-	if(s->inturb) {
-		dbg("unlink inturb");
-		usb_unlink_urb(s->inturb);
-		dbg("free_urb inturb");
-		usb_free_urb(s->inturb);
-		s->inturb=NULL;
-	}
-	
-	if(s->interrupt_in_buffer) {
-		dbg("kfree s->interrupt_in_buffer");
-		kfree(s->interrupt_in_buffer);
-		s->interrupt_in_buffer=NULL;
-	}
-
-	if(s->readurb) {
-		dbg("unlink readurb");
-		usb_unlink_urb(s->readurb);
-		dbg("free_urb readurb:");
-		usb_free_urb(s->readurb);
-		s->readurb=NULL;
-	}
-
-	if(s->bulk_in_buffer) {
-		dbg("kfree s->bulk_in_buffer");
-		kfree(s->bulk_in_buffer);
-		s->bulk_in_buffer=NULL;
-	}
-	
-	s->readurb_submitted = 0;
-	
-	if(s->writeurb) {
-		dbg("unlink writeurb");
-		usb_unlink_urb(s->writeurb);
-		dbg("free_urb writeurb:");
-		usb_free_urb(s->writeurb);
-		s->writeurb=NULL;
-	}
-
-	s->writeurb_submitted = 0;
-	
-	while(!list_empty(&s->free_skb_list)) {
-		skb=s->free_skb_list.next;
-		list_del(skb);
-		skb_list = list_entry (skb, skb_list_t, skb_list);
-		kfree(skb_list);
-	}
-
-	while(!list_empty(&s->tx_skb_list)) {
-		skb=s->tx_skb_list.next;
-		list_del(skb);
-		skb_list = list_entry (skb, skb_list_t, skb_list);
-		if (skb_list->skb) {
-			dbg ("Freeing SKB in queue");
-			dev_kfree_skb_any(skb_list->skb);
-			skb_list->skb = NULL;
-		}
-		kfree(skb_list);
-	}
-	
-	s->in_bh=0;
-	
-	dbg("plusb_free_all: finished");	
-}
-
-/*-------------------------------------------------------------------*/
-/*
- * plusb_alloc - allocate memory associated with one instance of the device
- */
-static int plusb_alloc(plusb_t *s)
-{
-	int i;
-	skb_list_t *skb;
-
-	dbg("plusb_alloc");
-
-	for(i=0 ; i < _SKB_NUM ; i++) {
-		skb=kmalloc(sizeof(skb_list_t), GFP_KERNEL);
-		if(!skb) {
-			err("kmalloc for skb_list failed");
-			goto reject;
-		}
-		memset(skb, 0, sizeof(skb_list_t));
-		list_add(&skb->skb_list, &s->free_skb_list);
-	}
-
-	dbg("inturb allocation:");
-	s->inturb=usb_alloc_urb(0);
-	if(!s->inturb) {
-		err("alloc_urb failed");
-		goto reject;
-	}
-
-	dbg("bulk read urb allocation:");
-	s->readurb=usb_alloc_urb(0);
-	if(!s->readurb) {
-		err("alloc_urb failed");
-		goto reject;
-	}
-	
-	dbg("bulk write urb allocation:");
-	s->writeurb=usb_alloc_urb(0);
-	if(!s->writeurb) {
-		err("alloc_urb for writeurb failed");
-		goto reject;
-	}
-	
-	dbg("readurb/inturb init:");
-	s->interrupt_in_buffer=kmalloc(64, GFP_KERNEL);
-	if(!s->interrupt_in_buffer) {
-		err("kmalloc failed");
-		goto reject;
-	}
-
-	/* The original value of '10' makes this interrupt fire off a LOT.
-	   It was set so low because the callback determined when to
-	   sumbit the buld read URB. I've lowered it to 100 - the driver
-	   doesn't depend on that logic anymore. -EZA
-	*/
-	FILL_INT_URB(s->inturb, s->usbdev,
-		     usb_rcvintpipe (s->usbdev, _PLUSB_INTPIPE),
-		     s->interrupt_in_buffer, 1,
-		     plusb_int_complete, s, HZ);
-
-	dbg("inturb submission:");
-	if(usb_submit_urb(s->inturb)<0) {
-		err("usb_submit_urb failed");
-		goto reject;
-	}
-	
-	dbg("readurb init:");
-	s->bulk_in_buffer = kmalloc(_BULK_DATA_LEN, GFP_KERNEL);
-	if (!s->bulk_in_buffer) {
-		err("kmalloc %d bytes for bulk in buffer failed", _BULK_DATA_LEN);
-	}
-
-	FILL_BULK_URB(s->readurb, s->usbdev,
-		      usb_rcvbulkpipe(s->usbdev, _PLUSB_BULKINPIPE),
-		      s->bulk_in_buffer, _BULK_DATA_LEN,
-		      plusb_read_bulk_complete, s);
-
-	/* The write urb will be initialized inside the network
-	   interrupt.
-	*/
-
-	/* get the bulk read going */
-	submit_read_urb("plusb_alloc", s);
-
-	dbg ("plusb_alloc: finished. readurb=%p writeurb=%p inturb=%p",
-		s->readurb, s->writeurb, s->inturb);
-	
-	return 0;
-
-  reject:
-  	dbg("plusb_alloc: failed");
-	
-	plusb_free_all(s);
-	return -ENOMEM;
-}
-
-/*-------------------------------------------------------------------*/
-
-static int plusb_net_open(struct net_device *dev)
-{
-	plusb_t *s=dev->priv;
-	
-	dbg("plusb_net_open");
-	
-	if(plusb_alloc(s))
-		return -ENOMEM;
-
-	s->opened=1;
-	
-	MOD_INC_USE_COUNT;
-
-	netif_start_queue(dev);
-
-	dbg("plusb_net_open: success");
-	
-	return 0;
-	
-}
-
-/* --------------------------------------------------------------------- */
-
-static int plusb_net_stop(struct net_device *dev)
-{
-	plusb_t *s=dev->priv;
-
-	netif_stop_queue(dev);
-	
-	dbg("plusb_net_stop");	
-	
-	s->opened=0;
-	plusb_free_all(s);
-
-	MOD_DEC_USE_COUNT;
-	dbg("plusb_net_stop:finished");
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static struct net_device_stats *plusb_net_get_stats(struct net_device *dev)
-{
-	plusb_t *s=dev->priv;
-	
-	dbg("net_device_stats");
-	
-	return &s->net_stats;
-}
-
-/* --------------------------------------------------------------------- */
-
-static plusb_t *plusb_find_struct (void)
-{
-	int u;
-
-	for (u = 0; u < NRPLUSB; u++) {
-		plusb_t *s = &plusb[u];
-		if (!s->connected)
-			return s;
-	}
-	return NULL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void plusb_disconnect (struct usb_device *usbdev, void *ptr)
-{
-	plusb_t *s = ptr;
-
-	dbg("plusb_disconnect");
-	
-	plusb_free_all(s);
-
-	if(!s->opened && s->net_dev.name) {
-		dbg("unregistering netdev: %s",s->net_dev.name);
-		unregister_netdev(&s->net_dev);
-		s->net_dev.name[0] = '\0';
-#if (LINUX_VERSION_CODE < 0x020300)
-		dbg("plusb_disconnect: About to free name");
- 		kfree (s->net_dev.name);
- 		s->net_dev.name = NULL;
-#endif	
-	}
-	
-	dbg("plusb_disconnect: finished");
-	MOD_DEC_USE_COUNT;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int plusb_change_mtu(struct net_device *dev, int new_mtu)
-{
-	if ((new_mtu < 68) || (new_mtu > _BULK_DATA_LEN))
-		return -EINVAL;
-
-	printk("plusb: changing mtu to %d\n", new_mtu);
-	dev->mtu = new_mtu;
-	
-	/* NOTE: Could we change the size of the READ URB here dynamically
-	   to save kernel memory?
-	*/
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-int plusb_net_init(struct net_device *dev)
-{
-	dbg("plusb_net_init");
-	
-	dev->open=plusb_net_open;
-	dev->stop=plusb_net_stop;
-	dev->hard_start_xmit=plusb_net_xmit;
-	dev->get_stats	= plusb_net_get_stats;
-	ether_setup(dev);
-	dev->change_mtu = plusb_change_mtu;
-	/* Setting the default MTU to 16K gives good performance for
-	   me, and keeps the ping latency low too.  Setting it up
-	   to 32K made performance go down. -EZA
-	   Pavel says it would be best not to do this...
-	*/
-	/*dev->mtu=16384; */
-	dev->tx_queue_len = 0;	
-	dev->flags = IFF_POINTOPOINT|IFF_NOARP;
-
-	
-	dbg("plusb_net_init: finished");
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum, const struct usb_device_id *id)
-{
-	plusb_t *s;
-
-	dbg("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d",
-	  usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum);
-
-	if (usbdev->descriptor.idVendor != 0x067b || usbdev->descriptor.idProduct > 0x1)
-		return NULL;
-
-	/* We don't handle multiple configurations */
-	if (usbdev->descriptor.bNumConfigurations != 1)
-		return NULL;
-
-	s = plusb_find_struct ();
-	if (!s)
-		return NULL;
-
-	s->usbdev = usbdev;
-
-	if (usb_set_configuration (s->usbdev, usbdev->config[0].bConfigurationValue) < 0) {
-		err("set_configuration failed");
-		return NULL;
-	}
-
-	if (usb_set_interface (s->usbdev, 0, 0) < 0) {
-		err("set_interface failed");
-		return NULL;
-	}
-
-#if (LINUX_VERSION_CODE < 0x020300)
- 	{
- 		int i;
-		
- 		/* For Kernel version 2.2, the driver is responsible for
- 		   allocating this memory. For version 2.4, the rules
- 		   have apparently changed, but there is a nifty function
- 		   'init_netdev' that might make this easier...  It's in 
- 		   ../net/net_init.c - but can we get there from here?  (no)
-		   -EZA
- 		*/
- 		
- 		/* Find the device number... we seem to have lost it... -EZA */
- 		for (i=0; i<NRPLUSB; i++) {
- 			if (&plusb[i] == s)
- 				break;
- 		}
- 	
- 		if(!s->net_dev.name) {
- 			s->net_dev.name = kmalloc(strlen("plusbXXXX"), GFP_KERNEL);
- 			sprintf (s->net_dev.name, "plusb%d", i);
- 			s->net_dev.init=plusb_net_init;
- 			s->net_dev.priv=s;
- 			
- 			printk ("plusb_probe: Registering Device\n");	
- 			if(!register_netdev(&s->net_dev))
- 				info("registered: %s", s->net_dev.name);
- 			else {
- 				err("register_netdev failed");
- 				s->net_dev.name[0] = '\0';
- 			}
- 			dbg ("plusb_probe: Connected!");
- 		}
- 	}
-#else
- 	/* Kernel version 2.3+ works a little bit differently than 2.2 */
-	if(!s->net_dev.name[0]) {
-		strcpy(s->net_dev.name, "plusb%d");
-		s->net_dev.init=plusb_net_init;
-		s->net_dev.priv=s;
-		if(!register_netdev(&s->net_dev))
-			info("registered: %s", s->net_dev.name);
-		else {
-			err("register_netdev failed");
-			s->net_dev.name[0] = '\0';
-		}
-	}
-#endif
-	
-	s->connected = 1;
-
-	if(s->opened) {
-		dbg("net device already allocated, restarting USB transfers");
-		plusb_alloc(s);
-	}
-
-	info("bound to interface: %d dev: %p", ifnum, usbdev);
-	MOD_INC_USE_COUNT;
-	return s;
-}
-/* --------------------------------------------------------------------- */
-
-static struct usb_driver plusb_driver =
-{
-	name: "plusb",
-	probe: plusb_probe,
-	disconnect: plusb_disconnect,
-};
-
-/* --------------------------------------------------------------------- */
-
-static int __init plusb_init (void)
-{
-	unsigned u;
-	dbg("plusb_init");
-	
-	/* initialize struct */
-	for (u = 0; u < NRPLUSB; u++) {
-		plusb_t *s = &plusb[u];
-		memset (s, 0, sizeof (plusb_t));
-		INIT_LIST_HEAD (&s->tx_skb_list);
-		INIT_LIST_HEAD (&s->free_skb_list);
-		spin_lock_init (&s->lock);
-	}
-
-	/* register misc device */
-	usb_register (&plusb_driver);
-
-	dbg("plusb_init: driver registered");
-
-	info(DRIVER_VERSION ":" DRIVER_DESC);
-
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void __exit plusb_cleanup (void)
-{
-	unsigned u;
-
-	dbg("plusb_cleanup");
-	for (u = 0; u < NRPLUSB; u++) {
-		plusb_t *s = &plusb[u];
-#if (LINUX_VERSION_CODE < 0x020300)
-		if(s->net_dev.name) {
-			dbg("unregistering netdev: %s",s->net_dev.name);
-			unregister_netdev(&s->net_dev);
-			s->net_dev.name[0] = '\0';
-			kfree (s->net_dev.name);
-			s->net_dev.name = NULL;		
-		}		
-#else
-		if(s->net_dev.name[0]) {
-			dbg("unregistering netdev: %s",s->net_dev.name);
-			unregister_netdev(&s->net_dev);
-			s->net_dev.name[0] = '\0';
-		}
-#endif
-	}
-	usb_deregister (&plusb_driver);
-	dbg("plusb_cleanup: finished");
-}
-
-/* --------------------------------------------------------------------- */
-
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-
-
-module_init (plusb_init);
-module_exit (plusb_cleanup);
-
-/* --------------------------------------------------------------------- */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)