diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-10-28 16:26:12 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-10-28 16:26:12 +0100 |
commit | 7a9787e1eba95a166265e6a260cf30af04ef0a99 (patch) | |
tree | e730a4565e0318140d2fbd2f0415d18a339d7336 /drivers/usb/class | |
parent | 41b9eb264c8407655db57b60b4457fe1b2ec9977 (diff) | |
parent | 0173a3265b228da319ceb9c1ec6a5682fd1b2d92 (diff) |
Merge commit 'v2.6.28-rc2' into x86/pci-ioapic-boot-irq-quirks
Diffstat (limited to 'drivers/usb/class')
-rw-r--r-- | drivers/usb/class/Kconfig | 10 | ||||
-rw-r--r-- | drivers/usb/class/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 270 | ||||
-rw-r--r-- | drivers/usb/class/cdc-acm.h | 6 | ||||
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 169 | ||||
-rw-r--r-- | drivers/usb/class/usblp.c | 25 | ||||
-rw-r--r-- | drivers/usb/class/usbtmc.c | 1087 |
7 files changed, 1458 insertions, 110 deletions
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig index 66f17ed88cb..2519e320098 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -40,3 +40,13 @@ config USB_WDM To compile this driver as a module, choose M here: the module will be called cdc-wdm. +config USB_TMC + tristate "USB Test and Measurement Class support" + depends on USB + help + Say Y here if you want to connect a USB device that follows + the USB.org specification for USB Test and Measurement devices + to your computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called usbtmc. diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile index 535d59a3060..32e85277b5c 100644 --- a/drivers/usb/class/Makefile +++ b/drivers/usb/class/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_USB_ACM) += cdc-acm.o obj-$(CONFIG_USB_PRINTER) += usblp.o obj-$(CONFIG_USB_WDM) += cdc-wdm.o +obj-$(CONFIG_USB_TMC) += usbtmc.o diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index c3201affa0b..20104443081 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -51,6 +51,7 @@ */ #undef DEBUG +#undef VERBOSE_DEBUG #include <linux/kernel.h> #include <linux/errno.h> @@ -70,6 +71,9 @@ #include "cdc-acm.h" + +#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */ + /* * Version Information */ @@ -85,6 +89,12 @@ static DEFINE_MUTEX(open_mutex); #define ACM_READY(acm) (acm && acm->dev && acm->used) +#ifdef VERBOSE_DEBUG +#define verbose 1 +#else +#define verbose 0 +#endif + /* * Functions for ACM control messages. */ @@ -136,19 +146,17 @@ static int acm_wb_alloc(struct acm *acm) static int acm_wb_is_avail(struct acm *acm) { int i, n; + unsigned long flags; n = ACM_NW; + spin_lock_irqsave(&acm->write_lock, flags); for (i = 0; i < ACM_NW; i++) { n -= acm->wb[i].use; } + spin_unlock_irqrestore(&acm->write_lock, flags); return n; } -static inline int acm_wb_is_used(struct acm *acm, int wbn) -{ - return acm->wb[wbn].use; -} - /* * Finish write. */ @@ -157,51 +165,62 @@ static void acm_write_done(struct acm *acm, struct acm_wb *wb) unsigned long flags; spin_lock_irqsave(&acm->write_lock, flags); - acm->write_ready = 1; wb->use = 0; + acm->transmitting--; spin_unlock_irqrestore(&acm->write_lock, flags); } /* * Poke write. + * + * the caller is responsible for locking */ + +static int acm_start_wb(struct acm *acm, struct acm_wb *wb) +{ + int rc; + + acm->transmitting++; + + wb->urb->transfer_buffer = wb->buf; + wb->urb->transfer_dma = wb->dmah; + wb->urb->transfer_buffer_length = wb->len; + wb->urb->dev = acm->dev; + + if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) { + dbg("usb_submit_urb(write bulk) failed: %d", rc); + acm_write_done(acm, wb); + } + return rc; +} + static int acm_write_start(struct acm *acm, int wbn) { unsigned long flags; - struct acm_wb *wb; + struct acm_wb *wb = &acm->wb[wbn]; int rc; spin_lock_irqsave(&acm->write_lock, flags); if (!acm->dev) { + wb->use = 0; spin_unlock_irqrestore(&acm->write_lock, flags); return -ENODEV; } - if (!acm->write_ready) { + dbg("%s susp_count: %d", __func__, acm->susp_count); + if (acm->susp_count) { + acm->delayed_wb = wb; + schedule_work(&acm->waker); spin_unlock_irqrestore(&acm->write_lock, flags); return 0; /* A white lie */ } + usb_mark_last_busy(acm->dev); - if (!acm_wb_is_used(acm, wbn)) { - spin_unlock_irqrestore(&acm->write_lock, flags); - return 0; - } - wb = &acm->wb[wbn]; - - if(acm_wb_is_avail(acm) <= 1) - acm->write_ready = 0; + rc = acm_start_wb(acm, wb); spin_unlock_irqrestore(&acm->write_lock, flags); - wb->urb->transfer_buffer = wb->buf; - wb->urb->transfer_dma = wb->dmah; - wb->urb->transfer_buffer_length = wb->len; - wb->urb->dev = acm->dev; - - if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) { - dbg("usb_submit_urb(write bulk) failed: %d", rc); - acm_write_done(acm, wb); - } return rc; + } /* * attributes exported through sysfs @@ -304,10 +323,11 @@ static void acm_ctrl_irq(struct urb *urb) break; } exit: + usb_mark_last_busy(acm->dev); retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) - err ("%s - usb_submit_urb failed with result %d", - __func__, retval); + dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " + "result %d", __func__, retval); } /* data interface returns incoming bytes, or we got unthrottled */ @@ -320,8 +340,11 @@ static void acm_read_bulk(struct urb *urb) dbg("Entering acm_read_bulk with status %d", status); - if (!ACM_READY(acm)) + if (!ACM_READY(acm)) { + dev_dbg(&acm->data->dev, "Aborting, acm not ready"); return; + } + usb_mark_last_busy(acm->dev); if (status) dev_dbg(&acm->data->dev, "bulk rx status %d\n", status); @@ -331,6 +354,7 @@ static void acm_read_bulk(struct urb *urb) if (likely(status == 0)) { spin_lock(&acm->read_lock); + acm->processing++; list_add_tail(&rcv->list, &acm->spare_read_urbs); list_add_tail(&buf->list, &acm->filled_read_bufs); spin_unlock(&acm->read_lock); @@ -343,7 +367,8 @@ static void acm_read_bulk(struct urb *urb) /* nevertheless the tasklet must be kicked unconditionally so the queue cannot dry up */ } - tasklet_schedule(&acm->urb_task); + if (likely(!acm->susp_count)) + tasklet_schedule(&acm->urb_task); } static void acm_rx_tasklet(unsigned long _acm) @@ -354,16 +379,23 @@ static void acm_rx_tasklet(unsigned long _acm) struct acm_ru *rcv; unsigned long flags; unsigned char throttled; + dbg("Entering acm_rx_tasklet"); if (!ACM_READY(acm)) + { + dbg("acm_rx_tasklet: ACM not ready"); return; + } spin_lock_irqsave(&acm->throttle_lock, flags); throttled = acm->throttle; spin_unlock_irqrestore(&acm->throttle_lock, flags); if (throttled) + { + dbg("acm_rx_tasklet: throttled"); return; + } next_buffer: spin_lock_irqsave(&acm->read_lock, flags); @@ -403,6 +435,7 @@ urbs: while (!list_empty(&acm->spare_read_bufs)) { spin_lock_irqsave(&acm->read_lock, flags); if (list_empty(&acm->spare_read_urbs)) { + acm->processing = 0; spin_unlock_irqrestore(&acm->read_lock, flags); return; } @@ -425,44 +458,72 @@ urbs: rcv->urb->transfer_dma = buf->dma; rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); - /* This shouldn't kill the driver as unsuccessful URBs are returned to the free-urbs-pool and resubmited ASAP */ - if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { + spin_lock_irqsave(&acm->read_lock, flags); + if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { list_add(&buf->list, &acm->spare_read_bufs); - spin_lock_irqsave(&acm->read_lock, flags); list_add(&rcv->list, &acm->spare_read_urbs); + acm->processing = 0; spin_unlock_irqrestore(&acm->read_lock, flags); return; + } else { + spin_unlock_irqrestore(&acm->read_lock, flags); + dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf); } } + spin_lock_irqsave(&acm->read_lock, flags); + acm->processing = 0; + spin_unlock_irqrestore(&acm->read_lock, flags); } /* data interface wrote those outgoing bytes */ static void acm_write_bulk(struct urb *urb) { - struct acm *acm; struct acm_wb *wb = urb->context; + struct acm *acm = wb->instance; - dbg("Entering acm_write_bulk with status %d", urb->status); + if (verbose || urb->status + || (urb->actual_length != urb->transfer_buffer_length)) + dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n", + urb->actual_length, + urb->transfer_buffer_length, + urb->status); - acm = wb->instance; acm_write_done(acm, wb); if (ACM_READY(acm)) schedule_work(&acm->work); + else + wake_up_interruptible(&acm->drain_wait); } static void acm_softint(struct work_struct *work) { struct acm *acm = container_of(work, struct acm, work); - dbg("Entering acm_softint."); - + + dev_vdbg(&acm->data->dev, "tx work\n"); if (!ACM_READY(acm)) return; tty_wakeup(acm->tty); } +static void acm_waker(struct work_struct *waker) +{ + struct acm *acm = container_of(waker, struct acm, waker); + int rv; + + rv = usb_autopm_get_interface(acm->control); + if (rv < 0) { + dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__); + return; + } + if (acm->delayed_wb) { + acm_start_wb(acm, acm->delayed_wb); + acm->delayed_wb = NULL; + } + usb_autopm_put_interface(acm->control); +} + /* * TTY handlers */ @@ -492,6 +553,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) if (usb_autopm_get_interface(acm->control) < 0) goto early_bail; + else + acm->control->needs_remote_wakeup = 1; mutex_lock(&acm->mutex); if (acm->used++) { @@ -509,6 +572,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); INIT_LIST_HEAD(&acm->spare_read_bufs); @@ -525,8 +589,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) tasklet_schedule(&acm->urb_task); done: -err_out: mutex_unlock(&acm->mutex); +err_out: mutex_unlock(&open_mutex); return rv; @@ -558,6 +622,8 @@ static void acm_tty_unregister(struct acm *acm) kfree(acm); } +static int acm_tty_chars_in_buffer(struct tty_struct *tty); + static void acm_tty_close(struct tty_struct *tty, struct file *filp) { struct acm *acm = tty->driver_data; @@ -570,12 +636,21 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) 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); @@ -625,7 +700,7 @@ static int acm_tty_write_room(struct tty_struct *tty) * Do not let the line discipline to know that we have a reserve, * or it might get too enthusiastic. */ - return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0; + return acm_wb_is_avail(acm) ? acm->writesize : 0; } static int acm_tty_chars_in_buffer(struct tty_struct *tty) @@ -660,13 +735,16 @@ static void acm_tty_unthrottle(struct tty_struct *tty) tasklet_schedule(&acm->urb_task); } -static void acm_tty_break_ctl(struct tty_struct *tty, int state) +static int acm_tty_break_ctl(struct tty_struct *tty, int state) { struct acm *acm = tty->driver_data; + int retval; if (!ACM_READY(acm)) - return; - if (acm_send_break(acm, state ? 0xffff : 0)) + return -EINVAL; + retval = acm_send_break(acm, state ? 0xffff : 0); + if (retval < 0) dbg("send break failed"); + return retval; } static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file) @@ -766,17 +844,27 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios * USB probe and disconnect routines. */ -/* Little helper: write buffers free */ +/* Little helpers: write/read buffers free */ static void acm_write_buffers_free(struct acm *acm) { int i; 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++) { - usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah); + usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah); } } +static void acm_read_buffers_free(struct acm *acm) +{ + struct usb_device *usb_dev = interface_to_usbdev(acm->control); + 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); +} + /* Little helper: write buffers allocate */ static int acm_write_buffers_alloc(struct acm *acm) { @@ -837,7 +925,7 @@ static int acm_probe (struct usb_interface *intf, /* normal probing*/ if (!buffer) { - err("Weird descriptor references\n"); + dev_err(&intf->dev, "Weird descriptor references\n"); return -EINVAL; } @@ -847,21 +935,24 @@ static int acm_probe (struct usb_interface *intf, buflen = intf->cur_altsetting->endpoint->extralen; buffer = intf->cur_altsetting->endpoint->extra; } else { - err("Zero length descriptor references\n"); + dev_err(&intf->dev, + "Zero length descriptor references\n"); return -EINVAL; } } while (buflen > 0) { if (buffer [1] != USB_DT_CS_INTERFACE) { - err("skipping garbage\n"); + 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) { - err("More than one union descriptor, skipping ..."); + dev_err(&intf->dev, "More than one " + "union descriptor, " + "skipping ...\n"); goto next_desc; } union_header = (struct usb_cdc_union_desc *) @@ -879,7 +970,9 @@ static int acm_probe (struct usb_interface *intf, call_management_function = buffer[3]; call_interface_num = buffer[4]; if ((call_management_function & 3) != 3) - err("This device cannot do calls on its own. It is no modem."); + 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 @@ -964,7 +1057,7 @@ skip_normal_probe: for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); if (minor == ACM_TTY_MINORS) { - err("no more free acm devices"); + dev_err(&intf->dev, "no more free acm devices\n"); return -ENODEV; } @@ -987,11 +1080,12 @@ skip_normal_probe: acm->urb_task.func = acm_rx_tasklet; acm->urb_task.data = (unsigned long) acm; INIT_WORK(&acm->work, acm_softint); + INIT_WORK(&acm->waker, acm_waker); + init_waitqueue_head(&acm->drain_wait); spin_lock_init(&acm->throttle_lock); spin_lock_init(&acm->write_lock); spin_lock_init(&acm->read_lock); mutex_init(&acm->mutex); - acm->write_ready = 1; acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); @@ -1023,9 +1117,11 @@ skip_normal_probe: rcv->instance = acm; } for (i = 0; i < num_rx_buf; i++) { - struct acm_rb *buf = &(acm->rb[i]); + struct acm_rb *rb = &(acm->rb[i]); - if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) { + 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"); goto alloc_fail7; } @@ -1087,6 +1183,7 @@ skip_countries: acm_set_line(acm, &acm->line); usb_driver_claim_interface(&acm_driver, data_interface, acm); + usb_set_intfdata(data_interface, acm); usb_get_intf(control_interface); tty_register_device(acm_tty_driver, minor, &control_interface->dev); @@ -1098,8 +1195,7 @@ alloc_fail8: for (i = 0; i < ACM_NW; i++) usb_free_urb(acm->wb[i].urb); alloc_fail7: - for (i = 0; i < num_rx_buf; i++) - usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); + acm_read_buffers_free(acm); for (i = 0; i < num_rx_buf; i++) usb_free_urb(acm->ru[i].urb); usb_free_urb(acm->ctrlurb); @@ -1116,6 +1212,7 @@ alloc_fail: static void stop_data_traffic(struct acm *acm) { int i; + dbg("Entering stop_data_traffic"); tasklet_disable(&acm->urb_task); @@ -1128,24 +1225,19 @@ static void stop_data_traffic(struct acm *acm) tasklet_enable(&acm->urb_task); cancel_work_sync(&acm->work); + cancel_work_sync(&acm->waker); } static void acm_disconnect(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); - int i; - if (!acm || !acm->dev) { - dbg("disconnect on nonexisting interface"); + /* sibling interface is already cleaning up */ + if (!acm) return; - } mutex_lock(&open_mutex); - if (!usb_get_intfdata(intf)) { - mutex_unlock(&open_mutex); - return; - } if (acm->country_codes){ device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); @@ -1161,10 +1253,10 @@ static void acm_disconnect(struct usb_interface *intf) acm_write_buffers_free(acm); usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); - for (i = 0; i < acm->rx_buflimit; i++) - usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); + acm_read_buffers_free(acm); - usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf); + usb_driver_release_interface(&acm_driver, intf == acm->control ? + acm->data : acm->control); if (!acm->used) { acm_tty_unregister(acm); @@ -1178,11 +1270,31 @@ static void acm_disconnect(struct usb_interface *intf) tty_hangup(acm->tty); } +#ifdef CONFIG_PM static int acm_suspend(struct usb_interface *intf, pm_message_t message) { struct acm *acm = usb_get_intfdata(intf); + int cnt; + + if (acm->dev->auto_pm) { + int b; + + spin_lock_irq(&acm->read_lock); + spin_lock(&acm->write_lock); + b = acm->processing + acm->transmitting; + spin_unlock(&acm->write_lock); + spin_unlock_irq(&acm->read_lock); + if (b) + return -EBUSY; + } - if (acm->susp_count++) + spin_lock_irq(&acm->read_lock); + spin_lock(&acm->write_lock); + cnt = acm->susp_count++; + spin_unlock(&acm->write_lock); + spin_unlock_irq(&acm->read_lock); + + if (cnt) return 0; /* we treat opened interfaces differently, @@ -1201,15 +1313,21 @@ static int acm_resume(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); int rv = 0; + int cnt; + + spin_lock_irq(&acm->read_lock); + acm->susp_count -= 1; + cnt = acm->susp_count; + spin_unlock_irq(&acm->read_lock); - if (--acm->susp_count) + if (cnt) return 0; mutex_lock(&acm->mutex); if (acm->used) { rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); if (rv < 0) - goto err_out; + goto err_out; tasklet_schedule(&acm->urb_task); } @@ -1218,6 +1336,8 @@ err_out: mutex_unlock(&acm->mutex); return rv; } + +#endif /* CONFIG_PM */ /* * USB driver structure. */ @@ -1248,6 +1368,9 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, /* control interfaces with various AT-command sets */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, @@ -1273,10 +1396,14 @@ static struct usb_driver acm_driver = { .name = "cdc_acm", .probe = acm_probe, .disconnect = acm_disconnect, +#ifdef CONFIG_PM .suspend = acm_suspend, .resume = acm_resume, +#endif .id_table = acm_ids, +#ifdef CONFIG_PM .supports_autosuspend = 1, +#endif }; /* @@ -1333,7 +1460,8 @@ static int __init acm_init(void) return retval; } - info(DRIVER_VERSION ":" DRIVER_DESC); + printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" + DRIVER_DESC "\n"); return 0; } diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 046e064b033..1f95e7aa1b6 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -106,11 +106,14 @@ struct acm { struct list_head spare_read_bufs; struct list_head filled_read_bufs; int write_used; /* number of non-empty write buffers */ - int write_ready; /* write urb is not running */ + int processing; + int transmitting; spinlock_t write_lock; struct mutex mutex; struct usb_cdc_line_coding line; /* bits, stop, parity */ struct work_struct work; /* work queue entry for line discipline waking up */ + struct work_struct waker; + wait_queue_head_t drain_wait; /* close processing */ struct tasklet_struct urb_task; /* rx processing */ spinlock_t throttle_lock; /* synchronize throtteling and read callback */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ @@ -123,6 +126,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 */ + struct acm_wb *delayed_wb; /* write queued for a device about to be woken */ }; #define CDC_DATA_INTERFACE_TYPE 0x0a diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 731db051070..5a8ecc045e3 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -28,8 +28,9 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.02" +#define DRIVER_VERSION "v0.03" #define DRIVER_AUTHOR "Oliver Neukum" +#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" static struct usb_device_id wdm_ids[] = { { @@ -41,6 +42,8 @@ static struct usb_device_id wdm_ids[] = { { } }; +MODULE_DEVICE_TABLE (usb, wdm_ids); + #define WDM_MINOR_BASE 176 @@ -87,6 +90,7 @@ struct wdm_device { dma_addr_t ihandle; struct mutex wlock; struct mutex rlock; + struct mutex plock; wait_queue_head_t wait; struct work_struct rxwork; int werr; @@ -130,10 +134,12 @@ static void wdm_in_callback(struct urb *urb) "nonzero urb status received: -ESHUTDOWN"); break; case -EPIPE: - err("nonzero urb status received: -EPIPE"); + dev_err(&desc->intf->dev, + "nonzero urb status received: -EPIPE\n"); break; default: - err("Unexpected error %d", status); + dev_err(&desc->intf->dev, + "Unexpected error %d\n", status); break; } } @@ -168,16 +174,18 @@ static void wdm_int_callback(struct urb *urb) return; /* unplug */ case -EPIPE: set_bit(WDM_INT_STALL, &desc->flags); - err("Stall on int endpoint"); + dev_err(&desc->intf->dev, "Stall on int endpoint\n"); goto sw; /* halt is cleared in work */ default: - err("nonzero urb status received: %d", status); + dev_err(&desc->intf->dev, + "nonzero urb status received: %d\n", status); break; } } if (urb->actual_length < sizeof(struct usb_cdc_notification)) { - err("wdm_int_callback - %d bytes", urb->actual_length); + dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n", + urb->actual_length); goto exit; } @@ -196,7 +204,8 @@ static void wdm_int_callback(struct urb *urb) goto exit; default: clear_bit(WDM_POLL_RUNNING, &desc->flags); - err("unknown notification %d received: index %d len %d", + dev_err(&desc->intf->dev, + "unknown notification %d received: index %d len %d\n", dr->bNotificationType, dr->wIndex, dr->wLength); goto exit; } @@ -205,7 +214,7 @@ static void wdm_int_callback(struct urb *urb) req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; req->wValue = 0; req->wIndex = desc->inum; - req->wLength = cpu_to_le16(desc->bMaxPacketSize0); + req->wLength = cpu_to_le16(desc->wMaxCommand); usb_fill_control_urb( desc->response, @@ -214,7 +223,7 @@ static void wdm_int_callback(struct urb *urb) usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0), (unsigned char *)req, desc->inbuf, - desc->bMaxPacketSize0, + desc->wMaxCommand, wdm_in_callback, desc ); @@ -234,19 +243,22 @@ static void wdm_int_callback(struct urb *urb) sw: rv = schedule_work(&desc->rxwork); if (rv) - err("Cannot schedule work"); + dev_err(&desc->intf->dev, + "Cannot schedule work\n"); } } exit: rv = usb_submit_urb(urb, GFP_ATOMIC); if (rv) - err("%s - usb_submit_urb failed with result %d", - __func__, rv); + dev_err(&desc->intf->dev, + "%s - usb_submit_urb failed with result %d\n", + __func__, rv); } static void kill_urbs(struct wdm_device *desc) { + /* the order here is essential */ usb_kill_urb(desc->command); usb_kill_urb(desc->validity); usb_kill_urb(desc->response); @@ -266,7 +278,7 @@ static void cleanup(struct wdm_device *desc) desc->sbuf, desc->validity->transfer_dma); usb_buffer_free(interface_to_usbdev(desc->intf), - desc->wMaxPacketSize, + desc->wMaxCommand, desc->inbuf, desc->response->transfer_dma); kfree(desc->orq); @@ -299,6 +311,9 @@ static ssize_t wdm_write if (r) goto outnl; + r = usb_autopm_get_interface(desc->intf); + if (r < 0) + goto outnp; r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); if (r < 0) @@ -347,11 +362,14 @@ static ssize_t wdm_write if (rv < 0) { kfree(buf); clear_bit(WDM_IN_USE, &desc->flags); + dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); } else { dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d", req->wIndex); } out: + usb_autopm_put_interface(desc->intf); +outnp: mutex_unlock(&desc->wlock); outnl: return rv < 0 ? rv : count; @@ -376,6 +394,11 @@ retry: rv = wait_event_interruptible(desc->wait, test_bit(WDM_READ, &desc->flags)); + if (test_bit(WDM_DISCONNECTING, &desc->flags)) { + rv = -ENODEV; + goto err; + } + usb_mark_last_busy(interface_to_usbdev(desc->intf)); if (rv < 0) { rv = -ERESTARTSYS; goto err; @@ -387,7 +410,8 @@ retry: int t = desc->rerr; desc->rerr = 0; spin_unlock_irq(&desc->iuspin); - err("reading had resulted in %d", t); + dev_err(&desc->intf->dev, + "reading had resulted in %d\n", t); rv = -EIO; goto err; } @@ -418,12 +442,15 @@ retry: desc->ubuf[i] = desc->ubuf[i + cntr]; desc->length -= cntr; + /* in case we had outstanding data */ + if (!desc->length) + clear_bit(WDM_READ, &desc->flags); rv = cntr; err: mutex_unlock(&desc->rlock); if (rv < 0) - err("wdm_read: exit error"); + dev_err(&desc->intf->dev, "wdm_read: exit error\n"); return rv; } @@ -433,7 +460,8 @@ static int wdm_flush(struct file *file, fl_owner_t id) wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); if (desc->werr < 0) - err("Error in flush path: %d", desc->werr); + dev_err(&desc->intf->dev, "Error in flush path: %d\n", + desc->werr); return desc->werr; } @@ -480,18 +508,29 @@ static int wdm_open(struct inode *inode, struct file *file) if (test_bit(WDM_DISCONNECTING, &desc->flags)) goto out; - desc->count++; + ; file->private_data = desc; - rv = usb_submit_urb(desc->validity, GFP_KERNEL); - + rv = usb_autopm_get_interface(desc->intf); if (rv < 0) { - desc->count--; - err("Error submitting int urb - %d", rv); + dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); goto out; } - rv = 0; + intf->needs_remote_wakeup = 1; + mutex_lock(&desc->plock); + if (!desc->count++) { + rv = usb_submit_urb(desc->validity, GFP_KERNEL); + if (rv < 0) { + desc->count--; + dev_err(&desc->intf->dev, + "Error submitting int urb - %d\n", rv); + } + } else { + rv = 0; + } + mutex_unlock(&desc->plock); + usb_autopm_put_interface(desc->intf); out: mutex_unlock(&wdm_mutex); return rv; @@ -502,10 +541,15 @@ static int wdm_release(struct inode *inode, struct file *file) struct wdm_device *desc = file->private_data; mutex_lock(&wdm_mutex); + mutex_lock(&desc->plock); desc->count--; + mutex_unlock(&desc->plock); + if (!desc->count) { dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); kill_urbs(desc); + if (!test_bit(WDM_DISCONNECTING, &desc->flags)) + desc->intf->needs_remote_wakeup = 0; } mutex_unlock(&wdm_mutex); return 0; @@ -568,7 +612,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) while (buflen > 0) { if (buffer [1] != USB_DT_CS_INTERFACE) { - err("skipping garbage"); + dev_err(&intf->dev, "skipping garbage\n"); goto next_desc; } @@ -582,7 +626,8 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) "Finding maximum buffer length: %d", maxcom); break; default: - err("Ignoring extra header, type %d, length %d", + dev_err(&intf->dev, + "Ignoring extra header, type %d, length %d\n", buffer[2], buffer[0]); break; } @@ -597,6 +642,7 @@ next_desc: goto out; mutex_init(&desc->wlock); mutex_init(&desc->rlock); + mutex_init(&desc->plock); spin_lock_init(&desc->iuspin); init_waitqueue_head(&desc->wait); desc->wMaxCommand = maxcom; @@ -698,6 +744,7 @@ static void wdm_disconnect(struct usb_interface *intf) spin_lock_irqsave(&desc->iuspin, flags); set_bit(WDM_DISCONNECTING, &desc->flags); set_bit(WDM_READ, &desc->flags); + /* to terminate pending flushes */ clear_bit(WDM_IN_USE, &desc->flags); spin_unlock_irqrestore(&desc->iuspin, flags); cancel_work_sync(&desc->rxwork); @@ -708,11 +755,82 @@ static void wdm_disconnect(struct usb_interface *intf) mutex_unlock(&wdm_mutex); } +static int wdm_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct wdm_device *desc = usb_get_intfdata(intf); + int rv = 0; + + dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); + + mutex_lock(&desc->plock); +#ifdef CONFIG_PM + if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) { + rv = -EBUSY; + } else { +#endif + cancel_work_sync(&desc->rxwork); + kill_urbs(desc); +#ifdef CONFIG_PM + } +#endif + mutex_unlock(&desc->plock); + + return rv; +} + +static int recover_from_urb_loss(struct wdm_device *desc) +{ + int rv = 0; + + if (desc->count) { + rv = usb_submit_urb(desc->validity, GFP_NOIO); + if (rv < 0) + dev_err(&desc->intf->dev, + "Error resume submitting int urb - %d\n", rv); + } + return rv; +} +static int wdm_resume(struct usb_interface *intf) +{ + struct wdm_device *desc = usb_get_intfdata(intf); + int rv; + + dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); + mutex_lock(&desc->plock); + rv = recover_from_urb_loss(desc); + mutex_unlock(&desc->plock); + return rv; +} + +static int wdm_pre_reset(struct usb_interface *intf) +{ + struct wdm_device *desc = usb_get_intfdata(intf); + + mutex_lock(&desc->plock); + return 0; +} + +static int wdm_post_reset(struct usb_interface *intf) +{ + struct wdm_device *desc = usb_get_intfdata(intf); + int rv; + + rv = recover_from_urb_loss(desc); + mutex_unlock(&desc->plock); + return 0; +} + static struct usb_driver wdm_driver = { .name = "cdc_wdm", .probe = wdm_probe, .disconnect = wdm_disconnect, + .suspend = wdm_suspend, + .resume = wdm_resume, + .reset_resume = wdm_resume, + .pre_reset = wdm_pre_reset, + .post_reset = wdm_post_reset, .id_table = wdm_ids, + .supports_autosuspend = 1, }; /* --- low level module stuff --- */ @@ -735,6 +853,5 @@ module_init(wdm_init); module_exit(wdm_exit); MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION("USB Abstract Control Model driver for " - "USB WCM Device Management"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 0647164d36d..b5775af3ba2 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -593,8 +593,9 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = usblp_hp_channel_change_request(usblp, arg, &newChannel); if (err < 0) { - err("usblp%d: error = %d setting " - "HP channel", + dev_err(&usblp->dev->dev, + "usblp%d: error = %d setting " + "HP channel\n", usblp->minor, err); retval = -EIO; goto done; @@ -1076,15 +1077,16 @@ static int usblp_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev (intf); - struct usblp *usblp = NULL; + struct usblp *usblp; int protocol; int retval; /* Malloc and start initializing usblp structure so we can use it * directly. */ - if (!(usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL))) { + usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL); + if (!usblp) { retval = -ENOMEM; - goto abort; + goto abort_ret; } usblp->dev = dev; mutex_init(&usblp->wmut); @@ -1179,12 +1181,11 @@ abort_intfdata: usb_set_intfdata (intf, NULL); device_remove_file(&intf->dev, &dev_attr_ieee1284_id); abort: - if (usblp) { - kfree(usblp->readbuf); - kfree(usblp->statusbuf); - kfree(usblp->device_id_string); - kfree(usblp); - } + kfree(usblp->readbuf); + kfree(usblp->statusbuf); + kfree(usblp->device_id_string); + kfree(usblp); +abort_ret: return retval; } @@ -1345,7 +1346,7 @@ static void usblp_disconnect(struct usb_interface *intf) usb_deregister_dev(intf, &usblp_class); if (!usblp || !usblp->dev) { - err("bogus disconnect"); + dev_err(&intf->dev, "bogus disconnect\n"); BUG (); } diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c new file mode 100644 index 00000000000..543811f6e6e --- /dev/null +++ b/drivers/usb/class/usbtmc.c @@ -0,0 +1,1087 @@ +/** + * drivers/usb/class/usbtmc.c - USB Test & Measurment class driver + * + * Copyright (C) 2007 Stefan Kopp, Gechingen, Germany + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de> + * + * 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. + * + * 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. + * + * The GNU General Public License is available at + * http://www.gnu.org/copyleft/gpl.html. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/kref.h> +#include <linux/mutex.h> +#include <linux/usb.h> +#include <linux/usb/tmc.h> + + +#define USBTMC_MINOR_BASE 176 + +/* + * Size of driver internal IO buffer. Must be multiple of 4 and at least as + * large as wMaxPacketSize (which is usually 512 bytes). + */ +#define USBTMC_SIZE_IOBUFFER 2048 + +/* Default USB timeout (in milliseconds) */ +#define USBTMC_TIMEOUT 10 + +/* + * Maximum number of read cycles to empty bulk in endpoint during CLEAR and + * ABORT_BULK_IN requests. Ends the loop if (for whatever reason) a short + * packet is never read. + */ +#define USBTMC_MAX_READS_TO_CLEAR_BULK_IN 100 + +static struct usb_device_id usbtmc_devices[] = { + { USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, 3, 0), }, + { 0, } /* terminating entry */ +}; + +/* + * This structure is the capabilities for the device + * See section 4.2.1.8 of the USBTMC specification for details. + */ +struct usbtmc_dev_capabilities { + __u8 interface_capabilities; + __u8 device_capabilities; + __u8 usb488_interface_capabilities; + __u8 usb488_device_capabilities; +}; + +/* This structure holds private data for each USBTMC device. One copy is + * allocated for each USBTMC device in the driver's probe function. + */ +struct usbtmc_device_data { + const struct usb_device_id *id; + struct usb_device *usb_dev; + struct usb_interface *intf; + + unsigned int bulk_in; + unsigned int bulk_out; + + u8 bTag; + u8 bTag_last_write; /* needed for abort */ + u8 bTag_last_read; /* needed for abort */ + + /* attributes from the USB TMC spec for this device */ + u8 TermChar; + bool TermCharEnabled; + bool auto_abort; + + struct usbtmc_dev_capabilities capabilities; + struct kref kref; + struct mutex io_mutex; /* only one i/o function running at a time */ +}; +#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref) + +/* Forward declarations */ +static struct usb_driver usbtmc_driver; + +static void usbtmc_delete(struct kref *kref) +{ + struct usbtmc_device_data *data = to_usbtmc_data(kref); + + usb_put_dev(data->usb_dev); + kfree(data); +} + +static int usbtmc_open(struct inode *inode, struct file *filp) +{ + struct usb_interface *intf; + struct usbtmc_device_data *data; + int retval = -ENODEV; + + intf = usb_find_interface(&usbtmc_driver, iminor(inode)); + if (!intf) { + printk(KERN_ERR KBUILD_MODNAME + ": can not find device for minor %d", iminor(inode)); + goto exit; + } + + data = usb_get_intfdata(intf); + kref_get(&data->kref); + + /* Store pointer in file structure's private data field */ + filp->private_data = data; + +exit: + return retval; +} + +static int usbtmc_release(struct inode *inode, struct file *file) +{ + struct usbtmc_device_data *data = file->private_data; + + kref_put(&data->kref, usbtmc_delete); + return 0; +} + +static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data) +{ + char *buffer; + struct device *dev; + int rv; + int n; + int actual; + struct usb_host_interface *current_setting; + int max_size; + + dev = &data->intf->dev; + buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_INITIATE_ABORT_BULK_IN, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, + data->bTag_last_read, data->bulk_in, + buffer, 2, USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]); + + if (buffer[0] == USBTMC_STATUS_FAILED) { + rv = 0; + goto exit; + } + + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", + buffer[0]); + rv = -EPERM; + goto exit; + } + + max_size = 0; + current_setting = data->intf->cur_altsetting; + for (n = 0; n < current_setting->desc.bNumEndpoints; n++) + if (current_setting->endpoint[n].desc.bEndpointAddress == + data->bulk_in) + max_size = le16_to_cpu(current_setting->endpoint[n]. + desc.wMaxPacketSize); + + if (max_size == 0) { + dev_err(dev, "Couldn't get wMaxPacketSize\n"); + rv = -EPERM; + goto exit; + } + + dev_dbg(&data->intf->dev, "wMaxPacketSize is %d\n", max_size); + + n = 0; + + do { + dev_dbg(dev, "Reading from bulk in EP\n"); + + rv = usb_bulk_msg(data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, + data->bulk_in), + buffer, USBTMC_SIZE_IOBUFFER, + &actual, USBTMC_TIMEOUT); + + n++; + + if (rv < 0) { + dev_err(dev, "usb_bulk_msg returned %d\n", rv); + goto exit; + } + } while ((actual == max_size) && + (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + + if (actual == max_size) { + dev_err(dev, "Couldn't clear device buffer within %d cycles\n", + USBTMC_MAX_READS_TO_CLEAR_BULK_IN); + rv = -EPERM; + goto exit; + } + + n = 0; + +usbtmc_abort_bulk_in_status: + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_CHECK_ABORT_BULK_IN_STATUS, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, + 0, data->bulk_in, buffer, 0x08, + USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]); + + if (buffer[0] == USBTMC_STATUS_SUCCESS) { + rv = 0; + goto exit; + } + + if (buffer[0] != USBTMC_STATUS_PENDING) { + dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]); + rv = -EPERM; + goto exit; + } + + if (buffer[1] == 1) + do { + dev_dbg(dev, "Reading from bulk in EP\n"); + + rv = usb_bulk_msg(data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, + data->bulk_in), + buffer, USBTMC_SIZE_IOBUFFER, + &actual, USBTMC_TIMEOUT); + + n++; + + if (rv < 0) { + dev_err(dev, "usb_bulk_msg returned %d\n", rv); + goto exit; + } + } while ((actual = max_size) && + (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + + if (actual == max_size) { + dev_err(dev, "Couldn't clear device buffer within %d cycles\n", + USBTMC_MAX_READS_TO_CLEAR_BULK_IN); + rv = -EPERM; + goto exit; + } + + goto usbtmc_abort_bulk_in_status; + +exit: + kfree(buffer); + return rv; + +} + +static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data) +{ + struct device *dev; + u8 *buffer; + int rv; + int n; + + dev = &data->intf->dev; + + buffer = kmalloc(8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_INITIATE_ABORT_BULK_OUT, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, + data->bTag_last_write, data->bulk_out, + buffer, 2, USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "INITIATE_ABORT_BULK_OUT returned %x\n", buffer[0]); + + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "INITIATE_ABORT_BULK_OUT returned %x\n", + buffer[0]); + rv = -EPERM; + goto exit; + } + + n = 0; + +usbtmc_abort_bulk_out_check_status: + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, + 0, data->bulk_out, buffer, 0x08, + USBTMC_TIMEOUT); + n++; + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "CHECK_ABORT_BULK_OUT returned %x\n", buffer[0]); + + if (buffer[0] == USBTMC_STATUS_SUCCESS) + goto usbtmc_abort_bulk_out_clear_halt; + + if ((buffer[0] == USBTMC_STATUS_PENDING) && + (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)) + goto usbtmc_abort_bulk_out_check_status; + + rv = -EPERM; + goto exit; + +usbtmc_abort_bulk_out_clear_halt: + rv = usb_control_msg(data->usb_dev, + usb_sndctrlpipe(data->usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, data->bulk_out, buffer, + 0, USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + rv = 0; + +exit: + kfree(buffer); + return rv; +} + +static ssize_t usbtmc_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + struct usbtmc_device_data *data; + struct device *dev; + unsigned long int n_characters; + u8 *buffer; + int actual; + int done; + int remaining; + int retval; + int this_part; + + /* Get pointer to private data structure */ + data = filp->private_data; + dev = &data->intf->dev; + + buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + mutex_lock(&data->io_mutex); + + remaining = count; + done = 0; + + while (remaining > 0) { + if (remaining > USBTMC_SIZE_IOBUFFER - 12 - 3) + this_part = USBTMC_SIZE_IOBUFFER - 12 - 3; + else + this_part = remaining; + + /* Setup IO buffer for DEV_DEP_MSG_IN message + * Refer to class specs for details + */ + buffer[0] = 2; + buffer[1] = data->bTag; + buffer[2] = ~(data->bTag); + buffer[3] = 0; /* Reserved */ + buffer[4] = (this_part - 12 - 3) & 255; + buffer[5] = ((this_part - 12 - 3) >> 8) & 255; + buffer[6] = ((this_part - 12 - 3) >> 16) & 255; + buffer[7] = ((this_part - 12 - 3) >> 24) & 255; + buffer[8] = data->TermCharEnabled * 2; + /* Use term character? */ + buffer[9] = data->TermChar; + buffer[10] = 0; /* Reserved */ + buffer[11] = 0; /* Reserved */ + + /* Send bulk URB */ + retval = usb_bulk_msg(data->usb_dev, + usb_sndbulkpipe(data->usb_dev, + data->bulk_out), + buffer, 12, &actual, USBTMC_TIMEOUT); + + /* Store bTag (in case we need to abort) */ + data->bTag_last_write = data->bTag; + + /* Increment bTag -- and increment again if zero */ + data->bTag++; + if (!data->bTag) + (data->bTag)++; + + if (retval < 0) { + dev_err(dev, "usb_bulk_msg returned %d\n", retval); + if (data->auto_abort) + usbtmc_ioctl_abort_bulk_out(data); + goto exit; + } + + /* Send bulk URB */ + retval = usb_bulk_msg(data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, + data->bulk_in), + buffer, USBTMC_SIZE_IOBUFFER, &actual, + USBTMC_TIMEOUT); + + /* Store bTag (in case we need to abort) */ + data->bTag_last_read = data->bTag; + + if (retval < 0) { + dev_err(dev, "Unable to read data, error %d\n", retval); + if (data->auto_abort) + usbtmc_ioctl_abort_bulk_in(data); + goto exit; + } + + /* How many characters did the instrument send? */ + n_characters = buffer[4] + + (buffer[5] << 8) + + (buffer[6] << 16) + + (buffer[7] << 24); + + /* Copy buffer to user space */ + if (copy_to_user(buf + done, &buffer[12], n_characters)) { + /* There must have been an addressing problem */ + retval = -EFAULT; + goto exit; + } + + done += n_characters; + if (n_characters < USBTMC_SIZE_IOBUFFER) + remaining = 0; + } + + /* Update file position value */ + *f_pos = *f_pos + done; + retval = done; + +exit: + mutex_unlock(&data->io_mutex); + kfree(buffer); + return retval; +} + +static ssize_t usbtmc_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct usbtmc_device_data *data; + u8 *buffer; + int retval; + int actual; + unsigned long int n_bytes; + int n; + int remaining; + int done; + int this_part; + + data = filp->private_data; + + buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + mutex_lock(&data->io_mutex); + + remaining = count; + done = 0; + + while (remaining > 0) { + if (remaining > USBTMC_SIZE_IOBUFFER - 12) { + this_part = USBTMC_SIZE_IOBUFFER - 12; + buffer[8] = 0; + } else { + this_part = remaining; + buffer[8] = 1; + } + + /* Setup IO buffer for DEV_DEP_MSG_OUT message */ + buffer[0] = 1; + buffer[1] = data->bTag; + buffer[2] = ~(data->bTag); + buffer[3] = 0; /* Reserved */ + buffer[4] = this_part & 255; + buffer[5] = (this_part >> 8) & 255; + buffer[6] = (this_part >> 16) & 255; + buffer[7] = (this_part >> 24) & 255; + /* buffer[8] is set above... */ + buffer[9] = 0; /* Reserved */ + buffer[10] = 0; /* Reserved */ + buffer[11] = 0; /* Reserved */ + + if (copy_from_user(&buffer[12], buf + done, this_part)) { + retval = -EFAULT; + goto exit; + } + + n_bytes = 12 + this_part; + if (this_part % 4) + n_bytes += 4 - this_part % 4; + for (n = 12 + this_part; n < n_bytes; n++) + buffer[n] = 0; + + retval = usb_bulk_msg(data->usb_dev, + usb_sndbulkpipe(data->usb_dev, + data->bulk_out), + buffer, n_bytes, &actual, USBTMC_TIMEOUT); + + data->bTag_last_write = data->bTag; + data->bTag++; + + if (!data->bTag) + data->bTag++; + + if (retval < 0) { + dev_err(&data->intf->dev, + "Unable to send data, error %d\n", retval); + if (data->auto_abort) + usbtmc_ioctl_abort_bulk_out(data); + goto exit; + } + + remaining -= this_part; + done += this_part; + } + + retval = count; +exit: + mutex_unlock(&data->io_mutex); + kfree(buffer); + return retval; +} + +static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) +{ + struct usb_host_interface *current_setting; + struct usb_endpoint_descriptor *desc; + struct device *dev; + u8 *buffer; + int rv; + int n; + int actual; + int max_size; + + dev = &data->intf->dev; + + dev_dbg(dev, "Sending INITIATE_CLEAR request\n"); + + buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_INITIATE_CLEAR, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, 0, buffer, 1, USBTMC_TIMEOUT); + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "INITIATE_CLEAR returned %x\n", buffer[0]); + + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "INITIATE_CLEAR returned %x\n", buffer[0]); + rv = -EPERM; + goto exit; + } + + max_size = 0; + current_setting = data->intf->cur_altsetting; + for (n = 0; n < current_setting->desc.bNumEndpoints; n++) { + desc = ¤t_setting->endpoint[n].desc; + if (desc->bEndpointAddress == data->bulk_in) + max_size = le16_to_cpu(desc->wMaxPacketSize); + } + + if (max_size == 0) { + dev_err(dev, "Couldn't get wMaxPacketSize\n"); + rv = -EPERM; + goto exit; + } + + dev_dbg(dev, "wMaxPacketSize is %d\n", max_size); + + n = 0; + +usbtmc_clear_check_status: + + dev_dbg(dev, "Sending CHECK_CLEAR_STATUS request\n"); + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_CHECK_CLEAR_STATUS, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, 0, buffer, 2, USBTMC_TIMEOUT); + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "CHECK_CLEAR_STATUS returned %x\n", buffer[0]); + + if (buffer[0] == USBTMC_STATUS_SUCCESS) + goto usbtmc_clear_bulk_out_halt; + + if (buffer[0] != USBTMC_STATUS_PENDING) { + dev_err(dev, "CHECK_CLEAR_STATUS returned %x\n", buffer[0]); + rv = -EPERM; + goto exit; + } + + if (buffer[1] == 1) + do { + dev_dbg(dev, "Reading from bulk in EP\n"); + + rv = usb_bulk_msg(data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, + data->bulk_in), + buffer, USBTMC_SIZE_IOBUFFER, + &actual, USBTMC_TIMEOUT); + n++; + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", + rv); + goto exit; + } + } while ((actual == max_size) && + (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + + if (actual == max_size) { + dev_err(dev, "Couldn't clear device buffer within %d cycles\n", + USBTMC_MAX_READS_TO_CLEAR_BULK_IN); + rv = -EPERM; + goto exit; + } + + goto usbtmc_clear_check_status; + +usbtmc_clear_bulk_out_halt: + + rv = usb_control_msg(data->usb_dev, + usb_sndctrlpipe(data->usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, + data->bulk_out, buffer, 0, + USBTMC_TIMEOUT); + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + rv = 0; + +exit: + kfree(buffer); + return rv; +} + +static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data) +{ + u8 *buffer; + int rv; + + buffer = kmalloc(2, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, + usb_sndctrlpipe(data->usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, data->bulk_out, + buffer, 0, USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n", + rv); + goto exit; + } + rv = 0; + +exit: + kfree(buffer); + return rv; +} + +static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data) +{ + u8 *buffer; + int rv; + + buffer = kmalloc(2, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, usb_sndctrlpipe(data->usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, data->bulk_in, buffer, 0, + USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n", + rv); + goto exit; + } + rv = 0; + +exit: + kfree(buffer); + return rv; +} + +static int get_capabilities(struct usbtmc_device_data *data) +{ + struct device *dev = &data->usb_dev->dev; + char *buffer; + int rv; + + buffer = kmalloc(0x18, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_GET_CAPABILITIES, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, 0, buffer, 0x18, USBTMC_TIMEOUT); + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + return rv; + } + + dev_dbg(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); + dev_dbg(dev, "Interface capabilities are %x\n", buffer[4]); + dev_dbg(dev, "Device capabilities are %x\n", buffer[5]); + dev_dbg(dev, "USB488 interface capabilities are %x\n", buffer[14]); + dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]); + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); + return -EPERM; + } + + data->capabilities.interface_capabilities = buffer[4]; + data->capabilities.device_capabilities = buffer[5]; + data->capabilities.usb488_interface_capabilities = buffer[14]; + data->capabilities.usb488_device_capabilities = buffer[15]; + + kfree(buffer); + return 0; +} + +#define capability_attribute(name) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface(dev); \ + struct usbtmc_device_data *data = usb_get_intfdata(intf); \ + \ + return sprintf(buf, "%d\n", data->capabilities.name); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) + +capability_attribute(interface_capabilities); +capability_attribute(device_capabilities); +capability_attribute(usb488_interface_capabilities); +capability_attribute(usb488_device_capabilities); + +static struct attribute *capability_attrs[] = { + &dev_attr_interface_capabilities.attr, + &dev_attr_device_capabilities.attr, + &dev_attr_usb488_interface_capabilities.attr, + &dev_attr_usb488_device_capabilities.attr, + NULL, +}; + +static struct attribute_group capability_attr_grp = { + .attrs = capability_attrs, +}; + +static ssize_t show_TermChar(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbtmc_device_data *data = usb_get_intfdata(intf); + + return sprintf(buf, "%c\n", data->TermChar); +} + +static ssize_t store_TermChar(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbtmc_device_data *data = usb_get_intfdata(intf); + + if (count < 1) + return -EINVAL; + data->TermChar = buf[0]; + return count; +} +static DEVICE_ATTR(TermChar, S_IRUGO, show_TermChar, store_TermChar); + +#define data_attribute(name) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface(dev); \ + struct usbtmc_device_data *data = usb_get_intfdata(intf); \ + \ + return sprintf(buf, "%d\n", data->name); \ +} \ +static ssize_t store_##name(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct usb_interface *intf = to_usb_interface(dev); \ + struct usbtmc_device_data *data = usb_get_intfdata(intf); \ + ssize_t result; \ + unsigned val; \ + \ + result = sscanf(buf, "%u\n", &val); \ + if (result != 1) \ + result = -EINVAL; \ + data->name = val; \ + if (result < 0) \ + return result; \ + else \ + return count; \ +} \ +static DEVICE_ATTR(name, S_IRUGO, show_##name, store_##name) + +data_attribute(TermCharEnabled); +data_attribute(auto_abort); + +static struct attribute *data_attrs[] = { + &dev_attr_TermChar.attr, + &dev_attr_TermCharEnabled.attr, + &dev_attr_auto_abort.attr, + NULL, +}; + +static struct attribute_group data_attr_grp = { + .attrs = data_attrs, +}; + +static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data) +{ + struct device *dev; + u8 *buffer; + int rv; + + dev = &data->intf->dev; + + buffer = kmalloc(2, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC_REQUEST_INDICATOR_PULSE, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, 0, buffer, 0x01, USBTMC_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "usb_control_msg returned %d\n", rv); + goto exit; + } + + dev_dbg(dev, "INDICATOR_PULSE returned %x\n", buffer[0]); + + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "INDICATOR_PULSE returned %x\n", buffer[0]); + rv = -EPERM; + goto exit; + } + rv = 0; + +exit: + kfree(buffer); + return rv; +} + +static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usbtmc_device_data *data; + int retval = -EBADRQC; + + data = file->private_data; + mutex_lock(&data->io_mutex); + + switch (cmd) { + case USBTMC_IOCTL_CLEAR_OUT_HALT: + retval = usbtmc_ioctl_clear_out_halt(data); + + case USBTMC_IOCTL_CLEAR_IN_HALT: + retval = usbtmc_ioctl_clear_in_halt(data); + + case USBTMC_IOCTL_INDICATOR_PULSE: + retval = usbtmc_ioctl_indicator_pulse(data); + + case USBTMC_IOCTL_CLEAR: + retval = usbtmc_ioctl_clear(data); + + case USBTMC_IOCTL_ABORT_BULK_OUT: + retval = usbtmc_ioctl_abort_bulk_out(data); + + case USBTMC_IOCTL_ABORT_BULK_IN: + retval = usbtmc_ioctl_abort_bulk_in(data); + } + + mutex_unlock(&data->io_mutex); + return retval; +} + +static struct file_operations fops = { + .owner = THIS_MODULE, + .read = usbtmc_read, + .write = usbtmc_write, + .open = usbtmc_open, + .release = usbtmc_release, + .unlocked_ioctl = usbtmc_ioctl, +}; + +static struct usb_class_driver usbtmc_class = { + .name = "usbtmc%d", + .fops = &fops, + .minor_base = USBTMC_MINOR_BASE, +}; + + +static int usbtmc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usbtmc_device_data *data; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int n; + int retcode; + + dev_dbg(&intf->dev, "%s called\n", __func__); + + data = kmalloc(sizeof(struct usbtmc_device_data), GFP_KERNEL); + if (!data) { + dev_err(&intf->dev, "Unable to allocate kernel memory\n"); + return -ENOMEM; + } + + data->intf = intf; + data->id = id; + data->usb_dev = usb_get_dev(interface_to_usbdev(intf)); + usb_set_intfdata(intf, data); + kref_init(&data->kref); + mutex_init(&data->io_mutex); + + /* Initialize USBTMC bTag and other fields */ + data->bTag = 1; + data->TermCharEnabled = 0; + data->TermChar = '\n'; + + /* USBTMC devices have only one setting, so use that */ + iface_desc = data->intf->cur_altsetting; + + /* Find bulk in endpoint */ + for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) { + endpoint = &iface_desc->endpoint[n].desc; + + if (usb_endpoint_is_bulk_in(endpoint)) { + data->bulk_in = endpoint->bEndpointAddress; + dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n", + data->bulk_in); + break; + } + } + + /* Find bulk out endpoint */ + for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) { + endpoint = &iface_desc->endpoint[n].desc; + + if (usb_endpoint_is_bulk_out(endpoint)) { + data->bulk_out = endpoint->bEndpointAddress; + dev_dbg(&intf->dev, "Found Bulk out endpoint at %u\n", + data->bulk_out); + break; + } + } + + retcode = get_capabilities(data); + if (retcode) + dev_err(&intf->dev, "can't read capabilities\n"); + else + retcode = sysfs_create_group(&intf->dev.kobj, + &capability_attr_grp); + + retcode = sysfs_create_group(&intf->dev.kobj, &data_attr_grp); + + retcode = usb_register_dev(intf, &usbtmc_class); + if (retcode) { + dev_err(&intf->dev, "Not able to get a minor" + " (base %u, slice default): %d\n", USBTMC_MINOR_BASE, + retcode); + goto error_register; + } + dev_dbg(&intf->dev, "Using minor number %d\n", intf->minor); + + return 0; + +error_register: + sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); + sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); + kref_put(&data->kref, usbtmc_delete); + return retcode; +} + +static void usbtmc_disconnect(struct usb_interface *intf) +{ + struct usbtmc_device_data *data; + + dev_dbg(&intf->dev, "usbtmc_disconnect called\n"); + + data = usb_get_intfdata(intf); + usb_deregister_dev(intf, &usbtmc_class); + sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); + sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); + kref_put(&data->kref, usbtmc_delete); +} + +static struct usb_driver usbtmc_driver = { + .name = "usbtmc", + .id_table = usbtmc_devices, + .probe = usbtmc_probe, + .disconnect = usbtmc_disconnect +}; + +static int __init usbtmc_init(void) +{ + int retcode; + + retcode = usb_register(&usbtmc_driver); + if (retcode) + printk(KERN_ERR KBUILD_MODNAME": Unable to register driver\n"); + return retcode; +} +module_init(usbtmc_init); + +static void __exit usbtmc_exit(void) +{ + usb_deregister(&usbtmc_driver); +} +module_exit(usbtmc_exit); + +MODULE_LICENSE("GPL"); |