patch-2.3.38 linux/drivers/usb/acm.c
Next file: linux/drivers/usb/audio.c
Previous file: linux/drivers/usb/Makefile
Back to the patch index
Back to the overall index
- Lines: 485
- Date:
Fri Jan 7 16:07:49 2000
- Orig file:
v2.3.37/linux/drivers/usb/acm.c
- Orig date:
Tue Jan 4 13:57:17 2000
diff -u --recursive --new-file v2.3.37/linux/drivers/usb/acm.c linux/drivers/usb/acm.c
@@ -1,5 +1,5 @@
/*
- * acm.c Version 0.10
+ * acm.c Version 0.11
*
* Copyright (c) 1999 Armin Fuerst <fuerst@in.tum.de>
* Copyright (c) 1999 Pavel Machek <pavel@suse.cz>
@@ -11,8 +11,10 @@
* Sponsored by SuSE
*
* ChangeLog:
- * v0.9 Vojtech Pavlik - thorough cleaning, URBification, almost a rewrite
- * v0.10 Vojtech Pavlik - some more cleanups
+ * v0.9 - thorough cleaning, URBification, almost a rewrite
+ * v0.10 - some more cleanups
+ * v0.11 - fixed flow control, read error doesn't stop reads
+ * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced
*/
/*
@@ -43,22 +45,41 @@
#include <linux/tty_flip.h>
#include <linux/tty.h>
#include <linux/module.h>
-#include <linux/config.h>
-#include "usb.h"
+#define DEBUG
-#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
+#include "usb.h"
/*
* Major and minor numbers.
*/
#define ACM_TTY_MAJOR 166
-#define ACM_TTY_MINORS 8
+#define ACM_TTY_MINORS 32
+
+/*
+ * Requests.
+ */
+
+#define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+
+#define ACM_REQ_COMMAND 0x00
+#define ACM_REQ_RESPONSE 0x01
+#define ACM_REQ_SET_FEATURE 0x02
+#define ACM_REQ_GET_FEATURE 0x03
+#define ACM_REQ_CLEAR_FEATURE 0x04
+
+#define ACM_REQ_SET_LINE 0x20
+#define ACM_REQ_GET_LINE 0x21
+#define ACM_REQ_SET_CONTROL 0x22
+#define ACM_REQ_SEND_BREAK 0x23
+
+/*
+ * IRQs
+ */
+
+#define ACM_IRQ_NETWORK 0x00
+#define ACM_IRQ_LINE_STATE 0x20
/*
* Output control lines.
@@ -99,46 +120,37 @@
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 ctrlif; /* interface number for acm control messages */
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 */
+ unsigned int minor; /* acm minor number */
+ unsigned int present; /* this device is connected to the usb bus */
+ unsigned int used; /* someone has this acm's device open */
};
static struct usb_driver acm_driver;
-static struct acm acm_table[ACM_TTY_MINORS];
+static struct acm *acm_table[ACM_TTY_MINORS] = { NULL, NULL, NULL, /* .... */ };
-#define ACM_READY(acm) (acm->present && acm->used)
+#define ACM_READY(acm) (acm && acm->present && acm->used)
/*
* Functions for ACM control messages.
*/
-static void acm_set_control(unsigned int status, struct acm *acm)
+static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len)
{
- 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");
-
- acm_debug("output control lines: dtr%c rts%c",
- acm->ctrlout & ACM_CTRL_DTR ? '+' : '-', acm->ctrlout & ACM_CTRL_RTS ? '+' : '-');
+ int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
+ request, USB_RT_ACM, value, acm->ctrlif, buf, len, HZ * 5);
+ dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval);
+ return retval < 0 ? retval : 0;
}
-#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");
-}
-
-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
+#define acm_set_control(acm, control) acm_ctrl_msg(acm, ACM_REQ_SET_CONTROL, control, NULL, 0)
+#define acm_set_coding(acm, coding) acm_ctrl_msg(acm, ACM_REQ_SET_LINE, 0, coding, sizeof(struct acm_coding))
+#define acm_send_break(acm, ms) acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0)
/*
* Interrupt handler for various ACM control events
@@ -150,25 +162,25 @@
devrequest *dr = urb->transfer_buffer;
unsigned char *data = (unsigned char *)(dr + 1);
+ if (!ACM_READY(acm)) return;
+
if (urb->status < 0) {
- acm_debug("nonzero ctrl irq status received: %d", urb->status);
+ dbg("nonzero ctrl irq status received: %d", urb->status);
return;
}
- if (!ACM_READY(acm)) return;
-
switch (dr->request) {
- case 0x20: /* Set serial line state */
+ case ACM_IRQ_NETWORK:
+
+ dbg("%s network", data[0] ? "connected to" : "disconnected from");
+ return;
- if ((dr->index != 1) || (dr->length != 2)) {
- acm_debug("unknown set serial line request: index %d len %d", dr->index, dr->length);
- return;
- }
+ case ACM_IRQ_LINE_STATE:
acm->ctrlin = data[0] | (((unsigned int) data[1]) << 8);
- acm_debug("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
+ dbg("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 ? '+' : '-',
@@ -177,7 +189,7 @@
return;
default:
- acm_debug("unknown control event received: request %d index %d len %d data0 %d data1 %d",
+ dbg("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;
}
@@ -192,20 +204,20 @@
unsigned char *data = urb->transfer_buffer;
int i;
- if (urb->status) {
- acm_debug("nonzero read bulk status received: %d", urb->status);
- return;
- }
-
if (!ACM_READY(acm)) return;
- for (i = 0; i < urb->actual_length; i++)
- tty_insert_flip_char(tty, data[i], 0);
+ if (!urb->status) {
- tty_flip_buffer_push(tty);
+ for (i = 0; i < urb->actual_length; i++)
+ tty_insert_flip_char(tty, data[i], 0);
+
+ tty_flip_buffer_push(tty);
+
+ } else
+ dbg("nonzero read bulk status received: %d", urb->status);
if (usb_submit_urb(urb))
- acm_debug("failed resubmitting read urb");
+ dbg("failed resubmitting read urb");
return;
}
@@ -215,13 +227,11 @@
struct acm *acm = (struct acm *)urb->context;
struct tty_struct *tty = acm->tty;
- if (urb->status) {
- acm_debug("nonzero write bulk status received: %d", urb->status);
- return;
- }
-
if (!ACM_READY(acm)) return;
+ if (urb->status)
+ dbg("nonzero write bulk status received: %d", urb->status);
+
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
@@ -236,24 +246,24 @@
static int acm_tty_open(struct tty_struct *tty, struct file *filp)
{
- struct acm *acm = &acm_table[MINOR(tty->device)];
+ struct acm *acm = acm_table[MINOR(tty->device)];
+
+ if (!acm || !acm->present) return -EINVAL;
tty->driver_data = acm;
acm->tty = tty;
- if (!acm->present) return -EINVAL;
-
MOD_INC_USE_COUNT;
if (acm->used++) return 0;
if (usb_submit_urb(&acm->ctrlurb))
- acm_debug("usb_submit_urb(ctrl irq) failed");
+ dbg("usb_submit_urb(ctrl irq) failed");
if (usb_submit_urb(&acm->readurb))
- acm_debug("usb_submit_urb(read bulk) failed");
+ dbg("usb_submit_urb(read bulk) failed");
- acm_set_control(acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS, acm);
+ acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS);
return 0;
}
@@ -262,18 +272,22 @@
{
struct acm *acm = tty->driver_data;
- if (!acm->used) return;
+ if (!acm || !acm->used) return;
MOD_DEC_USE_COUNT;
if (--acm->used) return;
if (acm->present) {
- acm_set_control(acm->ctrlout = 0, acm);
+ acm_set_control(acm, acm->ctrlout = 0);
usb_unlink_urb(&acm->ctrlurb);
usb_unlink_urb(&acm->writeurb);
usb_unlink_urb(&acm->readurb);
+ return;
}
+
+ acm_table[acm->minor] = NULL;
+ kfree(acm);
}
static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
@@ -293,7 +307,7 @@
acm->writeurb.transfer_buffer_length = count;
if (usb_submit_urb(&acm->writeurb))
- acm_debug("usb_submit_urb(write bulk) failed");
+ dbg("usb_submit_urb(write bulk) failed");
return count;
}
@@ -309,29 +323,72 @@
{
struct acm *acm = tty->driver_data;
if (!ACM_READY(acm)) return -EINVAL;
- return acm->writeurb.status == -EINPROGRESS ? acm->writesize : 0;
+ return acm->writeurb.status == -EINPROGRESS ? acm->writeurb.transfer_buffer_length : 0;
}
static void acm_tty_throttle(struct tty_struct *tty)
{
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);
+ usb_unlink_urb(&acm->readurb);
}
static void acm_tty_unthrottle(struct tty_struct *tty)
{
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);
+ if (usb_submit_urb(&acm->readurb))
+ dbg("usb_submit_urb(read bulk) in unthrottle() failed");
}
-static void acm_tty_set_termios(struct tty_struct *tty, struct termios *old)
+static void acm_tty_break_ctl(struct tty_struct *tty, int state)
{
- acm_debug("set_termios called, but not there yet");
- return;
+ struct acm *acm = tty->driver_data;
+ if (!ACM_READY(acm)) return;
+ if (acm_send_break(acm, state ? 0xffff : 0))
+ dbg("send break failed");
+}
+
+static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct acm *acm = tty->driver_data;
+ unsigned int retval, ctrl, old;
+
+ if (!ACM_READY(acm)) return -EINVAL;
+
+ switch (cmd) {
+
+ case TIOCMGET:
+
+ return put_user((acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
+ (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
+ (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
+ (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
+ (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) |
+ TIOCM_CTS, (unsigned long *) arg);
+
+ case TIOCMSET:
+ case TIOCMBIS:
+ case TIOCMBIC:
+
+ if ((retval = get_user(ctrl, (unsigned long *) arg))) return retval;
+
+ ctrl = (ctrl & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (ctrl & TIOCM_RTS ? ACM_CTRL_RTS : 0);
+ old = acm->ctrlout;
+
+ switch (cmd) {
+ case TIOCMSET: acm->ctrlout = ctrl; break;
+ case TIOCMBIS: acm->ctrlout |= ctrl; break;
+ case TIOCMBIC: acm->ctrlout &= ~ctrl; break;
+ }
+
+ if (acm->ctrlout == old) return 0;
+ return acm_set_control(acm, acm->ctrlout);
+ }
+
+ dbg("unknown ioctl %#x", cmd);
+
+ return -ENOIOCTLCMD;
}
/*
@@ -346,15 +403,18 @@
int readsize, ctrlsize, minor, i;
unsigned char *buf;
- 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");
+ for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
+
+ if (acm_table[minor]) {
+ dbg("no more free acm devices");
return NULL;
}
- acm = acm_table + minor;
+
+ if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) return NULL;
memset(acm, 0, sizeof(struct acm));
+ acm_table[minor] = acm;
+ acm->minor = minor;
acm->dev = dev;
if (dev->descriptor.bDeviceClass != 2 || dev->descriptor.bDeviceSubClass != 0
@@ -364,8 +424,8 @@
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
- acm_debug("probing config %d", i);
acm->cfg = dev->config + i;
+ dbg("probing config %d", acm->cfg->bConfigurationValue);
ifcom = acm->cfg->interface[0].altsetting + 0;
if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 ||
@@ -402,8 +462,7 @@
readsize = epread->wMaxPacketSize;
acm->writesize = epwrite->wMaxPacketSize;
- if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL)))
- return NULL;
+ if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) return NULL;
FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress),
buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
@@ -414,13 +473,19 @@
FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
buf += readsize , acm->writesize, acm_write_bulk, acm);
+ acm->ctrlif = ifcom->bInterfaceNumber;
+
printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor);
+ acm_set_control(acm, acm->ctrlout);
+
+ acm->linecoding.speed = 115200;
+ acm->linecoding.databits = 8;
+ acm_set_coding(acm, &acm->linecoding);
+
usb_driver_claim_interface(&acm_driver, acm->cfg->interface + 0, acm);
usb_driver_claim_interface(&acm_driver, acm->cfg->interface + 1, acm);
- acm_set_control(acm->ctrlout, acm);
-
acm->present = 1;
return acm;
@@ -433,8 +498,8 @@
{
struct acm *acm = ptr;
- if (!acm->present) {
- acm_debug("disconnect on nonexisting interface");
+ if (!acm || !acm->present) {
+ dbg("disconnect on nonexisting interface");
return;
}
@@ -448,6 +513,11 @@
usb_driver_release_interface(&acm_driver, acm->cfg->interface + 0);
usb_driver_release_interface(&acm_driver, acm->cfg->interface + 1);
+
+ if (!acm->used) {
+ acm_table[acm->minor] = NULL;
+ kfree(acm);
+ }
}
/*
@@ -491,10 +561,11 @@
close: acm_tty_close,
write: acm_tty_write,
write_room: acm_tty_write_room,
- set_termios: acm_tty_set_termios,
+ ioctl: acm_tty_ioctl,
throttle: acm_tty_throttle,
unthrottle: acm_tty_unthrottle,
- chars_in_buffer: acm_tty_chars_in_buffer
+ chars_in_buffer: acm_tty_chars_in_buffer,
+ break_ctl: acm_tty_break_ctl
};
/*
@@ -513,8 +584,6 @@
int usb_acm_init(void)
#endif
{
- memset(acm_table, 0, sizeof(struct acm) * ACM_TTY_MINORS);
-
acm_tty_driver.init_termios = tty_std_termios;
acm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
@@ -528,4 +597,3 @@
return 0;
}
-
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)