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
- Lines: 1061
- Date:
Wed Dec 31 16:00:00 1969
- Orig file:
v2.4.9/linux/drivers/usb/plusb.c
- Orig date:
Tue Jul 3 17:08:21 2001
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)