📄 plusb.c
字号:
/*****************************************************************************//* * plusb.c -- prolific pl-2302 driver. * * Copyright (C) 2000 Deti Fliegl (deti@fliegl.de) * * 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. * * * * $Id: plusb.c,v 1.19 2000/03/13 16:44:42 acher Exp $ * *//*****************************************************************************/#include <linux/config.h>#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#include <linux/usb.h>#include "plusb.h"/* --------------------------------------------------------------------- */#define NRPLUSB 4/*-------------------------------------------------------------------*/static plusb_t plusb[NRPLUSB];/* --------------------------------------------------------------------- */static int plusb_add_buf_tail (plusb_t *s, struct list_head *dst, struct list_head *src){ unsigned long flags; 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;}/*-------------------------------------------------------------------*/static int plusb_my_bulk(plusb_t *s, int pipe, void *data, int size, int *actual_length){ int ret; dbg("plusb_my_bulk: len:%d",size); ret=usb_bulk_msg(s->usbdev, pipe, data, size, actual_length, 500); if(ret<0) { err("plusb: usb_bulk_msg failed(%d)",ret); } if( ret == -EPIPE ) { warn("CLEAR_FEATURE request to remove STALL condition."); if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe))) err("request failed"); } dbg("plusb_my_bulk: finished act: %d", *actual_length); return ret;}/* --------------------------------------------------------------------- */static void plusb_bh(void *context){ plusb_t *s=context; struct net_device_stats *stats=&s->net_stats; int ret=0; int actual_length; skb_list_t *skb_list; struct sk_buff *skb; dbg("plusb_bh: i:%d",in_interrupt()); while(!list_empty(&s->tx_skb_list)) { if(!(s->status&_PLUSB_TXOK)) break; skb_list = list_entry (s->tx_skb_list.next, skb_list_t, skb_list); if(!skb_list->state) { dbg("plusb_bh: not yet ready"); schedule(); continue; } skb=skb_list->skb; ret=plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE), skb->data, skb->len, &actual_length); if(ret || skb->len != actual_length ||!(skb->len%64)) { plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE), NULL, 0, &actual_length); } if(!ret) { stats->tx_packets++; stats->tx_bytes+=skb->len; } else { stats->tx_errors++; stats->tx_aborted_errors++; } dbg("plusb_bh: dev_kfree_skb"); dev_kfree_skb(skb); skb_list->state=0; plusb_add_buf_tail (s, &s->free_skb_list, &s->tx_skb_list); } dbg("plusb_bh: finished"); s->in_bh=0;}/* --------------------------------------------------------------------- */static int plusb_net_xmit(struct sk_buff *skb, struct net_device *dev){ plusb_t *s=dev->priv; skb_list_t *skb_list; int ret=NET_XMIT_SUCCESS; dbg("plusb_net_xmit: len:%d i:%d",skb->len,in_interrupt()); if(!s->connected || list_empty(&s->free_skb_list)) { ret=NET_XMIT_CN; goto lab; } plusb_add_buf_tail (s, &s->tx_skb_list, &s->free_skb_list); skb_list = list_entry (s->tx_skb_list.prev, skb_list_t, skb_list); skb_list->skb=skb; skb_list->state=1;lab: if(s->in_bh) return ret; dbg("plusb_net_xmit: queue_task"); s->in_bh=1; queue_task(&s->bh, &tq_scheduler); dbg("plusb_net_xmit: finished"); return ret;}/* --------------------------------------------------------------------- */static void plusb_bulk_complete(urb_t *purb){ plusb_t *s=purb->context; dbg("plusb_bulk_complete: status:%d length:%d",purb->status,purb->actual_length); if(!s->connected) return; 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) { err("plusb_bulk_complete: dev_alloc_skb(%d)=NULL, dropping frame",len); stats->rx_dropped++; return; } 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); } else purb->status=0;}/* --------------------------------------------------------------------- */static void plusb_int_complete(urb_t *purb){ plusb_t *s=purb->context; s->status=((unsigned char*)purb->transfer_buffer)[0]&255;#if 0 if((s->status&0x3f)!=0x20) { warn("invalid device status %02X", s->status); return; }#endif if(!s->connected) return; if(s->status&_PLUSB_RXD) { int ret; if(s->bulkurb->status) { err("plusb_int_complete: URB still in use"); return; } ret=usb_submit_urb(s->bulkurb); if(ret && ret!=-EBUSY) { err("plusb_int_complete: usb_submit_urb failed"); } } if(purb->status || s->status!=160) dbg("status: %p %d buf: %02X", purb->dev, purb->status, s->status);}/* --------------------------------------------------------------------- */static void plusb_free_all(plusb_t *s){ struct list_head *skb; skb_list_t *skb_list; dbg("plusb_free_all"); run_task_queue(&tq_immediate); if(s->inturb) { dbg("unlink inturb"); usb_unlink_urb(s->inturb); } if(s->inturb && s->inturb->transfer_buffer) { dbg("kfree inturb->transfer_buffer"); kfree(s->inturb->transfer_buffer); s->inturb->transfer_buffer=NULL; } if(s->inturb) { dbg("free_urb inturb"); usb_free_urb(s->inturb); s->inturb=NULL; } if(s->bulkurb) { dbg("unlink bulkurb"); usb_unlink_urb(s->bulkurb); } if(s->bulkurb && s->bulkurb->transfer_buffer) { dbg("kfree bulkurb->transfer_buffer"); kfree(s->bulkurb->transfer_buffer); s->bulkurb->transfer_buffer=NULL; } if(s->bulkurb) { dbg("free_urb bulkurb"); usb_free_urb(s->bulkurb); s->bulkurb=NULL; } 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); kfree(skb_list); } dbg("plusb_free_all: finished"); }/*-------------------------------------------------------------------*/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("bulkurb allocation:"); s->bulkurb=usb_alloc_urb(0); if(!s->bulkurb) { err("alloc_urb failed"); goto reject; } dbg("bulkurb/inturb init:"); s->inturb->dev=s->usbdev; s->inturb->pipe=usb_rcvintpipe (s->usbdev, _PLUSB_INTPIPE); s->inturb->transfer_buffer=kmalloc(64, GFP_KERNEL); if(!s->inturb->transfer_buffer) { err("kmalloc failed"); goto reject; } s->inturb->transfer_buffer_length=1; s->inturb->complete=plusb_int_complete; s->inturb->context=s; s->inturb->interval=10; dbg("inturb submission:"); if(usb_submit_urb(s->inturb)<0) { err("usb_submit_urb failed"); goto reject; } dbg("bulkurb init:"); s->bulkurb->dev=s->usbdev; s->bulkurb->pipe=usb_rcvbulkpipe (s->usbdev, _PLUSB_BULKINPIPE); s->bulkurb->transfer_buffer=kmalloc(_BULK_DATA_LEN, GFP_KERNEL); if(!s->bulkurb->transfer_buffer) { err("kmalloc failed"); goto reject; } s->bulkurb->transfer_buffer_length=_BULK_DATA_LEN; s->bulkurb->complete=plusb_bulk_complete; s->bulkurb->context=s; dbg("plusb_alloc: finished"); 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; dbg("plusb_net_open: success"); return 0; }/* --------------------------------------------------------------------- */static int plusb_net_stop(struct net_device *dev){ plusb_t *s=dev->priv; dbg("plusb_net_stop"); plusb_free_all(s); s->opened=0; 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"); s->connected = 0; plusb_free_all(s); if(!s->opened && s->net_dev.name) { dbg("unregistering netdev: %s",s->net_dev.name); unregister_netdev(&s->net_dev); kfree(s->net_dev.name); s->net_dev.name=NULL; } dbg("plusb_disconnect: finished"); MOD_DEC_USE_COUNT;}/* --------------------------------------------------------------------- */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->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){ 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(!s->net_dev.name) { s->net_dev.name=kmalloc(16, GFP_KERNEL); if(!s->net_dev.name || dev_alloc_name(&s->net_dev,"plusb%d")<0) { err("alloc name failed\n"); return NULL; } 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"); kfree(s->net_dev.name); s->net_dev.name=NULL; } } 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,};/* --------------------------------------------------------------------- */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)); s->bh.routine = (void (*)(void *))plusb_bh; s->bh.data = s; 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"); return 0;}/* --------------------------------------------------------------------- */void __exit plusb_cleanup (void){ unsigned u; dbg("plusb_cleanup"); for (u = 0; u < NRPLUSB; u++) { plusb_t *s = &plusb[u]; if(s->net_dev.name) { dbg("unregistering netdev: %s",s->net_dev.name); unregister_netdev(&s->net_dev); kfree(s->net_dev.name); s->net_dev.name=NULL; } } usb_deregister (&plusb_driver); dbg("plusb_cleanup: finished");}/* --------------------------------------------------------------------- */#ifdef MODULEMODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de");MODULE_DESCRIPTION ("PL-2302 USB Interface Driver for Linux (c)2000");/* --------------------------------------------------------------------- */int __init init_module (void){ return plusb_init ();}/* --------------------------------------------------------------------- */void __exit cleanup_module (void){ plusb_cleanup ();}#endif/* --------------------------------------------------------------------- */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -