Ok. I didn't make 2.4.0 in 2000. Tough. I tried, but we had some
[davej-history.git] / drivers / usb / acm.c
blob88112996668c83c0cc6de39c0ee593300d6a88f1
1 /*
2 * acm.c Version 0.18
4 * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de>
5 * Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
6 * Copyright (c) 1999 Johannes Erdfelt <jerdfelt@valinux.com>
7 * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
9 * USB Abstract Control Model driver for USB modems and ISDN adapters
11 * Sponsored by SuSE
13 * ChangeLog:
14 * v0.9 - thorough cleaning, URBification, almost a rewrite
15 * v0.10 - some more cleanups
16 * v0.11 - fixed flow control, read error doesn't stop reads
17 * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced
18 * v0.13 - added termios, added hangup
19 * v0.14 - sized down struct acm
20 * v0.15 - fixed flow control again - characters could be lost
21 * v0.16 - added code for modems with swapped data and control interfaces
22 * v0.17 - added new style probing
23 * v0.18 - fixed new style probing for devices with more configurations
27 * This program is free software; you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License as published by
29 * the Free Software Foundation; either version 2 of the License, or
30 * (at your option) any later version.
32 * This program is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
37 * You should have received a copy of the GNU General Public License
38 * along with this program; if not, write to the Free Software
39 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
42 #include <linux/kernel.h>
43 #include <linux/sched.h>
44 #include <linux/signal.h>
45 #include <linux/errno.h>
46 #include <linux/poll.h>
47 #include <linux/init.h>
48 #include <linux/malloc.h>
49 #include <linux/fcntl.h>
50 #include <linux/tty_driver.h>
51 #include <linux/tty_flip.h>
52 #include <linux/tty.h>
53 #include <linux/module.h>
54 #undef DEBUG
55 #include <linux/usb.h>
58 * CMSPAR, some architectures can't have space and mark parity.
61 #ifndef CMSPAR
62 #define CMSPAR 0
63 #endif
66 * Major and minor numbers.
69 #define ACM_TTY_MAJOR 166
70 #define ACM_TTY_MINORS 32
73 * Requests.
76 #define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE)
78 #define ACM_REQ_COMMAND 0x00
79 #define ACM_REQ_RESPONSE 0x01
80 #define ACM_REQ_SET_FEATURE 0x02
81 #define ACM_REQ_GET_FEATURE 0x03
82 #define ACM_REQ_CLEAR_FEATURE 0x04
84 #define ACM_REQ_SET_LINE 0x20
85 #define ACM_REQ_GET_LINE 0x21
86 #define ACM_REQ_SET_CONTROL 0x22
87 #define ACM_REQ_SEND_BREAK 0x23
90 * IRQs.
93 #define ACM_IRQ_NETWORK 0x00
94 #define ACM_IRQ_LINE_STATE 0x20
97 * Output control lines.
100 #define ACM_CTRL_DTR 0x01
101 #define ACM_CTRL_RTS 0x02
104 * Input control lines and line errors.
107 #define ACM_CTRL_DCD 0x01
108 #define ACM_CTRL_DSR 0x02
109 #define ACM_CTRL_BRK 0x04
110 #define ACM_CTRL_RI 0x08
112 #define ACM_CTRL_FRAMING 0x10
113 #define ACM_CTRL_PARITY 0x20
114 #define ACM_CTRL_OVERRUN 0x40
117 * Line speed and caracter encoding.
120 struct acm_line {
121 __u32 speed;
122 __u8 stopbits;
123 __u8 parity;
124 __u8 databits;
125 }__attribute__((packed));
128 * Internal driver structures.
131 struct acm {
132 struct usb_device *dev;/* the coresponding usb device */
133 struct usb_interface *iface;/* the interfaces - +0 control +1 data */
134 struct tty_struct *tty;/* the coresponding tty */
135 struct urb ctrlurb, readurb, writeurb;/* urbs */
136 struct acm_line line;/* line coding (bits, stop, parity) */
137 struct tq_struct tqueue;/* task queue for line discipline waking up */
138 unsigned int ctrlin;/* input control lines (DCD, DSR, RI, break, overruns) */
139 unsigned int ctrlout;/* output control lines (DTR, RTS) */
140 unsigned int writesize;/* max packet size for the output bulk endpoint */
141 unsigned int used;/* someone has this acm's device open */
142 unsigned int minor;/* acm minor number */
143 unsigned char throttle;/* throttled by tty layer */
144 unsigned char clocal;/* termios CLOCAL */
147 static struct usb_driver acm_driver;
148 static struct tty_driver acm_tty_driver;
149 static struct acm *acm_table[ACM_TTY_MINORS];
151 #define ACM_READY(acm) (acm && acm->dev && acm->used)
154 * Functions for ACM control messages.
157 static intacm_ctrl_msg(struct acm *acm,int request,int value,void*buf,int len)
159 int retval =usb_control_msg(acm->dev,usb_sndctrlpipe(acm->dev,0),
160 request, USB_RT_ACM, value, acm->iface[0].altsetting[0].bInterfaceNumber, buf, len, HZ *5);
161 dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
162 return retval <0? retval :0;
165 #define acm_set_control(acm, control) acm_ctrl_msg(acm, ACM_REQ_SET_CONTROL, control, NULL, 0)
166 #define acm_set_line(acm, line) acm_ctrl_msg(acm, ACM_REQ_SET_LINE, 0, line, sizeof(struct acm_line))
167 #define acm_send_break(acm, ms) acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0)
170 * Interrupt handler for various ACM control events
173 static voidacm_ctrl_irq(struct urb *urb)
175 struct acm *acm = urb->context;
176 devrequest *dr = urb->transfer_buffer;
177 unsigned char*data = (unsigned char*)(dr +1);
178 int newctrl;
180 if(!ACM_READY(acm))return;
182 if(urb->status <0) {
183 dbg("nonzero ctrl irq status received: %d", urb->status);
184 return;
187 switch(dr->request) {
189 case ACM_IRQ_NETWORK:
191 dbg("%s network", data[0] ?"connected to":"disconnected from");
192 return;
194 case ACM_IRQ_LINE_STATE:
196 newctrl =le16_to_cpup((__u16 *) data);
198 #if 0
199 /* Please someone tell me how to do this properly to kill pppd and not kill minicom */
200 if(acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
201 dbg("calling hangup");
202 tty_hangup(acm->tty);
204 #endif
206 acm->ctrlin = newctrl;
208 dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
209 acm->ctrlin & ACM_CTRL_DCD ?'+':'-', acm->ctrlin & ACM_CTRL_DSR ?'+':'-',
210 acm->ctrlin & ACM_CTRL_BRK ?'+':'-', acm->ctrlin & ACM_CTRL_RI ?'+':'-',
211 acm->ctrlin & ACM_CTRL_FRAMING ?'+':'-', acm->ctrlin & ACM_CTRL_PARITY ?'+':'-',
212 acm->ctrlin & ACM_CTRL_OVERRUN ?'+':'-');
214 return;
216 default:
217 dbg("unknown control event received: request %d index %d len %d data0 %d data1 %d",
218 dr->request, dr->index, dr->length, data[0], data[1]);
219 return;
223 static voidacm_read_bulk(struct urb *urb)
225 struct acm *acm = urb->context;
226 struct tty_struct *tty = acm->tty;
227 unsigned char*data = urb->transfer_buffer;
228 int i =0;
230 if(!ACM_READY(acm))return;
232 if(urb->status)
233 dbg("nonzero read bulk status received: %d", urb->status);
235 if(!urb->status & !acm->throttle) {
236 for(i =0; i < urb->actual_length && !acm->throttle; i++)
237 tty_insert_flip_char(tty, data[i],0);
238 tty_flip_buffer_push(tty);
241 if(acm->throttle) {
242 memmove(data, data + i, urb->actual_length - i);
243 urb->actual_length -= i;
244 return;
247 urb->actual_length =0;
248 urb->dev = acm->dev;
250 if(usb_submit_urb(urb))
251 dbg("failed resubmitting read urb");
254 static voidacm_write_bulk(struct urb *urb)
256 struct acm *acm = (struct acm *)urb->context;
258 if(!ACM_READY(acm))return;
260 if(urb->status)
261 dbg("nonzero write bulk status received: %d", urb->status);
263 queue_task(&acm->tqueue, &tq_immediate);
264 mark_bh(IMMEDIATE_BH);
267 static voidacm_softint(void*private)
269 struct acm *acm =private;
270 struct tty_struct *tty = acm->tty;
272 if(!ACM_READY(acm))return;
274 if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
275 (tty->ldisc.write_wakeup)(tty);
277 wake_up_interruptible(&tty->write_wait);
281 * TTY handlers
284 static intacm_tty_open(struct tty_struct *tty,struct file *filp)
286 struct acm *acm = acm_table[MINOR(tty->device)];
288 if(!acm || !acm->dev)return-EINVAL;
290 tty->driver_data = acm;
291 acm->tty = tty;
293 MOD_INC_USE_COUNT;
295 if(acm->used++)return0;
297 acm->ctrlurb.dev = acm->dev;
298 if(usb_submit_urb(&acm->ctrlurb))
299 dbg("usb_submit_urb(ctrl irq) failed");
301 acm->readurb.dev = acm->dev;
302 if(usb_submit_urb(&acm->readurb))
303 dbg("usb_submit_urb(read bulk) failed");
305 acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS);
307 return0;
310 static voidacm_tty_close(struct tty_struct *tty,struct file *filp)
312 struct acm *acm = tty->driver_data;
314 if(!acm || !acm->used)return;
316 if(!--acm->used) {
317 if(acm->dev) {
318 acm_set_control(acm, acm->ctrlout =0);
319 usb_unlink_urb(&acm->ctrlurb);
320 usb_unlink_urb(&acm->writeurb);
321 usb_unlink_urb(&acm->readurb);
322 }else{
323 tty_unregister_devfs(&acm_tty_driver, acm->minor);
324 acm_table[acm->minor] = NULL;
325 kfree(acm);
328 MOD_DEC_USE_COUNT;
331 static intacm_tty_write(struct tty_struct *tty,int from_user,const unsigned char*buf,int count)
333 struct acm *acm = tty->driver_data;
335 if(!ACM_READY(acm))return-EINVAL;
336 if(acm->writeurb.status == -EINPROGRESS)return0;
337 if(!count)return0;
339 count = (count > acm->writesize) ? acm->writesize : count;
341 if(from_user)
342 copy_from_user(acm->writeurb.transfer_buffer, buf, count);
343 else
344 memcpy(acm->writeurb.transfer_buffer, buf, count);
346 acm->writeurb.transfer_buffer_length = count;
347 acm->writeurb.dev = acm->dev;
349 if(usb_submit_urb(&acm->writeurb))
350 dbg("usb_submit_urb(write bulk) failed");
352 return count;
355 static intacm_tty_write_room(struct tty_struct *tty)
357 struct acm *acm = tty->driver_data;
358 if(!ACM_READY(acm))return-EINVAL;
359 return acm->writeurb.status == -EINPROGRESS ?0: acm->writesize;
362 static intacm_tty_chars_in_buffer(struct tty_struct *tty)
364 struct acm *acm = tty->driver_data;
365 if(!ACM_READY(acm))return-EINVAL;
366 return acm->writeurb.status == -EINPROGRESS ? acm->writeurb.transfer_buffer_length :0;
369 static voidacm_tty_throttle(struct tty_struct *tty)
371 struct acm *acm = tty->driver_data;
372 if(!ACM_READY(acm))return;
373 acm->throttle =1;
376 static voidacm_tty_unthrottle(struct tty_struct *tty)
378 struct acm *acm = tty->driver_data;
379 if(!ACM_READY(acm))return;
380 acm->throttle =0;
381 if(acm->readurb.status != -EINPROGRESS)
382 acm_read_bulk(&acm->readurb);
385 static voidacm_tty_break_ctl(struct tty_struct *tty,int state)
387 struct acm *acm = tty->driver_data;
388 if(!ACM_READY(acm))return;
389 if(acm_send_break(acm, state ?0xffff:0))
390 dbg("send break failed");
393 static intacm_tty_ioctl(struct tty_struct *tty,struct file *file,unsigned int cmd,unsigned long arg)
395 struct acm *acm = tty->driver_data;
396 unsigned int retval, mask, newctrl;
398 if(!ACM_READY(acm))return-EINVAL;
400 switch(cmd) {
402 case TIOCMGET:
404 returnput_user((acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR :0) |
405 (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS :0) |
406 (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR :0) |
407 (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI :0) |
408 (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD :0) |
409 TIOCM_CTS, (unsigned long*) arg);
411 case TIOCMSET:
412 case TIOCMBIS:
413 case TIOCMBIC:
415 if((retval =get_user(mask, (unsigned long*) arg)))return retval;
417 newctrl = acm->ctrlout;
418 mask = (mask & TIOCM_DTR ? ACM_CTRL_DTR :0) | (mask & TIOCM_RTS ? ACM_CTRL_RTS :0);
420 switch(cmd) {
421 case TIOCMSET: newctrl = mask;break;
422 case TIOCMBIS: newctrl |= mask;break;
423 case TIOCMBIC: newctrl &= ~mask;break;
426 if(acm->ctrlout == newctrl)return0;
427 returnacm_set_control(acm, acm->ctrlout = newctrl);
430 return-ENOIOCTLCMD;
433 static __u32 acm_tty_speed[] = {
434 0,50,75,110,134,150,200,300,600,
435 1200,1800,2400,4800,9600,19200,38400,
436 57600,115200,230400,460800,500000,576000,
437 921600,1000000,1152000,1500000,2000000,
438 2500000,3000000,3500000,4000000
441 static __u8 acm_tty_size[] = {
442 5,6,7,8
445 static voidacm_tty_set_termios(struct tty_struct *tty,struct termios *termios_old)
447 struct acm *acm = tty->driver_data;
448 struct termios *termios = tty->termios;
449 struct acm_line newline;
450 int newctrl = acm->ctrlout;
452 if(!ACM_READY(acm))return;
454 newline.speed =cpu_to_le32p(acm_tty_speed +
455 (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ?15:0));
456 newline.stopbits = termios->c_cflag & CSTOPB ?2:0;
457 newline.parity = termios->c_cflag & PARENB ?
458 (termios->c_cflag & PARODD ?1:2) + (termios->c_cflag & CMSPAR ?2:0) :0;
459 newline.databits = acm_tty_size[(termios->c_cflag & CSIZE) >>4];
461 acm->clocal = termios->c_cflag & CLOCAL;
463 if(!newline.speed) {
464 newline.speed = acm->line.speed;
465 newctrl &= ~ACM_CTRL_DTR;
466 }else newctrl |= ACM_CTRL_DTR;
468 if(newctrl != acm->ctrlout)
469 acm_set_control(acm, acm->ctrlout = newctrl);
471 if(memcmp(&acm->line, &newline,sizeof(struct acm_line))) {
472 memcpy(&acm->line, &newline,sizeof(struct acm_line));
473 dbg("set line: %d %d %d %d", newline.speed, newline.stopbits, newline.parity, newline.databits);
474 acm_set_line(acm, &acm->line);
479 * USB probe and disconnect routines.
482 static void*acm_probe(struct usb_device *dev,unsigned int ifnum,
483 const struct usb_device_id *id)
485 struct acm *acm;
486 struct usb_config_descriptor *cfacm;
487 struct usb_interface_descriptor *ifcom, *ifdata;
488 struct usb_endpoint_descriptor *epctrl, *epread, *epwrite;
489 int readsize, ctrlsize, minor, i;
490 unsigned char*buf;
493 * Since 0 is treated as a wildcard by the USB pattern matching,
494 * we explicitly check bDeviceSubClass and bDeviceProtocol here.
497 if(dev->descriptor.bDeviceSubClass !=0||
498 dev->descriptor.bDeviceProtocol !=0)
499 return NULL;
501 for(i =0; i < dev->descriptor.bNumConfigurations; i++) {
503 cfacm = dev->config + i;
505 dbg("probing config %d", cfacm->bConfigurationValue);
507 if(cfacm->bNumInterfaces !=2||
508 usb_interface_claimed(cfacm->interface +0) ||
509 usb_interface_claimed(cfacm->interface +1))
510 continue;
512 ifcom = cfacm->interface[0].altsetting +0;
513 ifdata = cfacm->interface[1].altsetting +0;
515 if(ifdata->bInterfaceClass !=10|| ifdata->bNumEndpoints <2) {
516 ifcom = cfacm->interface[1].altsetting +0;
517 ifdata = cfacm->interface[0].altsetting +0;
518 if(ifdata->bInterfaceClass !=10|| ifdata->bNumEndpoints <2)
519 continue;
522 if(ifcom->bInterfaceClass !=2|| ifcom->bInterfaceSubClass !=2||
523 ifcom->bInterfaceProtocol !=1|| ifcom->bNumEndpoints <1)
524 continue;
526 epctrl = ifcom->endpoint +0;
527 epread = ifdata->endpoint +0;
528 epwrite = ifdata->endpoint +1;
530 if((epctrl->bEndpointAddress &0x80) !=0x80|| (epctrl->bmAttributes &3) !=3||
531 (epread->bmAttributes &3) !=2|| (epwrite->bmAttributes &3) !=2||
532 ((epread->bEndpointAddress &0x80) ^ (epwrite->bEndpointAddress &0x80)) !=0x80)
533 continue;
535 if((epread->bEndpointAddress &0x80) !=0x80) {
536 epread = ifdata->endpoint +1;
537 epwrite = ifdata->endpoint +0;
540 usb_set_configuration(dev, cfacm->bConfigurationValue);
542 for(minor =0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
543 if(acm_table[minor]) {
544 err("no more free acm devices");
545 return NULL;
548 if(!(acm =kmalloc(sizeof(struct acm), GFP_KERNEL))) {
549 err("out of memory");
550 return NULL;
552 memset(acm,0,sizeof(struct acm));
554 ctrlsize = epctrl->wMaxPacketSize;
555 readsize = epread->wMaxPacketSize;
556 acm->writesize = epwrite->wMaxPacketSize;
557 acm->iface = cfacm->interface;
558 acm->minor = minor;
559 acm->dev = dev;
561 acm->tqueue.routine = acm_softint;
562 acm->tqueue.data = acm;
564 if(!(buf =kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
565 err("out of memory");
566 kfree(acm);
567 return NULL;
570 FILL_INT_URB(&acm->ctrlurb, dev,usb_rcvintpipe(dev, epctrl->bEndpointAddress),
571 buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
573 FILL_BULK_URB(&acm->readurb, dev,usb_rcvbulkpipe(dev, epread->bEndpointAddress),
574 buf += ctrlsize, readsize, acm_read_bulk, acm);
575 acm->readurb.transfer_flags |= USB_NO_FSBR;
577 FILL_BULK_URB(&acm->writeurb, dev,usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
578 buf += readsize, acm->writesize, acm_write_bulk, acm);
579 acm->writeurb.transfer_flags |= USB_NO_FSBR;
581 printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor);
583 acm_set_control(acm, acm->ctrlout);
585 acm->line.speed =cpu_to_le32(9600);
586 acm->line.databits =8;
587 acm_set_line(acm, &acm->line);
589 usb_driver_claim_interface(&acm_driver, acm->iface +0, acm);
590 usb_driver_claim_interface(&acm_driver, acm->iface +1, acm);
592 tty_register_devfs(&acm_tty_driver,0, minor);
593 return acm_table[minor] = acm;
596 return NULL;
599 static voidacm_disconnect(struct usb_device *dev,void*ptr)
601 struct acm *acm = ptr;
603 if(!acm || !acm->dev) {
604 dbg("disconnect on nonexisting interface");
605 return;
608 acm->dev = NULL;
610 usb_unlink_urb(&acm->ctrlurb);
611 usb_unlink_urb(&acm->readurb);
612 usb_unlink_urb(&acm->writeurb);
614 kfree(acm->ctrlurb.transfer_buffer);
616 usb_driver_release_interface(&acm_driver, acm->iface +0);
617 usb_driver_release_interface(&acm_driver, acm->iface +1);
619 if(!acm->used) {
620 tty_unregister_devfs(&acm_tty_driver, acm->minor);
621 acm_table[acm->minor] = NULL;
622 kfree(acm);
623 return;
626 if(acm->tty)
627 tty_hangup(acm->tty);
631 * USB driver structure.
634 static struct usb_device_id acm_ids[] = {
635 { bDeviceClass:2, bDeviceSubClass:0, bDeviceProtocol:0},
639 MODULE_DEVICE_TABLE(usb, acm_ids);
641 static struct usb_driver acm_driver = {
642 name:"acm",
643 probe: acm_probe,
644 disconnect: acm_disconnect,
645 id_table: acm_ids,
649 * TTY driver structures.
652 static int acm_tty_refcount;
654 static struct tty_struct *acm_tty_table[ACM_TTY_MINORS];
655 static struct termios *acm_tty_termios[ACM_TTY_MINORS];
656 static struct termios *acm_tty_termios_locked[ACM_TTY_MINORS];
658 static struct tty_driver acm_tty_driver = {
659 magic: TTY_DRIVER_MAGIC,
660 driver_name:"acm",
661 name:"usb/acm/%d",
662 major: ACM_TTY_MAJOR,
663 minor_start:0,
664 num: ACM_TTY_MINORS,
665 type: TTY_DRIVER_TYPE_SERIAL,
666 subtype: SERIAL_TYPE_NORMAL,
667 flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
669 refcount: &acm_tty_refcount,
671 table: acm_tty_table,
672 termios: acm_tty_termios,
673 termios_locked: acm_tty_termios_locked,
675 open: acm_tty_open,
676 close: acm_tty_close,
677 write: acm_tty_write,
678 write_room: acm_tty_write_room,
679 ioctl: acm_tty_ioctl,
680 throttle: acm_tty_throttle,
681 unthrottle: acm_tty_unthrottle,
682 chars_in_buffer: acm_tty_chars_in_buffer,
683 break_ctl: acm_tty_break_ctl,
684 set_termios: acm_tty_set_termios
688 * Init / exit.
691 static int __init acm_init(void)
693 acm_tty_driver.init_termios = tty_std_termios;
694 acm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
696 if(tty_register_driver(&acm_tty_driver))
697 return-1;
699 if(usb_register(&acm_driver) <0) {
700 tty_unregister_driver(&acm_tty_driver);
701 return-1;
704 return0;
707 static void __exit acm_exit(void)
709 usb_deregister(&acm_driver);
710 tty_unregister_driver(&acm_tty_driver);
713 module_init(acm_init);
714 module_exit(acm_exit);
716 MODULE_AUTHOR("Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik");
717 MODULE_DESCRIPTION("USB Abstract Control Model driver for USB modems and ISDN adapters");
close