patch-2.3.35 linux/drivers/usb/acm.c
Next file: linux/drivers/usb/audio.c
Previous file: linux/drivers/usb/README.scanner_hp_sane
Back to the patch index
Back to the overall index
- Lines: 999
- Date:
Mon Dec 27 14:03:38 1999
- Orig file:
v2.3.34/linux/drivers/usb/acm.c
- Orig date:
Mon Dec 20 18:48:22 1999
diff -u --recursive --new-file v2.3.34/linux/drivers/usb/acm.c linux/drivers/usb/acm.c
@@ -1,53 +1,34 @@
/*
- * USB Abstract Control Model based on Brad Keryan's USB busmouse driver
+ * acm.c Version 0.10
*
- * (C) Copyright 1999 Armin Fuerst <armin.please@put.your.email.here.!!!!>
- * (C) Copyright 1999 Pavel Machek <pavel@suse.cz>
- * (C) Copyright 1999 Johannes Erdfelt <jerdfelt@valinux.com>
+ * Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de>
+ * Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
+ * Copyright (c) 1999 Johannes Erdfelt <jerdfelt@valinux.com>
+ * Copyright (c) 1999 Vojtech Pavlik <vojtech@suse.cz>
*
- * version 0.8: Fixed endianity bug, some cleanups. I really hate to have
- * half of driver in form if (...) { info("x"); return y; }
- * Pavel Machek <pavel@suse.cz>
+ * USB Abstract Control Model driver for USB modems and ISDN adapters
*
- * version 0.7: Added usb flow control. Fixed bug in uhci.c (what idiot
- * wrote this code? ...Oops that was me). Fixed module cleanup. Did some
- * testing at 3Com => zmodem uload+download works, pppd had trouble but
- * seems to work now. Changed Menuconfig texts "Communications Device
- * Class (ACM)" might be a bit more intuitive. Ported to 2.3.13-1 prepatch.
- * (2/8/99)
+ * Sponsored by SuSE
*
- * version 0.6: Modularized driver, added disconnect code, improved
- * assignment of device to tty minor number.
- * (21/7/99)
- *
- * version 0.5: Driver now generates a tty instead of a simple character
- * device. Moved async bulk transfer to 2.3.10 kernel version. fixed a bug
- * in uhci_td_allocate. Commenetd out getstringtable which causes crash.
- * (13/7/99)
- *
- * version 0.4: Small fixes in the FIFO, cleanup. Updated Bulk transfer in
- * uhci.c. Should have the correct interface now.
- * (6/6/99)
- *
- * version 0.3: Major changes. Changed Bulk transfer to interrupt based
- * transfer. Using FIFO Buffers now. Consistent handling of open/close
- * file state and detected/nondetected device. File operations behave
- * according to this. Driver is able to send+receive now! Heureka!
- * (27/5/99)
- *
- * version 0.2: Improved Bulk transfer. TX led now flashes every time data is
- * sent. Send Encapsulated Data is not needed, nor does it do anything.
- * Why's that ?!? Thanks to Thomas Sailer for his close look at the bulk code.
- * He told me about some importand bugs. (5/21/99)
+ * ChangeLog:
+ * v0.9 Vojtech Pavlik - thorough cleaning, URBification, almost a rewrite
+ * v0.10 Vojtech Pavlik - some more cleanups
+ */
+
+/*
+ * 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.
*
- * version 0.1: Bulk transfer for uhci seems to work now, no dangling tds any
- * more. TX led of the ISDN TA flashed the first time. Does this mean it works?
- * The interrupt of the ctrl endpoint crashes the kernel => no read possible
- * (5/19/99)
+ * 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.
*
- * version 0.0: Driver sets up configuration, sets up data pipes, opens misc
- * device. No actual data transfer is done, since we don't have bulk transfer,
- * yet. Purely skeleton for now. (5/8/99)
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
@@ -62,581 +43,491 @@
#include <linux/tty_flip.h>
#include <linux/tty.h>
#include <linux/module.h>
+#include <linux/config.h>
#include "usb.h"
-#define NR_PORTS 3
-#define ACM_MAJOR 166 /* Wow, major is now officially allocated */
+#ifdef CONFIG_USB_ACM_DEBUG
+#define acm_debug(fmt,arg...) printk(KERN_DEBUG "acm: " fmt "\n" , ##arg)
+#else
+#define acm_debug(fmt,arg...) do {} while(0)
+#endif
+
+/*
+ * Major and minor numbers.
+ */
+
+#define ACM_TTY_MAJOR 166
+#define ACM_TTY_MINORS 8
-//#define info(message...) printk(KERN_DEBUG message)
-#define info(message...)
+/*
+ * Output control lines.
+ */
-#define CTRL_STAT_DTR 1
-#define CTRL_STAT_RTS 2
+#define ACM_CTRL_DTR 0x01
+#define ACM_CTRL_RTS 0x02
-static struct usb_driver acm_driver;
+/*
+ * Input control lines and line errors.
+ */
+
+#define ACM_CTRL_DCD 0x01
+#define ACM_CTRL_DSR 0x02
+#define ACM_CTRL_BRK 0x04
+#define ACM_CTRL_RI 0x08
+
+#define ACM_CTRL_FRAMING 0x10
+#define ACM_CTRL_PARITY 0x20
+#define ACM_CTRL_OVERRUN 0x40
-static int acm_refcount;
+/*
+ * Line speed and caracter encoding.
+ */
-static struct tty_driver acm_tty_driver;
-static struct tty_struct *acm_tty[NR_PORTS];
-static struct termios *acm_termios[NR_PORTS];
-static struct termios *acm_termios_locked[NR_PORTS];
-static struct acm_state acm_state_table[NR_PORTS];
-
-struct acm_state {
- struct usb_device *dev; //the coresponding usb device
- int cfgnum; //configuration number on this device
- struct tty_struct *tty; //the coresponding tty
- char present; //a device for this struct was detected => this tty is used
- char active; //someone has this acm's device open
- unsigned int ctrlstate; //Status of the serial control lines (handshake,...)
- unsigned int linecoding; //Status of the line coding (Bits, Stop, Parity)
- int writesize, readsize; //size of the usb buffers
- char *writebuffer, *readbuffer; //the usb buffers
- void *readtransfer, *writetransfer;
- void *ctrltransfer; //ptr to HC internal transfer struct
- char writing, reading; //flag if transfer is running
- unsigned int readendp,writeendp,ctrlendp; //endpoints and
- unsigned int readpipe,writepipe,ctrlpipe; //pipes (are one of these obsolete?)
- unsigned ctrlinterval; //interval to poll from device
+struct acm_coding {
+ __u32 speed;
+ __u8 stopbits;
+ __u8 parity;
+ __u8 databits;
+} __attribute__ ((packed));
+
+/*
+ * Internal driver structures.
+ */
+
+struct acm {
+ struct usb_device *dev; /* the coresponding usb device */
+ struct usb_config_descriptor *cfg; /* configuration number on this device */
+ struct tty_struct *tty; /* the coresponding tty */
+ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
+ unsigned int ctrlout; /* output control lines (DTR, RTS) */
+ struct acm_coding linecoding; /* line coding (bits, stop, parity) */
+ unsigned int writesize; /* max packet size for the output bulk endpoint */
+ struct urb ctrlurb, readurb, writeurb; /* urbs */
+ unsigned char present; /* this device is connected to the usb bus */
+ unsigned char used; /* someone has this acm's device open */
};
-#define ACM_READY (acm->present && acm->active)
+static struct usb_driver acm_driver;
+static struct acm acm_table[ACM_TTY_MINORS];
+
+#define ACM_READY(acm) (acm->present && acm->used)
-//functions for various ACM requests
+/*
+ * Functions for ACM control messages.
+ */
-void Set_Control_Line_Status(unsigned int status, struct acm_state *acm)
+static void acm_set_control(unsigned int status, struct acm *acm)
{
- struct usb_device *dev = acm->dev;
- int ret;
+ if (usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 0x22, 0x22, status, 0, NULL, 0, HZ) < 0)
+ acm_debug("acm_set_control() failed");
- info("Set_control_Line_Status\n");
+ acm_debug("output control lines: dtr%c rts%c",
+ acm->ctrlout & ACM_CTRL_DTR ? '+' : '-', acm->ctrlout & ACM_CTRL_RTS ? '+' : '-');
+}
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x22, 0x22,
- status, 0, NULL, 0, HZ);
- if (ret < 0)
- printk(KERN_ERR "acm: Set_Control_Line_Status failed\n");
+#if 0
+static void acm_set_coding(struct acm_coding *coding, struct acm *acm)
+{
+ if (usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 0x30, 0x22, 0, 0, coding, sizeof(struct acm_coding), HZ) < 0)
+ acm_debug("acm_set_coding() failed");
+}
- acm->ctrlstate = status;
+static void acm_send_break(int ms, struct acm *acm)
+{
+ if (usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), 0x30, 0x33, ms, 0, NULL, 0, HZ) < 0)
+ acm_debug("acm_send_break() failed");
}
+#endif
-void Set_Line_Coding(unsigned int coding, struct acm_state *acm)
+/*
+ * Interrupt handler for various ACM control events
+ */
+
+static void acm_ctrl_irq(struct urb *urb)
{
- struct usb_device *dev = acm->dev;
- int ret;
+ struct acm *acm = urb->context;
+ devrequest *dr = urb->transfer_buffer;
+ unsigned char *data = (unsigned char *)(dr + 1);
- info("Set_Line_Coding\n");
+ if (urb->status < 0) {
+ acm_debug("nonzero ctrl irq status received: %d", urb->status);
+ return;
+ }
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x30, 0x22,
- coding, 0, NULL, 0, HZ);
+ if (!ACM_READY(acm)) return;
- acm->linecoding = coding;
-}
+ switch (dr->request) {
-//Interrupt handler for various usb events
-static int acm_irq(int state, void *__buffer, int count, void *dev_id)
-{
- unsigned char *data;
- struct acm_state *acm = (struct acm_state *)dev_id;
- devrequest *dr;
+ case 0x20: /* Set serial line state */
- info("ACM_USB_IRQ\n");
+ if ((dr->index != 1) || (dr->length != 2)) {
+ acm_debug("unknown set serial line request: index %d len %d", dr->index, dr->length);
+ return;
+ }
- if (state) {
- printk(KERN_DEBUG "acm_irq: strange state received: %x\n", state);
- return 0;
- }
+ acm->ctrlin = data[0] | (((unsigned int) data[1]) << 8);
- if (!acm->present)
- return 0;
- if (!acm->active)
- return 1;
+ acm_debug("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
+ acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
+ acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
- dr = __buffer;
- data = __buffer;
- data += sizeof(dr);
-
-#if 0
- printk("reqtype: %02X\n",dr->requesttype);
- printk("request: %02X\n",dr->request);
- printk("wValue: %02X\n",dr->value);
- printk("wIndex: %02X\n",dr->index);
- printk("wLength: %02X\n",dr->length);
-#endif
+ return;
- switch(dr->request) {
- case 0x00: /* Network connection */
- printk(KERN_DEBUG "Network connection: ");
- if (dr->request==0) printk(KERN_DEBUG "disconnected\n");
- if (dr->request==1) printk(KERN_DEBUG "connected\n");
- break;
-
- case 0x01: /* Response available */
- printk(KERN_DEBUG "Response available\n");
- break;
-
- case 0x20: /* Set serial line state */
- printk(KERN_DEBUG "acm.c: Set serial control line state\n");
- if ((dr->index==1) && (dr->length==2)) {
- acm->ctrlstate= data[0] || (data[1] << 16);
- printk(KERN_DEBUG "Serstate: %02X\n",acm->ctrlstate);
- }
- break;
+ default:
+ acm_debug("unknown control event received: request %d index %d len %d data0 %d data1 %d",
+ dr->request, dr->index, dr->length, data[0], data[1]);
+ return;
}
- return 1; /* Continue transfer */
+ return;
}
-static int acm_read_irq(int state, void *__buffer, int count, void *dev_id)
+static void acm_read_bulk(struct urb *urb)
{
- struct acm_state *acm = (struct acm_state *) dev_id;
- struct tty_struct *tty = acm->tty;
- unsigned char* data=__buffer;
+ struct acm *acm = urb->context;
+ struct tty_struct *tty = acm->tty;
+ unsigned char *data = urb->transfer_buffer;
int i;
- info("ACM_READ_IRQ: state %d, %d bytes\n", state, count);
- if (state) {
- printk( "acm_read_irq: strange state received: %x\n", state );
- return 1;
+ if (urb->status) {
+ acm_debug("nonzero read bulk status received: %d", urb->status);
+ return;
}
-
- if (!ACM_READY)
- return 0; /* stop transfer */
- for (i=0;i<count;i++)
- tty_insert_flip_char(tty,data[i],0);
- tty_flip_buffer_push(tty);
+ if (!ACM_READY(acm)) return;
+
+ for (i = 0; i < urb->actual_length; i++)
+ tty_insert_flip_char(tty, data[i], 0);
- return 1;
+ tty_flip_buffer_push(tty);
+
+ if (usb_submit_urb(urb))
+ acm_debug("failed resubmitting read urb");
+
+ return;
}
-static int acm_write_irq(int state, void *__buffer, int count, void *dev_id)
+static void acm_write_bulk(struct urb *urb)
{
- struct acm_state *acm = (struct acm_state *) dev_id;
- struct tty_struct *tty = acm->tty;
+ struct acm *acm = (struct acm *)urb->context;
+ struct tty_struct *tty = acm->tty;
- info("ACM_WRITE_IRQ\n");
+ if (urb->status) {
+ acm_debug("nonzero write bulk status received: %d", urb->status);
+ return;
+ }
- if (!ACM_READY)
- return 0; /* stop transfer */
+ if (!ACM_READY(acm)) return;
- usb_terminate_bulk(acm->dev, acm->writetransfer);
- acm->writing = 0;
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
+
wake_up_interruptible(&tty->write_wait);
- return 0; /* stop tranfer */
+ return;
}
-/*TTY STUFF*/
-static int rs_open(struct tty_struct *tty, struct file *filp)
-{
- struct acm_state *acm;
- int ret;
+/*
+ * TTY handlers
+ */
- info("USB_FILE_OPEN\n");
+static int acm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ struct acm *acm = &acm_table[MINOR(tty->device)];
- tty->driver_data = acm =
- &acm_state_table[MINOR(tty->device) - tty->driver.minor_start];
+ tty->driver_data = acm;
acm->tty = tty;
-
- if (!acm->present)
- return -EINVAL;
- if (acm->active++)
+ if (!acm->present) return -EINVAL;
+
+ if (acm->used++) {
+ MOD_INC_USE_COUNT;
return 0;
-
+ }
+
MOD_INC_USE_COUNT;
- /* Start reading from the device */
- ret = usb_request_irq(acm->dev, acm->ctrlpipe, acm_irq,
- acm->ctrlinterval, acm, &acm->ctrltransfer);
- if (ret)
- printk(KERN_ERR "usb-acm: usb_request_irq failed (0x%x)\n", ret);
- acm->reading = 1;
- acm->readtransfer = usb_request_bulk(acm->dev, acm->readpipe,
- acm_read_irq, acm->readbuffer, acm->readsize, acm);
+ if (usb_submit_urb(&acm->ctrlurb))
+ acm_debug("usb_submit_urb(ctrl irq) failed");
+
+ if (usb_submit_urb(&acm->readurb))
+ acm_debug("usb_submit_urb(read bulk) failed");
- Set_Control_Line_Status(CTRL_STAT_DTR | CTRL_STAT_RTS, acm);
+ acm_set_control(acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS, acm);
return 0;
}
-static void rs_close(struct tty_struct *tty, struct file *filp)
+static void acm_tty_close(struct tty_struct *tty, struct file *filp)
{
- struct acm_state *acm = (struct acm_state *) tty->driver_data;
+ struct acm *acm = tty->driver_data;
- info("rs_close\n");
+ if (!ACM_READY(acm)) return;
- if (!acm->present)
+ if (--acm->used) {
+ MOD_DEC_USE_COUNT;
return;
-
- if (--acm->active)
- return;
-
- Set_Control_Line_Status(0, acm);
-
- if (acm->writing) {
- usb_terminate_bulk(acm->dev, acm->writetransfer);
- acm->writing = 0;
- }
- if (acm->reading) {
- usb_terminate_bulk(acm->dev, acm->readtransfer);
- acm->reading = 0;
}
-// usb_release_irq(acm->dev, acm->ctrltransfer, acm->ctrlpipe);
+
+ acm_set_control(acm->ctrlout = 0, acm);
+ usb_unlink_urb(&acm->writeurb);
+ usb_unlink_urb(&acm->readurb);
MOD_DEC_USE_COUNT;
}
-static int rs_write(struct tty_struct *tty, int from_user,
- const unsigned char *buf, int count)
+static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
{
- struct acm_state *acm = (struct acm_state *)tty->driver_data;
- int written;
-
- info("rs_write\n");
-
- if (!ACM_READY)
- return -EINVAL;
+ struct acm *acm = tty->driver_data;
- if (acm->writing) {
- info("already writing\n");
- return 0;
- }
+ if (!ACM_READY(acm)) return -EINVAL;
+ if (acm->writeurb.status == -EINPROGRESS) return 0;
- written = (count>acm->writesize) ? acm->writesize : count;
+ count = (count > acm->writesize) ? acm->writesize : count;
if (from_user)
- copy_from_user(acm->writebuffer, buf, written);
+ copy_from_user(acm->writeurb.transfer_buffer, buf, count);
else
- memcpy(acm->writebuffer, buf, written);
+ memcpy(acm->writeurb.transfer_buffer, buf, count);
- //start the transfer
- acm->writing = 1;
- acm->writetransfer = usb_request_bulk(acm->dev, acm->writepipe,
- acm_write_irq, acm->writebuffer, written, acm);
+ acm->writeurb.transfer_buffer_length = count;
- return written;
-}
+ if (usb_submit_urb(&acm->writeurb))
+ acm_debug("usb_submit_urb(write bulk) failed");
-static void rs_put_char(struct tty_struct *tty, unsigned char ch)
-{
- printk(KERN_DEBUG "acm: rs_put_char: Who called this unsupported routine?\n");
- BUG();
+ return count;
}
-static int rs_write_room(struct tty_struct *tty)
+static int acm_tty_write_room(struct tty_struct *tty)
{
- struct acm_state *acm = (struct acm_state *) tty->driver_data;
-
- info("rs_write_room\n");
-
- if (!ACM_READY)
- return -EINVAL;
-
- return acm->writing ? 0 : acm->writesize;
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return -EINVAL;
+ return acm->writeurb.status == -EINPROGRESS ? 0 : acm->writesize;
}
-static int rs_chars_in_buffer(struct tty_struct *tty)
+static int acm_tty_chars_in_buffer(struct tty_struct *tty)
{
- struct acm_state *acm = (struct acm_state *) tty->driver_data;
-
-// info("rs_chars_in_buffer\n");
-
- if (!ACM_READY)
- return -EINVAL;
-
- return acm->writing ? acm->writesize : 0;
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return -EINVAL;
+ return acm->writeurb.status == -EINPROGRESS ? acm->writesize : 0;
}
-static void rs_throttle(struct tty_struct *tty)
+static void acm_tty_throttle(struct tty_struct *tty)
{
- struct acm_state *acm = (struct acm_state *) tty->driver_data;
-
- info("rs_throttle\n");
-
- if (!ACM_READY)
- return;
-/*
- if (I_IXOFF(tty))
- rs_send_xchar(tty, STOP_CHAR(tty));
-*/
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return;
if (tty->termios->c_cflag & CRTSCTS)
- Set_Control_Line_Status(acm->ctrlstate & ~CTRL_STAT_RTS, acm);
+ acm_set_control(acm->ctrlout &= ~ACM_CTRL_RTS, acm);
}
-static void rs_unthrottle(struct tty_struct *tty)
+static void acm_tty_unthrottle(struct tty_struct *tty)
{
- struct acm_state *acm = (struct acm_state *) tty->driver_data;
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return;
+ if (tty->termios->c_cflag & CRTSCTS)
+ acm_set_control(acm->ctrlout |= ACM_CTRL_RTS, acm);
+}
- info("rs_unthrottle\n");
+static void acm_tty_set_termios(struct tty_struct *tty, struct termios *old)
+{
+ acm_debug("set_termios called, but not there yet");
+ return;
+}
- if (!ACM_READY)
- return;
/*
- if (I_IXOFF(tty))
- rs_send_xchar(tty, STOP_CHAR(tty));
-*/
- if (tty->termios->c_cflag & CRTSCTS)
- Set_Control_Line_Status(acm->ctrlstate | CTRL_STAT_RTS, acm);
-}
+ * USB probe and disconnect routines.
+ */
-static int get_free_acm(void)
+static void *acm_probe(struct usb_device *dev, unsigned int ifnum)
{
- int i;
-
- for (i = 0; i < NR_PORTS; i++) {
- if (!acm_state_table[i].present)
- return i;
- }
- return -1;
-}
-
-static void * acm_probe(struct usb_device *dev, unsigned int ifnum)
-{
- struct acm_state *acm;
- struct usb_interface_descriptor *interface;
- struct usb_endpoint_descriptor *endpoint;
- int cfgnum,acmno;
- int swapped=0;
-
- info("acm_probe\n");
-
- if (0>(acmno=get_free_acm())) {
- info("Too many acm devices connected\n");
+ struct acm *acm;
+ struct usb_interface_descriptor *ifcom, *ifdata;
+ struct usb_endpoint_descriptor *epctrl, *epread, *epwrite;
+ int readsize, ctrlsize, minor, i;
+ unsigned char *buf;
+ char *s = NULL;
+
+ for (minor = 0; minor < ACM_TTY_MINORS &&
+ (acm_table[minor].present || acm_table[minor].used); minor++);
+ if (acm_table[minor].present || acm_table[minor].used) {
+ acm_debug("no more free acm devices");
return NULL;
}
- acm = &acm_state_table[acmno];
+ acm = acm_table + minor;
+ memset(acm, 0, sizeof(struct acm));
+
+ acm->dev = dev;
- /* Only use CDC */
- if (dev->descriptor.bDeviceClass != 2 ||
- dev->descriptor.bDeviceSubClass != 0 ||
- dev->descriptor.bDeviceProtocol != 0)
+ if (dev->descriptor.bDeviceClass != 2 || dev->descriptor.bDeviceSubClass != 0
+ || dev->descriptor.bDeviceProtocol != 0) {
return NULL;
+ }
-#define IFCLASS(if) ((if->bInterfaceClass << 24) | (if->bInterfaceSubClass << 16) | (if->bInterfaceProtocol << 8) | (if->bNumEndpoints))
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
- /* FIXME: should the driver really be doing the configuration
- * selecting or should the usbcore? [different configurations
- * can have different bandwidth requirements] -greg */
-
- /* Now scan all configs for a ACM configuration */
- for (cfgnum=0;cfgnum<dev->descriptor.bNumConfigurations;cfgnum++) {
- /* The first one should be Communications interface? */
- interface = &dev->config[cfgnum].interface[0].altsetting[0];
- if (IFCLASS(interface) != 0x02020101)
- continue;
+ acm_debug("probing config %d", i);
+ acm->cfg = dev->config + i;
- /* Which uses an interrupt input */
- endpoint = &interface->endpoint[0];
- if ((endpoint->bEndpointAddress & 0x80) != 0x80 ||
- (endpoint->bmAttributes & 3) != 3)
- continue;
-
- /* The second one should be a Data interface? */
- interface = &dev->config[cfgnum].interface[1].altsetting[0];
- if (interface->bInterfaceClass != 10 ||
- interface->bNumEndpoints != 2)
+ ifcom = acm->cfg->interface[0].altsetting + 0;
+ if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 ||
+ ifcom->bInterfaceProtocol != 1 || ifcom->bNumEndpoints != 1)
continue;
- /* make sure both interfaces are available for our use */
- if (usb_interface_claimed(&dev->config[cfgnum].interface[0]) ||
- usb_interface_claimed(&dev->config[cfgnum].interface[1])) {
- printk("usb-acm: required interface already has a driver\n");
+ epctrl = ifcom->endpoint + 0;
+ if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3)
continue;
- }
- endpoint = &interface->endpoint[0];
- if ((endpoint->bEndpointAddress & 0x80) != 0x80)
- swapped = 1;
-
- /*With a bulk input */
- endpoint = &interface->endpoint[0^swapped];
- if ((endpoint->bEndpointAddress & 0x80) != 0x80 ||
- (endpoint->bmAttributes & 3) != 2)
+ ifdata = acm->cfg->interface[1].altsetting + 0;
+ if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2)
continue;
-
- /*And a bulk output */
- endpoint = &interface->endpoint[1^swapped];
- if ((endpoint->bEndpointAddress & 0x80) == 0x80 ||
- (endpoint->bmAttributes & 3) != 2)
+
+ if (usb_interface_claimed(acm->cfg->interface + 0) ||
+ usb_interface_claimed(acm->cfg->interface + 1))
continue;
- printk("USB ACM %d found\n",acmno);
- usb_set_configuration(dev, dev->config[cfgnum].bConfigurationValue);
+ epread = ifdata->endpoint + 0;
+ epwrite = ifdata->endpoint + 1;
- acm->dev=dev;
+ if ((epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 ||
+ ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80)
+ continue;
- acm->readendp=dev->config[cfgnum].interface[1].altsetting[0].endpoint[0^swapped].bEndpointAddress;
- acm->readpipe=usb_rcvbulkpipe(dev,acm->readendp);
- acm->readbuffer=kmalloc(acm->readsize=dev->config[cfgnum].interface[1].altsetting[0].endpoint[0^swapped].wMaxPacketSize,GFP_KERNEL);
- acm->reading=0;
- if (!acm->readbuffer) {
- printk("ACM: Couldn't allocate readbuffer\n");
- return NULL;
+ if ((epread->bEndpointAddress & 0x80) != 0x80) {
+ epread = ifdata->endpoint + 1;
+ epwrite = ifdata->endpoint + 0;
}
- acm->writeendp=dev->config[cfgnum].interface[1].altsetting[0].endpoint[1^swapped].bEndpointAddress;
- acm->writepipe=usb_sndbulkpipe(dev,acm->writeendp);
- acm->writebuffer=kmalloc(acm->writesize=dev->config[cfgnum].interface[1].altsetting[0].endpoint[1^swapped].wMaxPacketSize, GFP_KERNEL);
- acm->writing=0;
- if (!acm->writebuffer) {
- printk("ACM: Couldn't allocate writebuffer\n");
- kfree(acm->readbuffer);
+ usb_set_configuration(dev, acm->cfg->bConfigurationValue);
+
+ ctrlsize = epctrl->wMaxPacketSize;
+ readsize = epread->wMaxPacketSize;
+ acm->writesize = epwrite->wMaxPacketSize;
+
+ if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL)))
return NULL;
- }
- acm->ctrlendp=dev->config[cfgnum].interface[0].altsetting[0].endpoint[0].bEndpointAddress;
- acm->ctrlpipe=usb_rcvctrlpipe(acm->dev,acm->ctrlendp);
- acm->ctrlinterval=dev->config[cfgnum].interface[0].altsetting[0].endpoint[0].bInterval;
+ FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress),
+ buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
- acm->cfgnum = cfgnum;
- acm->present=1;
- MOD_INC_USE_COUNT;
+ FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress),
+ buf += ctrlsize, readsize, acm_read_bulk, acm);
+
+ FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
+ buf += readsize , acm->writesize, acm_write_bulk, acm);
+
+ printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor);
+
+ usb_driver_claim_interface(&acm_driver, acm->cfg->interface + 0, acm);
+ usb_driver_claim_interface(&acm_driver, acm->cfg->interface + 1, acm);
+
+ acm->present = 1;
- usb_driver_claim_interface(&acm_driver,
- &dev->config[cfgnum].interface[0], acm);
- usb_driver_claim_interface(&acm_driver,
- &dev->config[cfgnum].interface[1], acm);
return acm;
}
+
return NULL;
}
static void acm_disconnect(struct usb_device *dev, void *ptr)
{
- struct acm_state *acm = ptr;
+ struct acm *acm = ptr;
- info("acm_disconnect\n");
-
- if (!acm->present)
+ if (!acm->present) {
+ acm_debug("disconnect on nonexisting interface");
return;
+ }
- acm->active=0;
- acm->present=0;
- if (acm->writing){
- usb_terminate_bulk(acm->dev, acm->writetransfer);
- acm->writing=0;
- }
- if (acm->reading){
- usb_terminate_bulk(acm->dev, acm->readtransfer);
- acm->reading=0;
- }
- usb_release_irq(acm->dev,acm->ctrltransfer, acm->ctrlpipe);
- //BUG: What to do if a device is open?? Notify process or not allow cleanup?
- kfree(acm->writebuffer);
- kfree(acm->readbuffer);
-
- /* release the interfaces so that other drivers can have at them */
- usb_driver_release_interface(&acm_driver,
- &dev->config[acm->cfgnum].interface[0]);
- usb_driver_release_interface(&acm_driver,
- &dev->config[acm->cfgnum].interface[1]);
+ acm->present = 0;
- MOD_DEC_USE_COUNT;
+ usb_unlink_urb(&acm->ctrlurb);
+ usb_unlink_urb(&acm->readurb);
+ usb_unlink_urb(&acm->writeurb);
+
+ kfree(acm->ctrlurb.transfer_buffer);
+
+ usb_driver_release_interface(&acm_driver, acm->cfg->interface + 0);
+ usb_driver_release_interface(&acm_driver, acm->cfg->interface + 1);
}
-/*USB DRIVER STUFF*/
+/*
+ * USB driver structure.
+ */
+
static struct usb_driver acm_driver = {
- "acm",
- acm_probe,
- acm_disconnect,
- { NULL, NULL }
+ name: "acm",
+ probe: acm_probe,
+ disconnect: acm_disconnect
+};
+
+/*
+ * TTY driver structures.
+ */
+
+static int acm_tty_refcount;
+
+static struct tty_struct *acm_tty_table[ACM_TTY_MINORS];
+static struct termios *acm_tty_termios[ACM_TTY_MINORS];
+static struct termios *acm_tty_termios_locked[ACM_TTY_MINORS];
+
+static struct tty_driver acm_tty_driver = {
+ magic: TTY_DRIVER_MAGIC,
+ driver_name: "usb",
+ name: "ttyACM",
+ major: ACM_TTY_MAJOR,
+ minor_start: 0,
+ num: ACM_TTY_MINORS,
+ type: TTY_DRIVER_TYPE_SERIAL,
+ subtype: SERIAL_TYPE_NORMAL,
+ flags: TTY_DRIVER_REAL_RAW,
+
+ refcount: &acm_tty_refcount,
+
+ table: acm_tty_table,
+ termios: acm_tty_termios,
+ termios_locked: acm_tty_termios_locked,
+
+ open: acm_tty_open,
+ close: acm_tty_close,
+ write: acm_tty_write,
+ write_room: acm_tty_write_room,
+ set_termios: acm_tty_set_termios,
+ throttle: acm_tty_throttle,
+ unthrottle: acm_tty_unthrottle,
+ chars_in_buffer: acm_tty_chars_in_buffer
};
+/*
+ * Init / cleanup.
+ */
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ usb_deregister(&acm_driver);
+ tty_unregister_driver(&acm_tty_driver);
+}
+
+int init_module(void)
+#else
int usb_acm_init(void)
+#endif
{
- int cnt;
+ memset(acm_table, 0, sizeof(struct acm) * ACM_TTY_MINORS);
- info("usb_acm_init\n");
+ acm_tty_driver.init_termios = tty_std_termios;
+ acm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- //INITIALIZE GLOBAL DATA STRUCTURES
- for (cnt = 0; cnt < NR_PORTS; cnt++)
- memset(&acm_state_table[cnt], 0, sizeof(struct acm_state));
-
- //REGISTER TTY DRIVER
- memset(&acm_tty_driver, 0, sizeof(struct tty_driver));
- acm_tty_driver.magic = TTY_DRIVER_MAGIC;
- acm_tty_driver.driver_name = "usb";
- acm_tty_driver.name = "ttyACM";
- acm_tty_driver.major = ACM_MAJOR;
- acm_tty_driver.minor_start = 0;
- acm_tty_driver.num = NR_PORTS;
- acm_tty_driver.type = TTY_DRIVER_TYPE_SERIAL;
- acm_tty_driver.subtype = SERIAL_TYPE_NORMAL;
- acm_tty_driver.init_termios = tty_std_termios;
- acm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- acm_tty_driver.flags = TTY_DRIVER_REAL_RAW;
- acm_tty_driver.refcount = &acm_refcount;
- acm_tty_driver.table = acm_tty;
- acm_tty_driver.termios = acm_termios;
- acm_tty_driver.termios_locked = acm_termios_locked;
-
- acm_tty_driver.open = rs_open;
- acm_tty_driver.close = rs_close;
- acm_tty_driver.write = rs_write;
- acm_tty_driver.put_char = rs_put_char;
- acm_tty_driver.flush_chars = NULL; //rs_flush_chars;
- acm_tty_driver.write_room = rs_write_room;
- acm_tty_driver.ioctl = NULL; //rs_ioctl
- acm_tty_driver.set_termios = NULL; //rs_set_termios;
- acm_tty_driver.set_ldisc = NULL;
- acm_tty_driver.throttle = rs_throttle;
- acm_tty_driver.unthrottle = rs_unthrottle;
- acm_tty_driver.stop = NULL; //rs_stop;
- acm_tty_driver.start = NULL; //rs_start;
- acm_tty_driver.hangup = NULL; //rs_hangup;
- acm_tty_driver.break_ctl = NULL; //rs_break;
- acm_tty_driver.wait_until_sent = NULL; //rs_wait_until_sent;
- acm_tty_driver.send_xchar = NULL; //rs_send_xchar;
- acm_tty_driver.read_proc = NULL; //rs_read_proc;
- acm_tty_driver.chars_in_buffer = rs_chars_in_buffer;
- acm_tty_driver.flush_buffer = NULL; //rs_flush_buffer;
- if (tty_register_driver(&acm_tty_driver)) {
- printk(KERN_ERR "acm: failed to register tty driver\n");
- return -EPERM;
- }
+ if (tty_register_driver(&acm_tty_driver))
+ return -1;
if (usb_register(&acm_driver) < 0) {
tty_unregister_driver(&acm_tty_driver);
return -1;
}
- printk(KERN_INFO "USB ACM registered.\n");
return 0;
}
-void usb_acm_cleanup(void)
-{
- int i;
- struct acm_state *acm;
-
- info("usb_acm_cleanup\n");
-
- for (i=0;i<NR_PORTS;i++) {
- acm=&acm_state_table[i];
- if (acm->present) {
- printk("disconnecting %d\n",i);
- acm_disconnect(acm->dev, acm);
- }
- }
- tty_unregister_driver(&acm_tty_driver);
-
- usb_deregister(&acm_driver);
-
-}
-
-#ifdef MODULE
-int init_module(void)
-{
- return usb_acm_init();
-}
-
-void cleanup_module(void)
-{
- usb_acm_cleanup();
-}
-#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)