From 739e0285cbb162c8ddd0061fda581ee54a34c19a Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 11 Jun 2009 12:27:50 +0100 Subject: tty: Update cdc_acm The CDC ACM driver uses the tty layer correctly so needs conversion. Start by adding and initializing the port structures. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/usb/class/cdc-acm.c | 5 +++++ drivers/usb/class/cdc-acm.h | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 7a1164dd1d3..41d4ca527f8 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -89,6 +89,9 @@ static DEFINE_MUTEX(open_mutex); #define ACM_READY(acm) (acm && acm->dev && acm->used) +static const struct tty_port_operations acm_port_ops = { +}; + #ifdef VERBOSE_DEBUG #define verbose 1 #else @@ -1082,6 +1085,8 @@ skip_normal_probe: spin_lock_init(&acm->read_lock); mutex_init(&acm->mutex); acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); + tty_port_init(&acm->port); + acm->port.ops = &acm_port_ops; buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); if (!buf) { diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 1f95e7aa1b6..19967cdb269 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -90,7 +90,8 @@ struct acm { struct usb_interface *control; /* control interface */ struct usb_interface *data; /* data interface */ struct tty_struct *tty; /* the corresponding tty */ - struct urb *ctrlurb; /* urbs */ + struct tty_port port; /* our tty port data */ + struct urb *ctrlurb; /* urbs */ u8 *ctrl_buffer; /* buffers of urbs */ dma_addr_t ctrl_dma; /* dma handles of buffers */ u8 *country_codes; /* country codes from device */ -- cgit v1.2.3-70-g09d2 From 10077d4a6674f535abdbe25cdecb1202af7948f1 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 11 Jun 2009 12:36:09 +0100 Subject: tty: cdc_acm add krefs Now we have a port structure begin using the fields and kref counts Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/usb/class/cdc-acm.c | 164 ++++++++++++++++++++++++++------------------ drivers/usb/class/cdc-acm.h | 2 - 2 files changed, 98 insertions(+), 68 deletions(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 41d4ca527f8..b4e73aa759d 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -62,7 +62,7 @@ #include #include #include -#include +#include #include #include #include @@ -87,7 +87,7 @@ static struct acm *acm_table[ACM_TTY_MINORS]; static DEFINE_MUTEX(open_mutex); -#define ACM_READY(acm) (acm && acm->dev && acm->used) +#define ACM_READY(acm) (acm && acm->dev && acm->port.count) static const struct tty_port_operations acm_port_ops = { }; @@ -265,6 +265,7 @@ static void acm_ctrl_irq(struct urb *urb) { struct acm *acm = urb->context; struct usb_cdc_notification *dr = urb->transfer_buffer; + struct tty_struct *tty; unsigned char *data; int newctrl; int retval; @@ -297,12 +298,16 @@ static void acm_ctrl_irq(struct urb *urb) break; case USB_CDC_NOTIFY_SERIAL_STATE: - + tty = tty_port_tty_get(&acm->port); newctrl = get_unaligned_le16(data); - if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { - dbg("calling hangup"); - tty_hangup(acm->tty); + if (tty) { + if (!acm->clocal && + (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { + dbg("calling hangup"); + tty_hangup(tty); + } + tty_kref_put(tty); } acm->ctrlin = newctrl; @@ -374,15 +379,14 @@ static void acm_rx_tasklet(unsigned long _acm) { struct acm *acm = (void *)_acm; struct acm_rb *buf; - struct tty_struct *tty = acm->tty; + struct tty_struct *tty; struct acm_ru *rcv; unsigned long flags; unsigned char throttled; dbg("Entering acm_rx_tasklet"); - if (!ACM_READY(acm)) - { + if (!ACM_READY(acm)) { dbg("acm_rx_tasklet: ACM not ready"); return; } @@ -390,12 +394,13 @@ static void acm_rx_tasklet(unsigned long _acm) spin_lock_irqsave(&acm->throttle_lock, flags); throttled = acm->throttle; spin_unlock_irqrestore(&acm->throttle_lock, flags); - if (throttled) - { + if (throttled) { dbg("acm_rx_tasklet: throttled"); return; } + tty = tty_port_tty_get(&acm->port); + next_buffer: spin_lock_irqsave(&acm->read_lock, flags); if (list_empty(&acm->filled_read_bufs)) { @@ -409,20 +414,22 @@ next_buffer: dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); - tty_buffer_request_room(tty, buf->size); - spin_lock_irqsave(&acm->throttle_lock, flags); - throttled = acm->throttle; - spin_unlock_irqrestore(&acm->throttle_lock, flags); - if (!throttled) - tty_insert_flip_string(tty, buf->base, buf->size); - tty_flip_buffer_push(tty); - - if (throttled) { - dbg("Throttling noticed"); - spin_lock_irqsave(&acm->read_lock, flags); - list_add(&buf->list, &acm->filled_read_bufs); - spin_unlock_irqrestore(&acm->read_lock, flags); - return; + if (tty) { + spin_lock_irqsave(&acm->throttle_lock, flags); + throttled = acm->throttle; + spin_unlock_irqrestore(&acm->throttle_lock, flags); + if (!throttled) { + tty_buffer_request_room(tty, buf->size); + tty_insert_flip_string(tty, buf->base, buf->size); + tty_flip_buffer_push(tty); + } else { + tty_kref_put(tty); + dbg("Throttling noticed"); + spin_lock_irqsave(&acm->read_lock, flags); + list_add(&buf->list, &acm->filled_read_bufs); + spin_unlock_irqrestore(&acm->read_lock, flags); + return; + } } spin_lock_irqsave(&acm->read_lock, flags); @@ -431,6 +438,8 @@ next_buffer: goto next_buffer; urbs: + tty_kref_put(tty); + while (!list_empty(&acm->spare_read_bufs)) { spin_lock_irqsave(&acm->read_lock, flags); if (list_empty(&acm->spare_read_urbs)) { @@ -502,11 +511,14 @@ static void acm_write_bulk(struct urb *urb) static void acm_softint(struct work_struct *work) { struct acm *acm = container_of(work, struct acm, work); + struct tty_struct *tty; dev_vdbg(&acm->data->dev, "tx work\n"); if (!ACM_READY(acm)) return; - tty_wakeup(acm->tty); + tty = tty_port_tty_get(&acm->port); + tty_wakeup(tty); + tty_kref_put(tty); } static void acm_waker(struct work_struct *waker) @@ -546,8 +558,9 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) rv = 0; set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); + tty->driver_data = acm; - acm->tty = tty; + tty_port_tty_set(&acm->port, tty); if (usb_autopm_get_interface(acm->control) < 0) goto early_bail; @@ -555,11 +568,10 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) acm->control->needs_remote_wakeup = 1; mutex_lock(&acm->mutex); - if (acm->used++) { + if (acm->port.count++) { usb_autopm_put_interface(acm->control); goto done; - } - + } acm->ctrlurb->dev = acm->dev; if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { @@ -570,6 +582,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && (acm->ctrl_caps & USB_CDC_CAP_LINE)) goto full_bailout; + usb_autopm_put_interface(acm->control); INIT_LIST_HEAD(&acm->spare_read_urbs); @@ -585,7 +598,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) acm->throttle = 0; tasklet_schedule(&acm->urb_task); - + rv = tty_port_block_til_ready(&acm->port, tty, filp); done: mutex_unlock(&acm->mutex); err_out: @@ -596,16 +609,17 @@ full_bailout: usb_kill_urb(acm->ctrlurb); bail_out: usb_autopm_put_interface(acm->control); - acm->used--; + acm->port.count--; mutex_unlock(&acm->mutex); early_bail: mutex_unlock(&open_mutex); + tty_port_tty_set(&acm->port, NULL); return -EIO; } static void acm_tty_unregister(struct acm *acm) { - int i,nr; + int i, nr; nr = acm->rx_buflimit; tty_unregister_device(acm_tty_driver, acm->minor); @@ -622,37 +636,51 @@ static void acm_tty_unregister(struct acm *acm) static int acm_tty_chars_in_buffer(struct tty_struct *tty); +static void acm_port_down(struct acm *acm, int drain) +{ + int i, nr = acm->rx_buflimit; + mutex_lock(&open_mutex); + if (acm->dev) { + usb_autopm_get_interface(acm->control); + acm_set_control(acm, acm->ctrlout = 0); + /* try letting the last writes drain naturally */ + if (drain) { + wait_event_interruptible_timeout(acm->drain_wait, + (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev, + ACM_CLOSE_TIMEOUT * HZ); + } + usb_kill_urb(acm->ctrlurb); + for (i = 0; i < ACM_NW; i++) + usb_kill_urb(acm->wb[i].urb); + for (i = 0; i < nr; i++) + usb_kill_urb(acm->ru[i].urb); + acm->control->needs_remote_wakeup = 0; + usb_autopm_put_interface(acm->control); + } + mutex_unlock(&open_mutex); +} + +static void acm_tty_hangup(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + tty_port_hangup(&acm->port); + acm_port_down(acm, 0); +} + static void acm_tty_close(struct tty_struct *tty, struct file *filp) { struct acm *acm = tty->driver_data; - int i,nr; - if (!acm || !acm->used) + /* Perform the closing process and see if we need to do the hardware + shutdown */ + if (tty_port_close_start(&acm->port, tty, filp) == 0) return; - - nr = acm->rx_buflimit; + acm_port_down(acm, 0); + tty_port_close_end(&acm->port, tty); mutex_lock(&open_mutex); - if (!--acm->used) { - if (acm->dev) { - usb_autopm_get_interface(acm->control); - acm_set_control(acm, acm->ctrlout = 0); - - /* try letting the last writes drain naturally */ - wait_event_interruptible_timeout(acm->drain_wait, - (ACM_NW == acm_wb_is_avail(acm)) - || !acm->dev, - ACM_CLOSE_TIMEOUT * HZ); - - usb_kill_urb(acm->ctrlurb); - for (i = 0; i < ACM_NW; i++) - usb_kill_urb(acm->wb[i].urb); - for (i = 0; i < nr; i++) - usb_kill_urb(acm->ru[i].urb); - acm->control->needs_remote_wakeup = 0; - usb_autopm_put_interface(acm->control); - } else - acm_tty_unregister(acm); - } + tty_port_tty_set(&acm->port, NULL); + if (!acm->dev) + acm_tty_unregister(acm); mutex_unlock(&open_mutex); } @@ -885,8 +913,8 @@ static int acm_write_buffers_alloc(struct acm *acm) return 0; } -static int acm_probe (struct usb_interface *intf, - const struct usb_device_id *id) +static int acm_probe(struct usb_interface *intf, + const struct usb_device_id *id) { struct usb_cdc_union_desc *union_header = NULL; struct usb_cdc_country_functional_desc *cfd = NULL; @@ -1232,6 +1260,7 @@ static void acm_disconnect(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); + struct tty_struct *tty; /* sibling interface is already cleaning up */ if (!acm) @@ -1258,16 +1287,18 @@ static void acm_disconnect(struct usb_interface *intf) usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : acm->control); - if (!acm->used) { + if (acm->port.count == 0) { acm_tty_unregister(acm); mutex_unlock(&open_mutex); return; } mutex_unlock(&open_mutex); - - if (acm->tty) - tty_hangup(acm->tty); + tty = tty_port_tty_get(&acm->port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } } #ifdef CONFIG_PM @@ -1302,7 +1333,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message) */ mutex_lock(&acm->mutex); - if (acm->used) + if (acm->port.count) stop_data_traffic(acm); mutex_unlock(&acm->mutex); @@ -1324,7 +1355,7 @@ static int acm_resume(struct usb_interface *intf) return 0; mutex_lock(&acm->mutex); - if (acm->used) { + if (acm->port.count) { rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); if (rv < 0) goto err_out; @@ -1434,6 +1465,7 @@ static struct usb_driver acm_driver = { static const struct tty_operations acm_ops = { .open = acm_tty_open, .close = acm_tty_close, + .hangup = acm_tty_hangup, .write = acm_tty_write, .write_room = acm_tty_write_room, .ioctl = acm_tty_ioctl, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 19967cdb269..4c3856420ad 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -89,7 +89,6 @@ struct acm { struct usb_device *dev; /* the corresponding usb device */ struct usb_interface *control; /* control interface */ struct usb_interface *data; /* data interface */ - struct tty_struct *tty; /* the corresponding tty */ struct tty_port port; /* our tty port data */ struct urb *ctrlurb; /* urbs */ u8 *ctrl_buffer; /* buffers of urbs */ @@ -121,7 +120,6 @@ struct acm { unsigned int ctrlout; /* output control lines (DTR, RTS) */ unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ - unsigned int used; /* someone has this acm's device open */ unsigned int minor; /* acm minor number */ unsigned char throttle; /* throttled by tty layer */ unsigned char clocal; /* termios CLOCAL */ -- cgit v1.2.3-70-g09d2 From 6e47e069eb4dffa88ad91ddfc3fd85f32c35654b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 11 Jun 2009 12:37:06 +0100 Subject: tty: Clean up the ACM driver to CodingStyle Or at least most of it. There are further clean ups possible and there are are also thing checkpatch moans about that would be silly to "fix". Also note some FIXME points found as the cleanup was done. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/usb/class/cdc-acm.c | 291 ++++++++++++++++++++++++-------------------- 1 file changed, 160 insertions(+), 131 deletions(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index b4e73aa759d..ddeb6919253 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -16,7 +16,8 @@ * 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 + * v0.12 - added TIOCM ioctls, added break handling, made struct acm + * kmalloced * v0.13 - added termios, added hangup * v0.14 - sized down struct acm * v0.15 - fixed flow control again - characters could be lost @@ -102,13 +103,15 @@ static const struct tty_port_operations acm_port_ops = { * Functions for ACM control messages. */ -static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len) +static int acm_ctrl_msg(struct acm *acm, int request, int value, + void *buf, int len) { int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), request, USB_RT_ACM, value, acm->control->altsetting[0].desc.bInterfaceNumber, buf, len, 5000); - dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval); + dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", + request, value, len, retval); return retval < 0 ? retval : 0; } @@ -153,9 +156,8 @@ static int acm_wb_is_avail(struct acm *acm) n = ACM_NW; spin_lock_irqsave(&acm->write_lock, flags); - for (i = 0; i < ACM_NW; i++) { + for (i = 0; i < ACM_NW; i++) n -= acm->wb[i].use; - } spin_unlock_irqrestore(&acm->write_lock, flags); return n; } @@ -186,7 +188,8 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb) wb->urb->transfer_buffer_length = wb->len; wb->urb->dev = acm->dev; - if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) { + rc = usb_submit_urb(wb->urb, GFP_ATOMIC); + if (rc < 0) { dbg("usb_submit_urb(write bulk) failed: %d", rc); acm_write_done(acm, wb); } @@ -291,44 +294,45 @@ static void acm_ctrl_irq(struct urb *urb) data = (unsigned char *)(dr + 1); switch (dr->bNotificationType) { + case USB_CDC_NOTIFY_NETWORK_CONNECTION: + dbg("%s network", dr->wValue ? + "connected to" : "disconnected from"); + break; - case USB_CDC_NOTIFY_NETWORK_CONNECTION: + case USB_CDC_NOTIFY_SERIAL_STATE: + tty = tty_port_tty_get(&acm->port); + newctrl = get_unaligned_le16(data); - dbg("%s network", dr->wValue ? "connected to" : "disconnected from"); - break; - - case USB_CDC_NOTIFY_SERIAL_STATE: - tty = tty_port_tty_get(&acm->port); - newctrl = get_unaligned_le16(data); - - if (tty) { - if (!acm->clocal && - (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { - dbg("calling hangup"); - tty_hangup(tty); - } - tty_kref_put(tty); + if (tty) { + if (!acm->clocal && + (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { + dbg("calling hangup"); + tty_hangup(tty); } + tty_kref_put(tty); + } - acm->ctrlin = newctrl; - - 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 ? '+' : '-', - acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); + acm->ctrlin = newctrl; + 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 ? '+' : '-', + acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); break; - default: - dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", - dr->bNotificationType, dr->wIndex, - dr->wLength, data[0], data[1]); - break; + default: + dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", + dr->bNotificationType, dr->wIndex, + dr->wLength, data[0], data[1]); + break; } exit: usb_mark_last_busy(acm->dev); - retval = usb_submit_urb (urb, GFP_ATOMIC); + retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " "result %d", __func__, retval); @@ -466,10 +470,11 @@ urbs: rcv->urb->transfer_dma = buf->dma; rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - /* This shouldn't kill the driver as unsuccessful URBs are returned to the - free-urbs-pool and resubmited ASAP */ + /* This shouldn't kill the driver as unsuccessful URBs are + returned to the free-urbs-pool and resubmited ASAP */ spin_lock_irqsave(&acm->read_lock, flags); - if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { + if (acm->susp_count || + usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { list_add(&buf->list, &acm->spare_read_bufs); list_add(&rcv->list, &acm->spare_read_urbs); acm->processing = 0; @@ -588,12 +593,11 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) INIT_LIST_HEAD(&acm->spare_read_urbs); INIT_LIST_HEAD(&acm->spare_read_bufs); INIT_LIST_HEAD(&acm->filled_read_bufs); - for (i = 0; i < acm->rx_buflimit; i++) { + + for (i = 0; i < acm->rx_buflimit; i++) list_add(&(acm->ru[i].list), &acm->spare_read_urbs); - } - for (i = 0; i < acm->rx_buflimit; i++) { + for (i = 0; i < acm->rx_buflimit; i++) list_add(&(acm->rb[i].list), &acm->spare_read_bufs); - } acm->throttle = 0; @@ -684,7 +688,8 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&open_mutex); } -static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) +static int acm_tty_write(struct tty_struct *tty, + const unsigned char *buf, int count) { struct acm *acm = tty->driver_data; int stat; @@ -700,7 +705,8 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c return 0; spin_lock_irqsave(&acm->write_lock, flags); - if ((wbn = acm_wb_alloc(acm)) < 0) { + wbn = acm_wb_alloc(acm); + if (wbn < 0) { spin_unlock_irqrestore(&acm->write_lock, flags); return 0; } @@ -712,7 +718,8 @@ static int acm_tty_write(struct tty_struct *tty, const unsigned char *buf, int c wb->len = count; spin_unlock_irqrestore(&acm->write_lock, flags); - if ((stat = acm_write_start(acm, wbn)) < 0) + stat = acm_write_start(acm, wbn); + if (stat < 0) return stat; return count; } @@ -798,8 +805,10 @@ static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, return -EINVAL; newctrl = acm->ctrlout; - set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); - clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); + set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | + (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); + clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | + (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); newctrl = (newctrl & ~clear) | set; @@ -808,7 +817,8 @@ static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file, return acm_set_control(acm, acm->ctrlout = newctrl); } -static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { struct acm *acm = tty->driver_data; @@ -830,7 +840,8 @@ static const __u8 acm_tty_size[] = { 5, 6, 7, 8 }; -static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old) +static void acm_tty_set_termios(struct tty_struct *tty, + struct ktermios *termios_old) { struct acm *acm = tty->driver_data; struct ktermios *termios = tty->termios; @@ -840,19 +851,23 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios if (!ACM_READY(acm)) return; + /* FIXME: Needs to support the tty_baud interface */ + /* FIXME: Broken on sparc */ newline.dwDTERate = cpu_to_le32p(acm_tty_speed + (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0; newline.bParityType = termios->c_cflag & PARENB ? - (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; + (termios->c_cflag & PARODD ? 1 : 2) + + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; - + /* FIXME: Needs to clear unsupported bits in the termios */ acm->clocal = ((termios->c_cflag & CLOCAL) != 0); if (!newline.dwDTERate) { newline.dwDTERate = acm->line.dwDTERate; newctrl &= ~ACM_CTRL_DTR; - } else newctrl |= ACM_CTRL_DTR; + } else + newctrl |= ACM_CTRL_DTR; if (newctrl != acm->ctrlout) acm_set_control(acm, acm->ctrlout = newctrl); @@ -877,9 +892,8 @@ static void acm_write_buffers_free(struct acm *acm) struct acm_wb *wb; struct usb_device *usb_dev = interface_to_usbdev(acm->control); - for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { + for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah); - } } static void acm_read_buffers_free(struct acm *acm) @@ -888,7 +902,8 @@ static void acm_read_buffers_free(struct acm *acm) int i, n = acm->rx_buflimit; for (i = 0; i < n; i++) - usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); + usb_buffer_free(usb_dev, acm->readsize, + acm->rb[i].base, acm->rb[i].dma); } /* Little helper: write buffers allocate */ @@ -928,7 +943,7 @@ static int acm_probe(struct usb_interface *intf, struct usb_device *usb_dev = interface_to_usbdev(intf); struct acm *acm; int minor; - int ctrlsize,readsize; + int ctrlsize, readsize; u8 *buf; u8 ac_management_function = 0; u8 call_management_function = 0; @@ -948,7 +963,7 @@ static int acm_probe(struct usb_interface *intf, control_interface = usb_ifnum_to_if(usb_dev, 0); goto skip_normal_probe; } - + /* normal probing*/ if (!buffer) { dev_err(&intf->dev, "Weird descriptor references\n"); @@ -956,8 +971,10 @@ static int acm_probe(struct usb_interface *intf, } if (!buflen) { - if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) { - dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n"); + if (intf->cur_altsetting->endpoint->extralen && + intf->cur_altsetting->endpoint->extra) { + dev_dbg(&intf->dev, + "Seeking extra descriptors on endpoint\n"); buflen = intf->cur_altsetting->endpoint->extralen; buffer = intf->cur_altsetting->endpoint->extra; } else { @@ -968,47 +985,43 @@ static int acm_probe(struct usb_interface *intf, } while (buflen > 0) { - if (buffer [1] != USB_DT_CS_INTERFACE) { + if (buffer[1] != USB_DT_CS_INTERFACE) { dev_err(&intf->dev, "skipping garbage\n"); goto next_desc; } - switch (buffer [2]) { - case USB_CDC_UNION_TYPE: /* we've found it */ - if (union_header) { - dev_err(&intf->dev, "More than one " - "union descriptor, " - "skipping ...\n"); - goto next_desc; - } - union_header = (struct usb_cdc_union_desc *) - buffer; - break; - case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ - cfd = (struct usb_cdc_country_functional_desc *)buffer; - break; - case USB_CDC_HEADER_TYPE: /* maybe check version */ - break; /* for now we ignore it */ - case USB_CDC_ACM_TYPE: - ac_management_function = buffer[3]; - break; - case USB_CDC_CALL_MANAGEMENT_TYPE: - call_management_function = buffer[3]; - call_interface_num = buffer[4]; - if ((call_management_function & 3) != 3) - dev_err(&intf->dev, "This device " - "cannot do calls on its own. " - "It is no modem.\n"); - break; - default: - /* there are LOTS more CDC descriptors that - * could legitimately be found here. - */ - dev_dbg(&intf->dev, "Ignoring descriptor: " - "type %02x, length %d\n", - buffer[2], buffer[0]); - break; + switch (buffer[2]) { + case USB_CDC_UNION_TYPE: /* we've found it */ + if (union_header) { + dev_err(&intf->dev, "More than one " + "union descriptor, skipping ...\n"); + goto next_desc; } + union_header = (struct usb_cdc_union_desc *)buffer; + break; + case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ + cfd = (struct usb_cdc_country_functional_desc *)buffer; + break; + case USB_CDC_HEADER_TYPE: /* maybe check version */ + break; /* for now we ignore it */ + case USB_CDC_ACM_TYPE: + ac_management_function = buffer[3]; + break; + case USB_CDC_CALL_MANAGEMENT_TYPE: + call_management_function = buffer[3]; + call_interface_num = buffer[4]; + if ((call_management_function & 3) != 3) + dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); + break; + default: + /* there are LOTS more CDC descriptors that + * could legitimately be found here. + */ + dev_dbg(&intf->dev, "Ignoring descriptor: " + "type %02x, length %d\n", + buffer[2], buffer[0]); + break; + } next_desc: buflen -= buffer[0]; buffer += buffer[0]; @@ -1016,33 +1029,36 @@ next_desc: if (!union_header) { if (call_interface_num > 0) { - dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n"); + dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); control_interface = intf; } else { - dev_dbg(&intf->dev,"No union descriptor, giving up\n"); + dev_dbg(&intf->dev, + "No union descriptor, giving up\n"); return -ENODEV; } } else { control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); if (!control_interface || !data_interface) { - dev_dbg(&intf->dev,"no interfaces\n"); + dev_dbg(&intf->dev, "no interfaces\n"); return -ENODEV; } } - + if (data_interface_num != call_interface_num) - dev_dbg(&intf->dev,"Separate call control interface. That is not fully supported.\n"); + dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); skip_normal_probe: /*workaround for switched interfaces */ - if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) { - if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { + if (data_interface->cur_altsetting->desc.bInterfaceClass + != CDC_DATA_INTERFACE_TYPE) { + if (control_interface->cur_altsetting->desc.bInterfaceClass + == CDC_DATA_INTERFACE_TYPE) { struct usb_interface *t; - dev_dbg(&intf->dev,"Your device has switched interfaces.\n"); - + dev_dbg(&intf->dev, + "Your device has switched interfaces.\n"); t = control_interface; control_interface = data_interface; data_interface = t; @@ -1054,9 +1070,9 @@ skip_normal_probe: /* Accept probe requests only for the control interface */ if (intf != control_interface) return -ENODEV; - + if (usb_interface_claimed(data_interface)) { /* valid in this context */ - dev_dbg(&intf->dev,"The data interface isn't available\n"); + dev_dbg(&intf->dev, "The data interface isn't available\n"); return -EBUSY; } @@ -1073,8 +1089,8 @@ skip_normal_probe: if (!usb_endpoint_dir_in(epread)) { /* descriptors are swapped */ struct usb_endpoint_descriptor *t; - dev_dbg(&intf->dev,"The data interface has switched endpoints\n"); - + dev_dbg(&intf->dev, + "The data interface has switched endpoints\n"); t = epread; epread = epwrite; epwrite = t; @@ -1087,13 +1103,15 @@ skip_normal_probe: return -ENODEV; } - if (!(acm = kzalloc(sizeof(struct acm), GFP_KERNEL))) { + acm = kzalloc(sizeof(struct acm), GFP_KERNEL); + if (acm == NULL) { dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n"); goto alloc_fail; } ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); - readsize = le16_to_cpu(epread->wMaxPacketSize)* ( quirks == SINGLE_RX_URB ? 1 : 2); + readsize = le16_to_cpu(epread->wMaxPacketSize) * + (quirks == SINGLE_RX_URB ? 1 : 2); acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; acm->control = control_interface; acm->data = data_interface; @@ -1136,8 +1154,10 @@ skip_normal_probe: for (i = 0; i < num_rx_buf; i++) { struct acm_ru *rcv = &(acm->ru[i]); - if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) { - dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n"); + rcv->urb = usb_alloc_urb(0, GFP_KERNEL); + if (rcv->urb == NULL) { + dev_dbg(&intf->dev, + "out of memory (read urbs usb_alloc_urb)\n"); goto alloc_fail7; } @@ -1150,26 +1170,29 @@ skip_normal_probe: rb->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &rb->dma); if (!rb->base) { - dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n"); + dev_dbg(&intf->dev, + "out of memory (read bufs usb_buffer_alloc)\n"); goto alloc_fail7; } } - for(i = 0; i < ACM_NW; i++) - { + for (i = 0; i < ACM_NW; i++) { struct acm_wb *snd = &(acm->wb[i]); - if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) { - dev_dbg(&intf->dev, "out of memory (write urbs usb_alloc_urb)"); + snd->urb = usb_alloc_urb(0, GFP_KERNEL); + if (snd->urb == NULL) { + dev_dbg(&intf->dev, + "out of memory (write urbs usb_alloc_urb)"); goto alloc_fail7; } - usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), - NULL, acm->writesize, acm_write_bulk, snd); + usb_fill_bulk_urb(snd->urb, usb_dev, + usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), + NULL, acm->writesize, acm_write_bulk, snd); snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; snd->instance = acm; } - usb_set_intfdata (intf, acm); + usb_set_intfdata(intf, acm); i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); if (i < 0) @@ -1180,7 +1203,8 @@ skip_normal_probe: if (!acm->country_codes) goto skip_countries; acm->country_code_size = cfd->bLength - 4; - memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4); + memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, + cfd->bLength - 4); acm->country_rel_date = cfd->iCountryCodeRelDate; i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); @@ -1189,7 +1213,8 @@ skip_normal_probe: goto skip_countries; } - i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate); + i = device_create_file(&intf->dev, + &dev_attr_iCountryCodeRelDate); if (i < 0) { kfree(acm->country_codes); goto skip_countries; @@ -1197,8 +1222,10 @@ skip_normal_probe: } skip_countries: - usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), - acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); + usb_fill_int_urb(acm->ctrlurb, usb_dev, + usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), + acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, + epctrl->bInterval); acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; acm->ctrlurb->transfer_dma = acm->ctrl_dma; @@ -1245,7 +1272,7 @@ static void stop_data_traffic(struct acm *acm) tasklet_disable(&acm->urb_task); usb_kill_urb(acm->ctrlurb); - for(i = 0; i < ACM_NW; i++) + for (i = 0; i < ACM_NW; i++) usb_kill_urb(acm->wb[i].urb); for (i = 0; i < acm->rx_buflimit; i++) usb_kill_urb(acm->ru[i].urb); @@ -1267,7 +1294,7 @@ static void acm_disconnect(struct usb_interface *intf) return; mutex_lock(&open_mutex); - if (acm->country_codes){ + if (acm->country_codes) { device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); device_remove_file(&acm->control->dev, @@ -1281,7 +1308,8 @@ static void acm_disconnect(struct usb_interface *intf) stop_data_traffic(acm); acm_write_buffers_free(acm); - usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); + usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, + acm->ctrl_dma); acm_read_buffers_free(acm); usb_driver_release_interface(&acm_driver, intf == acm->control ? @@ -1434,7 +1462,7 @@ static struct usb_device_id acm_ids[] = { { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_GSM) }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, - USB_CDC_ACM_PROTO_AT_3G ) }, + USB_CDC_ACM_PROTO_AT_3G) }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_CDMA) }, @@ -1442,7 +1470,7 @@ static struct usb_device_id acm_ids[] = { { } }; -MODULE_DEVICE_TABLE (usb, acm_ids); +MODULE_DEVICE_TABLE(usb, acm_ids); static struct usb_driver acm_driver = { .name = "cdc_acm", @@ -1497,7 +1525,8 @@ static int __init acm_init(void) acm_tty_driver->subtype = SERIAL_TYPE_NORMAL, acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; acm_tty_driver->init_termios = tty_std_termios; - acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | + HUPCL | CLOCAL; tty_set_operations(acm_tty_driver, &acm_ops); retval = tty_register_driver(acm_tty_driver); @@ -1529,7 +1558,7 @@ static void __exit acm_exit(void) module_init(acm_init); module_exit(acm_exit); -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR); -- cgit v1.2.3-70-g09d2 From f7a386c5b8ff34cd84ae922603d1c6f9d234edee Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Thu, 30 Apr 2009 15:23:42 +0200 Subject: Driver Core: usb: add nodename support for usb drivers. This adds support for USB drivers to report their requested nodename to userspace. It also updates a number of USB drivers to provide the needed subdirectory and device name to be used for them. Signed-off-by: Kay Sievers Signed-off-by: Jan Blunck Signed-off-by: Greg Kroah-Hartman --- drivers/hid/usbhid/hiddev.c | 7 ++++++- drivers/media/video/dabusb.c | 6 ++++++ drivers/usb/class/usblp.c | 6 ++++++ drivers/usb/core/file.c | 13 ++++++++++++- drivers/usb/core/usb.c | 11 +++++++++++ drivers/usb/misc/iowarrior.c | 6 ++++++ drivers/usb/misc/legousbtower.c | 6 ++++++ include/linux/usb.h | 3 +++ 8 files changed, 56 insertions(+), 2 deletions(-) (limited to 'drivers/usb/class') diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index e9b436d2d94..9e9421525fb 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -850,8 +850,14 @@ static const struct file_operations hiddev_fops = { #endif }; +static char *hiddev_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + static struct usb_class_driver hiddev_class = { .name = "hiddev%d", + .nodename = hiddev_nodename, .fops = &hiddev_fops, .minor_base = HIDDEV_MINOR_BASE, }; @@ -955,7 +961,6 @@ static int hiddev_usbd_probe(struct usb_interface *intf, return -ENODEV; } - static /* const */ struct usb_driver hiddev_driver = { .name = "hiddev", .probe = hiddev_usbd_probe, diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index ba3709bec3f..ec2f45dde16 100644 --- a/drivers/media/video/dabusb.c +++ b/drivers/media/video/dabusb.c @@ -747,8 +747,14 @@ static const struct file_operations dabusb_fops = .release = dabusb_release, }; +static char *dabusb_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + static struct usb_class_driver dabusb_class = { .name = "dabusb%d", + .nodename = dabusb_nodename, .fops = &dabusb_fops, .minor_base = DABUSB_MINOR, }; diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index d2747a49b97..26c09f0257d 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -1057,8 +1057,14 @@ static const struct file_operations usblp_fops = { .release = usblp_release, }; +static char *usblp_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + static struct usb_class_driver usblp_class = { .name = "lp%d", + .nodename = usblp_nodename, .fops = &usblp_fops, .minor_base = USBLP_MINOR_BASE, }; diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 997e659ff69..5cef88929b3 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -67,6 +67,16 @@ static struct usb_class { struct class *class; } *usb_class; +static char *usb_nodename(struct device *dev) +{ + struct usb_class_driver *drv; + + drv = dev_get_drvdata(dev); + if (!drv || !drv->nodename) + return NULL; + return drv->nodename(dev); +} + static int init_usb_class(void) { int result = 0; @@ -90,6 +100,7 @@ static int init_usb_class(void) kfree(usb_class); usb_class = NULL; } + usb_class->class->nodename = usb_nodename; exit: return result; @@ -198,7 +209,7 @@ int usb_register_dev(struct usb_interface *intf, else temp = name; intf->usb_dev = device_create(usb_class->class, &intf->dev, - MKDEV(USB_MAJOR, minor), NULL, + MKDEV(USB_MAJOR, minor), class_driver, "%s", temp); if (IS_ERR(intf->usb_dev)) { down_write(&minor_rwsem); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 7eee400d3e3..927a27dd2f8 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -305,10 +305,21 @@ static struct dev_pm_ops usb_device_pm_ops = { #endif /* CONFIG_PM */ + +static char *usb_nodename(struct device *dev) +{ + struct usb_device *usb_dev; + + usb_dev = to_usb_device(dev); + return kasprintf(GFP_KERNEL, "bus/usb/%03d/%03d", + usb_dev->bus->busnum, usb_dev->devnum); +} + struct device_type usb_device_type = { .name = "usb_device", .release = usb_release_dev, .uevent = usb_dev_uevent, + .nodename = usb_nodename, .pm = &usb_device_pm_ops, }; diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index a4ef77ef917..3c5fe5cee05 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -726,12 +726,18 @@ static const struct file_operations iowarrior_fops = { .poll = iowarrior_poll, }; +static char *iowarrior_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + /* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with devfs and the driver core */ static struct usb_class_driver iowarrior_class = { .name = "iowarrior%d", + .nodename = iowarrior_nodename, .fops = &iowarrior_fops, .minor_base = IOWARRIOR_MINOR_BASE, }; diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index ab0f3226158..c1e2433f640 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -266,12 +266,18 @@ static const struct file_operations tower_fops = { .llseek = tower_llseek, }; +static char *legousbtower_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + /* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with the driver core */ static struct usb_class_driver tower_class = { .name = "legousbtower%d", + .nodename = legousbtower_nodename, .fops = &tower_fops, .minor_base = LEGO_USB_TOWER_MINOR_BASE, }; diff --git a/include/linux/usb.h b/include/linux/usb.h index 3aa2cd1f8d0..34cdfcac455 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -869,6 +869,8 @@ struct usb_driver { * struct usb_device_driver - identifies USB device driver to usbcore * @name: The driver name should be unique among USB drivers, * and should normally be the same as the module name. + * @nodename: Callback to provide a naming hint for a possible + * device node to create. * @probe: Called to see if the driver is willing to manage a particular * device. If it is, probe returns zero and uses dev_set_drvdata() * to associate driver-specific data with the device. If unwilling @@ -912,6 +914,7 @@ extern struct bus_type usb_bus_type; */ struct usb_class_driver { char *name; + char *(*nodename)(struct device *dev); const struct file_operations *fops; int minor_base; }; -- cgit v1.2.3-70-g09d2 From a2bfb4a346d2c2e25f84b35c6044ff53296be1ee Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Sat, 16 May 2009 21:13:19 +0200 Subject: USB: support for cdc-acm of single interface devices This implement support in cdc-acm for acm devices another popular OS can handle - adds support for autodetection of devices that use one interface - autodetection of endpoints - add a quirk for surpressing a setting that OS doesn't use - autoassume that quirk for single interface devices Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 68 +++++++++++++++++++++++++++++++++++++-------- drivers/usb/class/cdc-acm.h | 2 ++ 2 files changed, 58 insertions(+), 12 deletions(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index ddeb6919253..778e023d146 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -937,9 +937,9 @@ static int acm_probe(struct usb_interface *intf, int buflen = intf->altsetting->extralen; struct usb_interface *control_interface; struct usb_interface *data_interface; - struct usb_endpoint_descriptor *epctrl; - struct usb_endpoint_descriptor *epread; - struct usb_endpoint_descriptor *epwrite; + struct usb_endpoint_descriptor *epctrl = NULL; + struct usb_endpoint_descriptor *epread = NULL; + struct usb_endpoint_descriptor *epwrite = NULL; struct usb_device *usb_dev = interface_to_usbdev(intf); struct acm *acm; int minor; @@ -952,6 +952,7 @@ static int acm_probe(struct usb_interface *intf, unsigned long quirks; int num_rx_buf; int i; + int combined_interfaces = 0; /* normal quirks */ quirks = (unsigned long)id->driver_info; @@ -1033,9 +1034,15 @@ next_desc: data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); control_interface = intf; } else { - dev_dbg(&intf->dev, - "No union descriptor, giving up\n"); - return -ENODEV; + if (intf->cur_altsetting->desc.bNumEndpoints != 3) { + dev_dbg(&intf->dev,"No union descriptor, giving up\n"); + return -ENODEV; + } else { + dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n"); + combined_interfaces = 1; + control_interface = data_interface = intf; + goto look_for_collapsed_interface; + } } } else { control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); @@ -1049,6 +1056,36 @@ next_desc: if (data_interface_num != call_interface_num) dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); + if (control_interface == data_interface) { + /* some broken devices designed for windows work this way */ + dev_warn(&intf->dev,"Control and data interfaces are not separated!\n"); + combined_interfaces = 1; + /* a popular other OS doesn't use it */ + quirks |= NO_CAP_LINE; + if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) { + dev_err(&intf->dev, "This needs exactly 3 endpoints\n"); + return -EINVAL; + } +look_for_collapsed_interface: + for (i = 0; i < 3; i++) { + struct usb_endpoint_descriptor *ep; + ep = &data_interface->cur_altsetting->endpoint[i].desc; + + if (usb_endpoint_is_int_in(ep)) + epctrl = ep; + else if (usb_endpoint_is_bulk_out(ep)) + epwrite = ep; + else if (usb_endpoint_is_bulk_in(ep)) + epread = ep; + else + return -EINVAL; + } + if (!epctrl || !epread || !epwrite) + return -ENODEV; + else + goto made_compressed_probe; + } + skip_normal_probe: /*workaround for switched interfaces */ @@ -1068,10 +1105,11 @@ skip_normal_probe: } /* Accept probe requests only for the control interface */ - if (intf != control_interface) + if (!combined_interfaces && intf != control_interface) return -ENODEV; - if (usb_interface_claimed(data_interface)) { /* valid in this context */ + if (!combined_interfaces && usb_interface_claimed(data_interface)) { + /* valid in this context */ dev_dbg(&intf->dev, "The data interface isn't available\n"); return -EBUSY; } @@ -1095,6 +1133,7 @@ skip_normal_probe: epread = epwrite; epwrite = t; } +made_compressed_probe: dbg("interfaces are valid"); for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); @@ -1112,12 +1151,15 @@ skip_normal_probe: ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); readsize = le16_to_cpu(epread->wMaxPacketSize) * (quirks == SINGLE_RX_URB ? 1 : 2); + acm->combined_interfaces = combined_interfaces; acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; acm->control = control_interface; acm->data = data_interface; acm->minor = minor; acm->dev = usb_dev; acm->ctrl_caps = ac_management_function; + if (quirks & NO_CAP_LINE) + acm->ctrl_caps &= ~USB_CDC_CAP_LINE; acm->ctrlsize = ctrlsize; acm->readsize = readsize; acm->rx_buflimit = num_rx_buf; @@ -1223,9 +1265,10 @@ skip_normal_probe: skip_countries: usb_fill_int_urb(acm->ctrlurb, usb_dev, - usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), - acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, - epctrl->bInterval); + usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), + acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, + /* works around buggy devices */ + epctrl->bInterval ? epctrl->bInterval : 0xff); acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; acm->ctrlurb->transfer_dma = acm->ctrl_dma; @@ -1312,7 +1355,8 @@ static void acm_disconnect(struct usb_interface *intf) acm->ctrl_dma); acm_read_buffers_free(acm); - usb_driver_release_interface(&acm_driver, intf == acm->control ? + if (!acm->combined_interfaces) + usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : acm->control); if (acm->port.count == 0) { diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 4c3856420ad..1602324808b 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -125,6 +125,7 @@ struct acm { unsigned char clocal; /* termios CLOCAL */ unsigned int ctrl_caps; /* control capabilities from the class specific header */ unsigned int susp_count; /* number of suspended interfaces */ + int combined_interfaces:1; /* control and data collapsed */ struct acm_wb *delayed_wb; /* write queued for a device about to be woken */ }; @@ -133,3 +134,4 @@ struct acm { /* constants describing various quirks and errors */ #define NO_UNION_NORMAL 1 #define SINGLE_RX_URB 2 +#define NO_CAP_LINE 4 -- cgit v1.2.3-70-g09d2 From 1f17c5026ce27d0449903d34f9fca461a45fe1cb Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 28 May 2009 20:33:58 +0400 Subject: USB: cdc-acm: quirk for Alcatel OT-I650 This mobile phone fails to work as a modem, failing with: cdc_acm: Zero length descriptor references cdc_acm: probe of 1-6.1.3:1.1 failed with error -22 Tested to work fine with this patch. Signed-off-by: Kir Kolyshkin Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 778e023d146..38bfdb0f666 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1495,6 +1495,9 @@ static struct usb_device_id acm_ids[] = { Maybe we should define a new quirk for this. */ }, + { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */ + .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ + }, /* control interfaces with various AT-command sets */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, -- cgit v1.2.3-70-g09d2 From a92b63e7e4c185b4dd9e87762e2cb716e54482d0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 15 Jun 2009 13:13:05 -0700 Subject: USB: usbtmc: fix switch statment Steve Holland pointed out that we forgot to call break; in the switch statment. This probably resolves a lot of the bug reports I've gotten for the driver lately. Stupid me... Reported-by: Steve Holland Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index c40a9b284cc..3703789d0d2 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -927,21 +927,27 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case USBTMC_IOCTL_CLEAR_OUT_HALT: retval = usbtmc_ioctl_clear_out_halt(data); + break; case USBTMC_IOCTL_CLEAR_IN_HALT: retval = usbtmc_ioctl_clear_in_halt(data); + break; case USBTMC_IOCTL_INDICATOR_PULSE: retval = usbtmc_ioctl_indicator_pulse(data); + break; case USBTMC_IOCTL_CLEAR: retval = usbtmc_ioctl_clear(data); + break; case USBTMC_IOCTL_ABORT_BULK_OUT: retval = usbtmc_ioctl_abort_bulk_out(data); + break; case USBTMC_IOCTL_ABORT_BULK_IN: retval = usbtmc_ioctl_abort_bulk_in(data); + break; } mutex_unlock(&data->io_mutex); -- cgit v1.2.3-70-g09d2