📄 plusb.c
字号:
/*****************************************************************************//* * 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/* 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 32768typedef 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 sucessfully * 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 occured. */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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -