1 /*****************************************************************************/
4 * plusb.c -- prolific pl-2302 driver.
6 * Copyright (C) 2000 Deti Fliegl (deti@fliegl.de)
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 * $Id: plusb.c,v 1.18 2000/02/14 10:38:58 fliegl Exp $
28 /*****************************************************************************/
30 #include <linux/module.h>
31 #include <linux/socket.h>
32 #include <linux/miscdevice.h>
33 #include <linux/list.h>
34 #include <linux/vmalloc.h>
35 #include <linux/slab.h>
36 #include <linux/init.h>
37 #include <asm/uaccess.h>
38 #include <asm/atomic.h>
39 #include <linux/delay.h>
40 #include <linux/netdevice.h>
41 #include <linux/etherdevice.h>
42 #include <linux/skbuff.h>
49 /* --------------------------------------------------------------------- */
53 /*-------------------------------------------------------------------*/
55 static plusb_t plusb
[NRPLUSB
];
57 /* --------------------------------------------------------------------- */
58 static int plusb_add_buf_tail (plusb_t
*s
, struct list_head
*dst
, struct list_head
*src
)
61 struct list_head
*tmp
;
64 spin_lock_irqsave (&s
->lock
, flags
);
66 if (list_empty (src
)) {
67 // no elements in source buffer
73 list_add_tail (tmp
, dst
);
75 err
: spin_unlock_irqrestore (&s
->lock
, flags
);
78 /*-------------------------------------------------------------------*/
80 static int plusb_my_bulk(plusb_t
*s
, int pipe
, void *data
, int size
, int *actual_length
)
84 dbg("plusb_my_bulk: len:%d",size
);
86 ret
=usb_bulk_msg(s
->usbdev
, pipe
, data
, size
, actual_length
, 500);
88 err("plusb: usb_bulk_msg failed(%d)",ret
);
92 warn("CLEAR_FEATURE request to remove STALL condition.");
93 if(usb_clear_halt(s
->usbdev
, usb_pipeendpoint(pipe
)))
94 err("request failed");
97 dbg("plusb_my_bulk: finished act: %d", *actual_length
);
101 /* --------------------------------------------------------------------- */
103 static void plusb_bh(void *context
)
106 struct net_device_stats
*stats
=&s
->net_stats
;
109 skb_list_t
*skb_list
;
112 dbg("plusb_bh: i:%d",in_interrupt());
114 while(!list_empty(&s
->tx_skb_list
)) {
116 if(!(s
->status
&_PLUSB_TXOK
))
119 skb_list
= list_entry (s
->tx_skb_list
.next
, skb_list_t
, skb_list
);
120 if(!skb_list
->state
) {
121 dbg("plusb_bh: not yet ready");
127 ret
=plusb_my_bulk(s
, usb_sndbulkpipe (s
->usbdev
, _PLUSB_BULKOUTPIPE
),
128 skb
->data
, skb
->len
, &actual_length
);
130 if(ret
|| skb
->len
!= actual_length
||!(skb
->len
%64)) {
131 plusb_my_bulk(s
, usb_sndbulkpipe (s
->usbdev
, _PLUSB_BULKOUTPIPE
),
132 NULL
, 0, &actual_length
);
137 stats
->tx_bytes
+=skb
->len
;
141 stats
->tx_aborted_errors
++;
144 dbg("plusb_bh: dev_kfree_skb");
148 plusb_add_buf_tail (s
, &s
->free_skb_list
, &s
->tx_skb_list
);
151 dbg("plusb_bh: finished");
155 /* --------------------------------------------------------------------- */
157 static int plusb_net_xmit(struct sk_buff
*skb
, struct net_device
*dev
)
159 plusb_t
*s
=dev
->priv
;
160 skb_list_t
*skb_list
;
161 int ret
=NET_XMIT_SUCCESS
;
163 dbg("plusb_net_xmit: len:%d i:%d",skb
->len
,in_interrupt());
165 if(!s
->connected
|| list_empty(&s
->free_skb_list
)) {
170 plusb_add_buf_tail (s
, &s
->tx_skb_list
, &s
->free_skb_list
);
171 skb_list
= list_entry (s
->tx_skb_list
.prev
, skb_list_t
, skb_list
);
179 dbg("plusb_net_xmit: queue_task");
182 queue_task(&s
->bh
, &tq_scheduler
);
184 dbg("plusb_net_xmit: finished");
189 /* --------------------------------------------------------------------- */
191 static void plusb_bulk_complete(urb_t
*purb
)
193 plusb_t
*s
=purb
->context
;
195 dbg("plusb_bulk_complete: status:%d length:%d",purb
->status
,purb
->actual_length
);
202 int len
=purb
->transfer_buffer_length
;
203 struct net_device_stats
*stats
=&s
->net_stats
;
205 skb
=dev_alloc_skb(len
);
208 err("plusb_bulk_complete: dev_alloc_skb(%d)=NULL, dropping frame",len
);
213 dst
=(char *)skb_put(skb
, len
);
214 memcpy( dst
, purb
->transfer_buffer
, len
);
216 skb
->dev
=&s
->net_dev
;
217 skb
->protocol
=eth_type_trans(skb
, skb
->dev
);
219 stats
->rx_bytes
+=len
;
226 /* --------------------------------------------------------------------- */
228 static void plusb_int_complete(urb_t
*purb
)
230 plusb_t
*s
=purb
->context
;
231 s
->status
=((unsigned char*)purb
->transfer_buffer
)[0]&255;
233 if((s
->status
&0x3f)!=0x20) {
234 warn("invalid device status %02X", s
->status
);
241 if(s
->status
&_PLUSB_RXD
) {
244 if(s
->bulkurb
->status
) {
245 err("plusb_int_complete: URB still in use");
249 ret
=usb_submit_urb(s
->bulkurb
);
250 if(ret
&& ret
!=-EBUSY
) {
251 err("plusb_int_complete: usb_submit_urb failed");
255 if(purb
->status
|| s
->status
!=160)
256 dbg("status: %p %d buf: %02X", purb
->dev
, purb
->status
, s
->status
);
259 /* --------------------------------------------------------------------- */
261 static void plusb_free_all(plusb_t
*s
)
263 struct list_head
*skb
;
264 skb_list_t
*skb_list
;
266 dbg("plusb_free_all");
267 run_task_queue(&tq_immediate
);
270 dbg("unlink inturb");
271 usb_unlink_urb(s
->inturb
);
274 if(s
->inturb
&& s
->inturb
->transfer_buffer
) {
275 dbg("kfree inturb->transfer_buffer");
276 kfree(s
->inturb
->transfer_buffer
);
277 s
->inturb
->transfer_buffer
=NULL
;
281 dbg("free_urb inturb");
282 usb_free_urb(s
->inturb
);
287 dbg("unlink bulkurb");
288 usb_unlink_urb(s
->bulkurb
);
291 if(s
->bulkurb
&& s
->bulkurb
->transfer_buffer
) {
292 dbg("kfree bulkurb->transfer_buffer");
293 kfree(s
->bulkurb
->transfer_buffer
);
294 s
->bulkurb
->transfer_buffer
=NULL
;
297 dbg("free_urb bulkurb");
298 usb_free_urb(s
->bulkurb
);
302 while(!list_empty(&s
->free_skb_list
)) {
303 skb
=s
->free_skb_list
.next
;
305 skb_list
= list_entry (skb
, skb_list_t
, skb_list
);
309 while(!list_empty(&s
->tx_skb_list
)) {
310 skb
=s
->tx_skb_list
.next
;
312 skb_list
= list_entry (skb
, skb_list_t
, skb_list
);
315 dbg("plusb_free_all: finished");
318 /*-------------------------------------------------------------------*/
320 static int plusb_alloc(plusb_t
*s
)
327 for(i
=0 ; i
< _SKB_NUM
; i
++) {
328 skb
=kmalloc(sizeof(skb_list_t
), GFP_KERNEL
);
330 err("kmalloc for skb_list failed");
333 memset(skb
, 0, sizeof(skb_list_t
));
334 list_add(&skb
->skb_list
, &s
->free_skb_list
);
337 dbg("inturb allocation:");
338 s
->inturb
=usb_alloc_urb(0);
340 err("alloc_urb failed");
344 dbg("bulkurb allocation:");
345 s
->bulkurb
=usb_alloc_urb(0);
347 err("alloc_urb failed");
351 dbg("bulkurb/inturb init:");
352 s
->inturb
->dev
=s
->usbdev
;
353 s
->inturb
->pipe
=usb_rcvintpipe (s
->usbdev
, _PLUSB_INTPIPE
);
354 s
->inturb
->transfer_buffer
=kmalloc(64, GFP_KERNEL
);
355 if(!s
->inturb
->transfer_buffer
) {
356 err("kmalloc failed");
360 s
->inturb
->transfer_buffer_length
=1;
361 s
->inturb
->complete
=plusb_int_complete
;
362 s
->inturb
->context
=s
;
363 s
->inturb
->interval
=10;
365 dbg("inturb submission:");
366 if(usb_submit_urb(s
->inturb
)<0) {
367 err("usb_submit_urb failed");
371 dbg("bulkurb init:");
372 s
->bulkurb
->dev
=s
->usbdev
;
373 s
->bulkurb
->pipe
=usb_rcvbulkpipe (s
->usbdev
, _PLUSB_BULKINPIPE
);
374 s
->bulkurb
->transfer_buffer
=kmalloc(_BULK_DATA_LEN
, GFP_KERNEL
);
375 if(!s
->bulkurb
->transfer_buffer
) {
376 err("kmalloc failed");
380 s
->bulkurb
->transfer_buffer_length
=_BULK_DATA_LEN
;
381 s
->bulkurb
->complete
=plusb_bulk_complete
;
382 s
->bulkurb
->context
=s
;
384 dbg("plusb_alloc: finished");
389 dbg("plusb_alloc: failed");
395 /*-------------------------------------------------------------------*/
397 static int plusb_net_open(struct net_device
*dev
)
399 plusb_t
*s
=dev
->priv
;
401 dbg("plusb_net_open");
409 dbg("plusb_net_open: success");
415 /* --------------------------------------------------------------------- */
417 static int plusb_net_stop(struct net_device
*dev
)
419 plusb_t
*s
=dev
->priv
;
421 dbg("plusb_net_stop");
426 dbg("plusb_net_stop:finished");
430 /* --------------------------------------------------------------------- */
432 static struct net_device_stats
*plusb_net_get_stats(struct net_device
*dev
)
434 plusb_t
*s
=dev
->priv
;
436 dbg("net_device_stats");
438 return &s
->net_stats
;
441 /* --------------------------------------------------------------------- */
443 static plusb_t
*plusb_find_struct (void)
447 for (u
= 0; u
< NRPLUSB
; u
++) {
448 plusb_t
*s
= &plusb
[u
];
455 /* --------------------------------------------------------------------- */
457 static void plusb_disconnect (struct usb_device
*usbdev
, void *ptr
)
461 dbg("plusb_disconnect");
466 if(!s
->opened
&& s
->net_dev
.name
) {
467 dbg("unregistering netdev: %s",s
->net_dev
.name
);
468 unregister_netdev(&s
->net_dev
);
469 kfree(s
->net_dev
.name
);
470 s
->net_dev
.name
=NULL
;
473 dbg("plusb_disconnect: finished");
477 /* --------------------------------------------------------------------- */
479 int plusb_net_init(struct net_device
*dev
)
481 dbg("plusb_net_init");
483 dev
->open
=plusb_net_open
;
484 dev
->stop
=plusb_net_stop
;
485 dev
->hard_start_xmit
=plusb_net_xmit
;
486 dev
->get_stats
= plusb_net_get_stats
;
488 dev
->tx_queue_len
= 0;
489 dev
->flags
= IFF_POINTOPOINT
|IFF_NOARP
;
492 dbg("plusb_net_init: finished");
496 /* --------------------------------------------------------------------- */
498 static void *plusb_probe (struct usb_device
*usbdev
, unsigned int ifnum
)
502 dbg("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d",
503 usbdev
->descriptor
.idVendor
, usbdev
->descriptor
.idProduct
, ifnum
);
505 if (usbdev
->descriptor
.idVendor
!= 0x067b || usbdev
->descriptor
.idProduct
!= 0x1)
508 /* We don't handle multiple configurations */
509 if (usbdev
->descriptor
.bNumConfigurations
!= 1)
512 s
= plusb_find_struct ();
518 if (usb_set_configuration (s
->usbdev
, usbdev
->config
[0].bConfigurationValue
) < 0) {
519 err("set_configuration failed");
523 if (usb_set_interface (s
->usbdev
, 0, 0) < 0) {
524 err("set_interface failed");
528 if(!s
->net_dev
.name
) {
529 s
->net_dev
.name
=kmalloc(16, GFP_KERNEL
);
531 if(!s
->net_dev
.name
|| dev_alloc_name(&s
->net_dev
,"plusb%d")<0) {
532 err("alloc name failed\n");
536 s
->net_dev
.init
=plusb_net_init
;
538 if(!register_netdev(&s
->net_dev
))
539 info("registered: %s", s
->net_dev
.name
);
541 err("register_netdev failed");
542 kfree(s
->net_dev
.name
);
543 s
->net_dev
.name
=NULL
;
550 dbg("net device already allocated, restarting USB transfers");
554 info("bound to interface: %d dev: %p", ifnum
, usbdev
);
558 /* --------------------------------------------------------------------- */
560 static struct usb_driver plusb_driver
=
564 disconnect
: plusb_disconnect
,
567 /* --------------------------------------------------------------------- */
569 int __init
plusb_init (void)
574 /* initialize struct */
575 for (u
= 0; u
< NRPLUSB
; u
++) {
576 plusb_t
*s
= &plusb
[u
];
577 memset (s
, 0, sizeof (plusb_t
));
578 s
->bh
.routine
= (void (*)(void *))plusb_bh
;
580 INIT_LIST_HEAD (&s
->tx_skb_list
);
581 INIT_LIST_HEAD (&s
->free_skb_list
);
582 spin_lock_init (&s
->lock
);
585 /* register misc device */
586 usb_register (&plusb_driver
);
588 dbg("plusb_init: driver registered");
593 /* --------------------------------------------------------------------- */
595 void __exit
plusb_cleanup (void)
599 dbg("plusb_cleanup");
600 for (u
= 0; u
< NRPLUSB
; u
++) {
601 plusb_t
*s
= &plusb
[u
];
602 if(s
->net_dev
.name
) {
603 dbg("unregistering netdev: %s",s
->net_dev
.name
);
604 unregister_netdev(&s
->net_dev
);
605 kfree(s
->net_dev
.name
);
606 s
->net_dev
.name
=NULL
;
609 usb_deregister (&plusb_driver
);
610 dbg("plusb_cleanup: finished");
613 /* --------------------------------------------------------------------- */
616 MODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de");
617 MODULE_DESCRIPTION ("PL-2302 USB Interface Driver for Linux (c)2000");
619 /* --------------------------------------------------------------------- */
620 int __init
init_module (void)
622 return plusb_init ();
624 /* --------------------------------------------------------------------- */
625 void __exit
cleanup_module (void)
632 /* --------------------------------------------------------------------- */