diff options
Diffstat (limited to 'drivers/usb/class')
| -rw-r--r-- | drivers/usb/class/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/usb/class/Makefile | 1 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-acm.c | 1414 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-acm.h | 78 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-wdm.c | 592 | ||||
| -rw-r--r-- | drivers/usb/class/usblp.c | 274 | ||||
| -rw-r--r-- | drivers/usb/class/usbtmc.c | 2378 |
7 files changed, 3405 insertions, 1341 deletions
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig index bb8b73682a70..d3f5162bd67e 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # USB Class driver configuration # @@ -6,13 +7,13 @@ comment "USB Device Class drivers" config USB_ACM tristate "USB Modem (CDC ACM) support" depends on TTY - ---help--- + help This driver supports USB modems and ISDN adapters which support the Communication Device Class Abstract Control Model interface. - Please read <file:Documentation/usb/acm.txt> for details. + Please read <file:Documentation/usb/acm.rst> for details. If your modem only reports "Cls=ff(vend.)" in the descriptors in - /proc/bus/usb/devices, then your modem will not work with this + /sys/kernel/debug/usb/devices, then your modem will not work with this driver. To compile this driver as a module, choose M here: the @@ -29,7 +30,7 @@ config USB_PRINTER config USB_WDM tristate "USB Wireless Device Management support" - ---help--- + help This driver supports the WMC Device Management functionality of cell phones compliant to the CDC WMC specification. You can use AT commands over this device. diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile index 32e85277b5cf..5d393a28f7f2 100644 --- a/drivers/usb/class/Makefile +++ b/drivers/usb/class/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Makefile for USB Class drivers # (one step up from the misc category) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 9f49bfe4c6f4..54be4aa1dcb2 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * cdc-acm.c * @@ -12,40 +13,30 @@ * USB Abstract Control Model driver for USB modems and ISDN adapters * * Sponsored by SuSE - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #undef DEBUG #undef VERBOSE_DEBUG #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/log2.h> #include <linux/tty.h> #include <linux/serial.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> +#include <linux/tty_ldisc.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/cdc.h> #include <asm/byteorder.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> +#include <linux/idr.h> #include <linux/list.h> #include "cdc-acm.h" @@ -56,24 +47,27 @@ static struct usb_driver acm_driver; static struct tty_driver *acm_tty_driver; -static struct acm *acm_table[ACM_TTY_MINORS]; -static DEFINE_MUTEX(acm_table_lock); +static DEFINE_IDR(acm_minors); +static DEFINE_MUTEX(acm_minors_lock); + +static void acm_tty_set_termios(struct tty_struct *tty, + const struct ktermios *termios_old); /* - * acm_table accessors + * acm_minors accessors */ /* - * Look up an ACM structure by index. If found and not disconnected, increment + * Look up an ACM structure by minor. If found and not disconnected, increment * its refcount and return it with its mutex held. */ -static struct acm *acm_get_by_index(unsigned index) +static struct acm *acm_get_by_minor(unsigned int minor) { struct acm *acm; - mutex_lock(&acm_table_lock); - acm = acm_table[index]; + mutex_lock(&acm_minors_lock); + acm = idr_find(&acm_minors, minor); if (acm) { mutex_lock(&acm->mutex); if (acm->disconnected) { @@ -84,7 +78,7 @@ static struct acm *acm_get_by_index(unsigned index) mutex_unlock(&acm->mutex); } } - mutex_unlock(&acm_table_lock); + mutex_unlock(&acm_minors_lock); return acm; } @@ -95,14 +89,9 @@ static int acm_alloc_minor(struct acm *acm) { int minor; - mutex_lock(&acm_table_lock); - for (minor = 0; minor < ACM_TTY_MINORS; minor++) { - if (!acm_table[minor]) { - acm_table[minor] = acm; - break; - } - } - mutex_unlock(&acm_table_lock); + mutex_lock(&acm_minors_lock); + minor = idr_alloc(&acm_minors, acm, 0, ACM_TTY_MINORS, GFP_KERNEL); + mutex_unlock(&acm_minors_lock); return minor; } @@ -110,9 +99,9 @@ static int acm_alloc_minor(struct acm *acm) /* Release the minor number associated with 'acm'. */ static void acm_release_minor(struct acm *acm) { - mutex_lock(&acm_table_lock); - acm_table[acm->minor] = NULL; - mutex_unlock(&acm_table_lock); + mutex_lock(&acm_minors_lock); + idr_remove(&acm_minors, acm->minor); + mutex_unlock(&acm_minors_lock); } /* @@ -122,26 +111,66 @@ static void acm_release_minor(struct acm *acm) 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), + int retval; + + retval = usb_autopm_get_interface(acm->control); + if (retval) + return retval; + + 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); + buf, len, USB_CTRL_SET_TIMEOUT); + dev_dbg(&acm->control->dev, - "%s - rq 0x%02x, val %#x, len %#x, result %d\n", - __func__, request, value, len, retval); + "%s - rq 0x%02x, val %#x, len %#x, result %d\n", + __func__, request, value, len, retval); + + usb_autopm_put_interface(acm->control); + return retval < 0 ? retval : 0; } /* devices aren't required to support these requests. * the cdc acm descriptor tells whether they do... */ -#define acm_set_control(acm, control) \ - acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) +static inline int acm_set_control(struct acm *acm, int control) +{ + if (acm->quirks & QUIRK_CONTROL_LINE_STATE) + return -EOPNOTSUPP; + + return acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, + control, NULL, 0); +} + #define acm_set_line(acm, line) \ acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) #define acm_send_break(acm, ms) \ acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) +static void acm_poison_urbs(struct acm *acm) +{ + int i; + + usb_poison_urb(acm->ctrlurb); + for (i = 0; i < ACM_NW; i++) + usb_poison_urb(acm->wb[i].urb); + for (i = 0; i < acm->rx_buflimit; i++) + usb_poison_urb(acm->read_urbs[i]); +} + +static void acm_unpoison_urbs(struct acm *acm) +{ + int i; + + for (i = 0; i < acm->rx_buflimit; i++) + usb_unpoison_urb(acm->read_urbs[i]); + for (i = 0; i < ACM_NW; i++) + usb_unpoison_urb(acm->wb[i].urb); + usb_unpoison_urb(acm->ctrlurb); +} + + /* * Write buffer management. * All of these assume proper locks taken by the caller. @@ -157,7 +186,8 @@ static int acm_wb_alloc(struct acm *acm) for (;;) { wb = &acm->wb[wbn]; if (!wb->use) { - wb->use = 1; + wb->use = true; + wb->len = 0; return wbn; } wbn = (wbn + 1) % ACM_NW; @@ -174,7 +204,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++) - n -= acm->wb[i].use; + if(acm->wb[i].use) + n--; spin_unlock_irqrestore(&acm->write_lock, flags); return n; } @@ -184,7 +215,7 @@ static int acm_wb_is_avail(struct acm *acm) */ static void acm_write_done(struct acm *acm, struct acm_wb *wb) { - wb->use = 0; + wb->use = false; acm->transmitting--; usb_autopm_put_interface_async(acm->control); } @@ -208,9 +239,10 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb) rc = usb_submit_urb(wb->urb, GFP_ATOMIC); if (rc < 0) { - dev_err(&acm->data->dev, - "%s - usb_submit_urb(write bulk) failed: %d\n", - __func__, rc); + if (rc != -EPERM) + dev_err(&acm->data->dev, + "%s - usb_submit_urb(write bulk) failed: %d\n", + __func__, rc); acm_write_done(acm, wb); } return rc; @@ -219,7 +251,7 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb) /* * attributes exported through sysfs */ -static ssize_t show_caps +static ssize_t bmCapabilities_show (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -227,9 +259,9 @@ static ssize_t show_caps return sprintf(buf, "%d", acm->ctrl_caps); } -static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); +static DEVICE_ATTR_RO(bmCapabilities); -static ssize_t show_country_codes +static ssize_t wCountryCodes_show (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -239,9 +271,9 @@ static ssize_t show_country_codes return acm->country_code_size; } -static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); +static DEVICE_ATTR_RO(wCountryCodes); -static ssize_t show_country_rel_date +static ssize_t iCountryCodeRelDate_show (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -250,18 +282,98 @@ static ssize_t show_country_rel_date return sprintf(buf, "%d", acm->country_rel_date); } -static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); +static DEVICE_ATTR_RO(iCountryCodeRelDate); /* * Interrupt handlers for various ACM device responses */ +static void acm_process_notification(struct acm *acm, unsigned char *buf) +{ + int newctrl; + int difference; + unsigned long flags; + struct usb_cdc_notification *dr = (struct usb_cdc_notification *)buf; + unsigned char *data = buf + sizeof(struct usb_cdc_notification); + + switch (dr->bNotificationType) { + case USB_CDC_NOTIFY_NETWORK_CONNECTION: + dev_dbg(&acm->control->dev, + "%s - network connection: %d\n", __func__, dr->wValue); + break; + + case USB_CDC_NOTIFY_SERIAL_STATE: + if (le16_to_cpu(dr->wLength) != 2) { + dev_dbg(&acm->control->dev, + "%s - malformed serial state\n", __func__); + break; + } + + newctrl = get_unaligned_le16(data); + dev_dbg(&acm->control->dev, + "%s - serial state: 0x%x\n", __func__, newctrl); + + if (!acm->clocal && (acm->ctrlin & ~newctrl & USB_CDC_SERIAL_STATE_DCD)) { + dev_dbg(&acm->control->dev, + "%s - calling hangup\n", __func__); + tty_port_tty_hangup(&acm->port, false); + } + + difference = acm->ctrlin ^ newctrl; + + if ((difference & USB_CDC_SERIAL_STATE_DCD) && acm->port.tty) { + struct tty_ldisc *ld = tty_ldisc_ref(acm->port.tty); + if (ld) { + if (ld->ops->dcd_change) + ld->ops->dcd_change(acm->port.tty, newctrl & USB_CDC_SERIAL_STATE_DCD); + tty_ldisc_deref(ld); + } + } + + spin_lock_irqsave(&acm->read_lock, flags); + acm->ctrlin = newctrl; + acm->oldcount = acm->iocount; + + if (difference & USB_CDC_SERIAL_STATE_DSR) + acm->iocount.dsr++; + if (difference & USB_CDC_SERIAL_STATE_DCD) + acm->iocount.dcd++; + if (newctrl & USB_CDC_SERIAL_STATE_BREAK) { + acm->iocount.brk++; + tty_insert_flip_char(&acm->port, 0, TTY_BREAK); + } + if (newctrl & USB_CDC_SERIAL_STATE_RING_SIGNAL) + acm->iocount.rng++; + if (newctrl & USB_CDC_SERIAL_STATE_FRAMING) + acm->iocount.frame++; + if (newctrl & USB_CDC_SERIAL_STATE_PARITY) + acm->iocount.parity++; + if (newctrl & USB_CDC_SERIAL_STATE_OVERRUN) + acm->iocount.overrun++; + spin_unlock_irqrestore(&acm->read_lock, flags); + + if (newctrl & USB_CDC_SERIAL_STATE_BREAK) + tty_flip_buffer_push(&acm->port); + + if (difference) + wake_up_all(&acm->wioctl); + + break; + + default: + dev_dbg(&acm->control->dev, + "%s - unknown notification %d received: index %d len %d\n", + __func__, + dr->bNotificationType, dr->wIndex, dr->wLength); + } +} + /* control interface reports status changes with "interrupt" transfers */ static void acm_ctrl_irq(struct urb *urb) { struct acm *acm = urb->context; - struct usb_cdc_notification *dr = urb->transfer_buffer; - unsigned char *data; - int newctrl; + struct usb_cdc_notification *dr; + unsigned int current_size = urb->actual_length; + unsigned int expected_size, copy_size, alloc_size; int retval; int status = urb->status; @@ -274,63 +386,77 @@ static void acm_ctrl_irq(struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dev_dbg(&acm->control->dev, - "%s - urb shutting down with status: %d\n", - __func__, status); + "%s - urb shutting down with status: %d\n", + __func__, status); return; default: dev_dbg(&acm->control->dev, - "%s - nonzero urb status received: %d\n", - __func__, status); + "%s - nonzero urb status received: %d\n", + __func__, status); goto exit; } usb_mark_last_busy(acm->dev); - data = (unsigned char *)(dr + 1); - switch (dr->bNotificationType) { - case USB_CDC_NOTIFY_NETWORK_CONNECTION: - dev_dbg(&acm->control->dev, "%s - network connection: %d\n", - __func__, dr->wValue); - break; - - case USB_CDC_NOTIFY_SERIAL_STATE: - newctrl = get_unaligned_le16(data); + if (acm->nb_index == 0) { + /* + * The first chunk of a message must contain at least the + * notification header with the length field, otherwise we + * can't get an expected_size. + */ + if (current_size < sizeof(struct usb_cdc_notification)) { + dev_dbg(&acm->control->dev, "urb too short\n"); + goto exit; + } + dr = urb->transfer_buffer; + } else { + dr = (struct usb_cdc_notification *)acm->notification_buffer; + } + /* size = notification-header + (optional) data */ + expected_size = sizeof(struct usb_cdc_notification) + + le16_to_cpu(dr->wLength); + + if (acm->nb_index != 0 || current_size < expected_size) { + /* notification is transmitted fragmented, reassemble */ + if (acm->nb_size < expected_size) { + u8 *new_buffer; + alloc_size = roundup_pow_of_two(expected_size); + /* Final freeing is done on disconnect. */ + new_buffer = krealloc(acm->notification_buffer, + alloc_size, GFP_ATOMIC); + if (!new_buffer) { + acm->nb_index = 0; + goto exit; + } - if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { - dev_dbg(&acm->control->dev, "%s - calling hangup\n", - __func__); - tty_port_tty_hangup(&acm->port, false); + acm->notification_buffer = new_buffer; + acm->nb_size = alloc_size; + dr = (struct usb_cdc_notification *)acm->notification_buffer; } - acm->ctrlin = newctrl; + copy_size = min(current_size, + expected_size - acm->nb_index); - dev_dbg(&acm->control->dev, - "%s - input control lines: dcd%c dsr%c break%c " - "ring%c framing%c parity%c overrun%c\n", - __func__, - 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; + memcpy(&acm->notification_buffer[acm->nb_index], + urb->transfer_buffer, copy_size); + acm->nb_index += copy_size; + current_size = acm->nb_index; + } - default: - dev_dbg(&acm->control->dev, - "%s - unknown notification %d received: index %d " - "len %d data0 %d data1 %d\n", - __func__, - dr->bNotificationType, dr->wIndex, - dr->wLength, data[0], data[1]); - break; + if (current_size >= expected_size) { + /* notification complete */ + acm_process_notification(acm, (unsigned char *)dr); + acm->nb_index = 0; } + exit: retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n", - __func__, retval); + if (retval && retval != -EPERM && retval != -ENODEV) + dev_err(&acm->control->dev, + "%s - usb_submit_urb failed: %d\n", __func__, retval); + else + dev_vdbg(&acm->control->dev, + "control resubmission terminated %d\n", retval); } static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) @@ -340,17 +466,19 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) if (!test_and_clear_bit(index, &acm->read_urbs_free)) return 0; - dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index); - res = usb_submit_urb(acm->read_urbs[index], mem_flags); if (res) { - if (res != -EPERM) { + if (res != -EPERM && res != -ENODEV) { dev_err(&acm->data->dev, - "%s - usb_submit_urb failed: %d\n", - __func__, res); + "urb %d failed submission with %d\n", + index, res); + } else { + dev_vdbg(&acm->data->dev, "intended failure %d\n", res); } set_bit(index, &acm->read_urbs_free); return res; + } else { + dev_vdbg(&acm->data->dev, "submitted urb %d\n", index); } return 0; @@ -372,11 +500,16 @@ static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags) static void acm_process_read_urb(struct acm *acm, struct urb *urb) { + unsigned long flags; + if (!urb->actual_length) return; + spin_lock_irqsave(&acm->read_lock, flags); tty_insert_flip_string(&acm->port, urb->transfer_buffer, urb->actual_length); + spin_unlock_irqrestore(&acm->read_lock, flags); + tty_flip_buffer_push(&acm->port); } @@ -384,34 +517,73 @@ static void acm_read_bulk_callback(struct urb *urb) { struct acm_rb *rb = urb->context; struct acm *acm = rb->instance; - unsigned long flags; + int status = urb->status; + bool stopped = false; + bool stalled = false; + bool cooldown = false; + + dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n", + rb->index, urb->actual_length, status); + + switch (status) { + case 0: + usb_mark_last_busy(acm->dev); + acm_process_read_urb(acm, urb); + break; + case -EPIPE: + set_bit(EVENT_RX_STALL, &acm->flags); + stalled = true; + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&acm->data->dev, + "%s - urb shutting down with status: %d\n", + __func__, status); + stopped = true; + break; + case -EOVERFLOW: + case -EPROTO: + dev_dbg(&acm->data->dev, + "%s - cooling babbling device\n", __func__); + usb_mark_last_busy(acm->dev); + set_bit(rb->index, &acm->urbs_in_error_delay); + set_bit(ACM_ERROR_DELAY, &acm->flags); + cooldown = true; + break; + default: + dev_dbg(&acm->data->dev, + "%s - nonzero urb status received: %d\n", + __func__, status); + break; + } - dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, - rb->index, urb->actual_length); + /* + * Make sure URB processing is done before marking as free to avoid + * racing with unthrottle() on another CPU. Matches the barriers + * implied by the test_and_clear_bit() in acm_submit_read_urb(). + */ + smp_mb__before_atomic(); set_bit(rb->index, &acm->read_urbs_free); + /* + * Make sure URB is marked as free before checking the throttled flag + * to avoid racing with unthrottle() on another CPU. Matches the + * smp_mb() in unthrottle(). + */ + smp_mb__after_atomic(); - if (!acm->dev) { - dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); + if (stopped || stalled || cooldown) { + if (stalled) + schedule_delayed_work(&acm->dwork, 0); + else if (cooldown) + schedule_delayed_work(&acm->dwork, HZ / 2); return; } - usb_mark_last_busy(acm->dev); - if (urb->status) { - dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", - __func__, urb->status); + if (test_bit(ACM_THROTTLED, &acm->flags)) return; - } - acm_process_read_urb(acm, urb); - /* throttle device if requested by tty */ - spin_lock_irqsave(&acm->read_lock, flags); - acm->throttled = acm->throttle_req; - if (!acm->throttled && !acm->susp_count) { - spin_unlock_irqrestore(&acm->read_lock, flags); - acm_submit_read_urb(acm, rb->index, GFP_ATOMIC); - } else { - spin_unlock_irqrestore(&acm->read_lock, flags); - } + acm_submit_read_urb(acm, rb->index, GFP_ATOMIC); } /* data interface wrote those outgoing bytes */ @@ -420,27 +592,45 @@ static void acm_write_bulk(struct urb *urb) struct acm_wb *wb = urb->context; struct acm *acm = wb->instance; unsigned long flags; + int status = urb->status; - if (urb->status || (urb->actual_length != urb->transfer_buffer_length)) - dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n", - __func__, + if (status || (urb->actual_length != urb->transfer_buffer_length)) + dev_vdbg(&acm->data->dev, "wrote len %d/%d, status %d\n", urb->actual_length, urb->transfer_buffer_length, - urb->status); + status); spin_lock_irqsave(&acm->write_lock, flags); acm_write_done(acm, wb); spin_unlock_irqrestore(&acm->write_lock, flags); - schedule_work(&acm->work); + set_bit(EVENT_TTY_WAKEUP, &acm->flags); + schedule_delayed_work(&acm->dwork, 0); } static void acm_softint(struct work_struct *work) { - struct acm *acm = container_of(work, struct acm, work); + int i; + struct acm *acm = container_of(work, struct acm, dwork.work); + + if (test_bit(EVENT_RX_STALL, &acm->flags)) { + smp_mb(); /* against acm_suspend() */ + if (!acm->susp_count) { + for (i = 0; i < acm->rx_buflimit; i++) + usb_kill_urb(acm->read_urbs[i]); + usb_clear_halt(acm->dev, acm->in); + acm_submit_read_urbs(acm, GFP_KERNEL); + clear_bit(EVENT_RX_STALL, &acm->flags); + } + } - dev_vdbg(&acm->data->dev, "%s\n", __func__); + if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) { + for (i = 0; i < acm->rx_buflimit; i++) + if (test_and_clear_bit(i, &acm->urbs_in_error_delay)) + acm_submit_read_urb(acm, i, GFP_KERNEL); + } - tty_port_tty_wakeup(&acm->port); + if (test_and_clear_bit(EVENT_TTY_WAKEUP, &acm->flags)) + tty_port_tty_wakeup(&acm->port); } /* @@ -452,9 +642,7 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty) struct acm *acm; int retval; - dev_dbg(tty->dev, "%s\n", __func__); - - acm = acm_get_by_index(tty->index); + acm = acm_get_by_minor(tty->index); if (!acm) return -ENODEV; @@ -462,6 +650,13 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty) if (retval) goto error_init_termios; + /* + * Suppress initial echoing for some devices which might send data + * immediately after acm driver has been installed. + */ + if (acm->quirks & DISABLE_ECHO) + tty->termios.c_lflag &= ~ECHO; + tty->driver_data = acm; return 0; @@ -475,17 +670,34 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) { struct acm *acm = tty->driver_data; - dev_dbg(tty->dev, "%s\n", __func__); - return tty_port_open(&acm->port, tty, filp); } +static void acm_port_dtr_rts(struct tty_port *port, bool active) +{ + struct acm *acm = container_of(port, struct acm, port); + int val; + int res; + + if (active) + val = USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS; + else + val = 0; + + /* FIXME: add missing ctrlout locking throughout driver */ + acm->ctrlout = val; + + res = acm_set_control(acm, val); + if (res && (acm->ctrl_caps & USB_CDC_CAP_LINE)) + /* This is broken in too many devices to spam the logs */ + dev_dbg(&acm->control->dev, "failed to set dtr/rts\n"); +} + static int acm_port_activate(struct tty_port *port, struct tty_struct *tty) { struct acm *acm = container_of(port, struct acm, port); int retval = -ENODEV; - - dev_dbg(&acm->control->dev, "%s\n", __func__); + int i; mutex_lock(&acm->mutex); if (acm->disconnected) @@ -495,62 +707,53 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty) if (retval) goto error_get_interface; - /* - * FIXME: Why do we need this? Allocating 64K of physically contiguous - * memory is really nasty... - */ set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); acm->control->needs_remote_wakeup = 1; acm->ctrlurb->dev = acm->dev; - if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) { + retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL); + if (retval) { dev_err(&acm->control->dev, "%s - usb_submit_urb(ctrl irq) failed\n", __func__); goto error_submit_urb; } - acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS; - if (acm_set_control(acm, acm->ctrlout) < 0 && - (acm->ctrl_caps & USB_CDC_CAP_LINE)) - goto error_set_control; - - usb_autopm_put_interface(acm->control); + acm_tty_set_termios(tty, NULL); /* * Unthrottle device in case the TTY was closed while throttled. */ - spin_lock_irq(&acm->read_lock); - acm->throttled = 0; - acm->throttle_req = 0; - spin_unlock_irq(&acm->read_lock); + clear_bit(ACM_THROTTLED, &acm->flags); - if (acm_submit_read_urbs(acm, GFP_KERNEL)) + retval = acm_submit_read_urbs(acm, GFP_KERNEL); + if (retval) goto error_submit_read_urbs; + usb_autopm_put_interface(acm->control); + mutex_unlock(&acm->mutex); return 0; error_submit_read_urbs: - acm->ctrlout = 0; - acm_set_control(acm, acm->ctrlout); -error_set_control: + for (i = 0; i < acm->rx_buflimit; i++) + usb_kill_urb(acm->read_urbs[i]); usb_kill_urb(acm->ctrlurb); error_submit_urb: usb_autopm_put_interface(acm->control); error_get_interface: disconnected: mutex_unlock(&acm->mutex); - return retval; + + return usb_translate_errors(retval); } static void acm_port_destruct(struct tty_port *port) { struct acm *acm = container_of(port, struct acm, port); - dev_dbg(&acm->control->dev, "%s\n", __func__); - - acm_release_minor(acm); + if (acm->minor != ACM_MINOR_INVALID) + acm_release_minor(acm); usb_put_intf(acm->control); kfree(acm->country_codes); kfree(acm); @@ -559,48 +762,57 @@ static void acm_port_destruct(struct tty_port *port) static void acm_port_shutdown(struct tty_port *port) { struct acm *acm = container_of(port, struct acm, port); - int i; + struct urb *urb; + struct acm_wb *wb; - dev_dbg(&acm->control->dev, "%s\n", __func__); + /* + * Need to grab write_lock to prevent race with resume, but no need to + * hold it due to the tty-port initialised flag. + */ + acm_poison_urbs(acm); + spin_lock_irq(&acm->write_lock); + spin_unlock_irq(&acm->write_lock); - mutex_lock(&acm->mutex); - if (!acm->disconnected) { - usb_autopm_get_interface(acm->control); - acm_set_control(acm, acm->ctrlout = 0); - usb_kill_urb(acm->ctrlurb); - 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->read_urbs[i]); - acm->control->needs_remote_wakeup = 0; - usb_autopm_put_interface(acm->control); + usb_autopm_get_interface_no_resume(acm->control); + acm->control->needs_remote_wakeup = 0; + usb_autopm_put_interface(acm->control); + + for (;;) { + urb = usb_get_from_anchor(&acm->delayed); + if (!urb) + break; + wb = urb->context; + wb->use = false; + usb_autopm_put_interface_async(acm->control); } - mutex_unlock(&acm->mutex); + + acm_unpoison_urbs(acm); + } static void acm_tty_cleanup(struct tty_struct *tty) { struct acm *acm = tty->driver_data; - dev_dbg(&acm->control->dev, "%s\n", __func__); + tty_port_put(&acm->port); } static void acm_tty_hangup(struct tty_struct *tty) { struct acm *acm = tty->driver_data; - dev_dbg(&acm->control->dev, "%s\n", __func__); + tty_port_hangup(&acm->port); } static void acm_tty_close(struct tty_struct *tty, struct file *filp) { struct acm *acm = tty->driver_data; - dev_dbg(&acm->control->dev, "%s\n", __func__); + tty_port_close(&acm->port, tty, filp); } -static int acm_tty_write(struct tty_struct *tty, - const unsigned char *buf, int count) +static ssize_t acm_tty_write(struct tty_struct *tty, const u8 *buf, + size_t count) { struct acm *acm = tty->driver_data; int stat; @@ -611,7 +823,7 @@ static int acm_tty_write(struct tty_struct *tty, if (!count) return 0; - dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count); + dev_vdbg(&acm->data->dev, "%zu bytes from tty layer\n", count); spin_lock_irqsave(&acm->write_lock, flags); wbn = acm_wb_alloc(acm); @@ -622,26 +834,28 @@ static int acm_tty_write(struct tty_struct *tty, wb = &acm->wb[wbn]; if (!acm->dev) { - wb->use = 0; + wb->use = false; spin_unlock_irqrestore(&acm->write_lock, flags); return -ENODEV; } count = (count > acm->writesize) ? acm->writesize : count; - dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count); + dev_vdbg(&acm->data->dev, "writing %zu bytes\n", count); memcpy(wb->buf, buf, count); wb->len = count; - usb_autopm_get_interface_async(acm->control); + stat = usb_autopm_get_interface_async(acm->control); + if (stat) { + wb->use = false; + spin_unlock_irqrestore(&acm->write_lock, flags); + return stat; + } + if (acm->susp_count) { - if (!acm->delayed_wb) - acm->delayed_wb = wb; - else - usb_autopm_put_interface_async(acm->control); + usb_anchor_urb(wb->urb, &acm->delayed); spin_unlock_irqrestore(&acm->write_lock, flags); - return count; /* A white lie */ + return count; } - usb_mark_last_busy(acm->dev); stat = acm_start_wb(acm, wb); spin_unlock_irqrestore(&acm->write_lock, flags); @@ -651,7 +865,7 @@ static int acm_tty_write(struct tty_struct *tty, return count; } -static int acm_tty_write_room(struct tty_struct *tty) +static unsigned int acm_tty_write_room(struct tty_struct *tty) { struct acm *acm = tty->driver_data; /* @@ -661,7 +875,20 @@ static int acm_tty_write_room(struct tty_struct *tty) return acm_wb_is_avail(acm) ? acm->writesize : 0; } -static int acm_tty_chars_in_buffer(struct tty_struct *tty) +static void acm_tty_flush_buffer(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + unsigned long flags; + int i; + + spin_lock_irqsave(&acm->write_lock, flags); + for (i = 0; i < ACM_NW; i++) + if (acm->wb[i].use) + usb_unlink_urb(acm->wb[i].urb); + spin_unlock_irqrestore(&acm->write_lock, flags); +} + +static unsigned int acm_tty_chars_in_buffer(struct tty_struct *tty) { struct acm *acm = tty->driver_data; /* @@ -680,24 +907,19 @@ static void acm_tty_throttle(struct tty_struct *tty) { struct acm *acm = tty->driver_data; - spin_lock_irq(&acm->read_lock); - acm->throttle_req = 1; - spin_unlock_irq(&acm->read_lock); + set_bit(ACM_THROTTLED, &acm->flags); } static void acm_tty_unthrottle(struct tty_struct *tty) { struct acm *acm = tty->driver_data; - unsigned int was_throttled; - spin_lock_irq(&acm->read_lock); - was_throttled = acm->throttled; - acm->throttled = 0; - acm->throttle_req = 0; - spin_unlock_irq(&acm->read_lock); + clear_bit(ACM_THROTTLED, &acm->flags); + + /* Matches the smp_mb__after_atomic() in acm_read_bulk_callback(). */ + smp_mb(); - if (was_throttled) - acm_submit_read_urbs(acm, GFP_KERNEL); + acm_submit_read_urbs(acm, GFP_KERNEL); } static int acm_tty_break_ctl(struct tty_struct *tty, int state) @@ -705,10 +927,13 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state) struct acm *acm = tty->driver_data; int retval; + if (!(acm->ctrl_caps & USB_CDC_CAP_BRK)) + return -EOPNOTSUPP; + retval = acm_send_break(acm, state ? 0xffff : 0); if (retval < 0) - dev_dbg(&acm->control->dev, "%s - send break failed\n", - __func__); + dev_dbg(&acm->control->dev, + "%s - send break failed\n", __func__); return retval; } @@ -716,11 +941,11 @@ static int acm_tty_tiocmget(struct tty_struct *tty) { struct acm *acm = tty->driver_data; - return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | - (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | - (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | - (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | - (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | + return (acm->ctrlout & USB_CDC_CTRL_DTR ? TIOCM_DTR : 0) | + (acm->ctrlout & USB_CDC_CTRL_RTS ? TIOCM_RTS : 0) | + (acm->ctrlin & USB_CDC_SERIAL_STATE_DSR ? TIOCM_DSR : 0) | + (acm->ctrlin & USB_CDC_SERIAL_STATE_RING_SIGNAL ? TIOCM_RI : 0) | + (acm->ctrlin & USB_CDC_SERIAL_STATE_DCD ? TIOCM_CD : 0) | TIOCM_CTS; } @@ -731,10 +956,10 @@ static int acm_tty_tiocmset(struct tty_struct *tty, unsigned int newctrl; 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 ? USB_CDC_CTRL_DTR : 0) | + (set & TIOCM_RTS ? USB_CDC_CTRL_RTS : 0); + clear = (clear & TIOCM_DTR ? USB_CDC_CTRL_DTR : 0) | + (clear & TIOCM_RTS ? USB_CDC_CTRL_RTS : 0); newctrl = (newctrl & ~clear) | set; @@ -743,41 +968,30 @@ static int acm_tty_tiocmset(struct tty_struct *tty, return acm_set_control(acm, acm->ctrlout = newctrl); } -static int get_serial_info(struct acm *acm, struct serial_struct __user *info) +static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss) { - struct serial_struct tmp; - - if (!info) - return -EINVAL; + struct acm *acm = tty->driver_data; - memset(&tmp, 0, sizeof(tmp)); - tmp.flags = ASYNC_LOW_LATENCY; - tmp.xmit_fifo_size = acm->writesize; - tmp.baud_base = le32_to_cpu(acm->line.dwDTERate); - tmp.close_delay = acm->port.close_delay / 10; - tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ss->line = acm->minor; + mutex_lock(&acm->port.mutex); + ss->close_delay = jiffies_to_msecs(acm->port.close_delay) / 10; + ss->closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : - acm->port.closing_wait / 10; - - if (copy_to_user(info, &tmp, sizeof(tmp))) - return -EFAULT; - else - return 0; + jiffies_to_msecs(acm->port.closing_wait) / 10; + mutex_unlock(&acm->port.mutex); + return 0; } -static int set_serial_info(struct acm *acm, - struct serial_struct __user *newinfo) +static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) { - struct serial_struct new_serial; + struct acm *acm = tty->driver_data; unsigned int closing_wait, close_delay; int retval = 0; - if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) - return -EFAULT; - - close_delay = new_serial.close_delay * 10; - closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; + close_delay = msecs_to_jiffies(ss->close_delay * 10); + closing_wait = ss->closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : + msecs_to_jiffies(ss->closing_wait * 10); mutex_lock(&acm->port.mutex); @@ -785,8 +999,6 @@ static int set_serial_info(struct acm *acm, if ((close_delay != acm->port.close_delay) || (closing_wait != acm->port.closing_wait)) retval = -EPERM; - else - retval = -EOPNOTSUPP; } else { acm->port.close_delay = close_delay; acm->port.closing_wait = closing_wait; @@ -796,6 +1008,65 @@ static int set_serial_info(struct acm *acm, return retval; } +static int wait_serial_change(struct acm *acm, unsigned long arg) +{ + int rv = 0; + DECLARE_WAITQUEUE(wait, current); + struct async_icount old, new; + + do { + spin_lock_irq(&acm->read_lock); + old = acm->oldcount; + new = acm->iocount; + acm->oldcount = new; + spin_unlock_irq(&acm->read_lock); + + if ((arg & TIOCM_DSR) && + old.dsr != new.dsr) + break; + if ((arg & TIOCM_CD) && + old.dcd != new.dcd) + break; + if ((arg & TIOCM_RI) && + old.rng != new.rng) + break; + + add_wait_queue(&acm->wioctl, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + remove_wait_queue(&acm->wioctl, &wait); + if (acm->disconnected) { + if (arg & TIOCM_CD) + break; + else + rv = -ENODEV; + } else { + if (signal_pending(current)) + rv = -ERESTARTSYS; + } + } while (!rv); + + + + return rv; +} + +static int acm_tty_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct acm *acm = tty->driver_data; + + icount->dsr = acm->iocount.dsr; + icount->rng = acm->iocount.rng; + icount->dcd = acm->iocount.dcd; + icount->frame = acm->iocount.frame; + icount->overrun = acm->iocount.overrun; + icount->parity = acm->iocount.parity; + icount->brk = acm->iocount.brk; + + return 0; +} + static int acm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -803,11 +1074,14 @@ static int acm_tty_ioctl(struct tty_struct *tty, int rv = -ENOIOCTLCMD; switch (cmd) { - case TIOCGSERIAL: /* gets serial port data */ - rv = get_serial_info(acm, (struct serial_struct __user *) arg); - break; - case TIOCSSERIAL: - rv = set_serial_info(acm, (struct serial_struct __user *) arg); + case TIOCMIWAIT: + rv = usb_autopm_get_interface(acm->control); + if (rv < 0) { + rv = -EIO; + break; + } + rv = wait_serial_change(acm, arg); + usb_autopm_put_interface(acm->control); break; } @@ -815,7 +1089,7 @@ static int acm_tty_ioctl(struct tty_struct *tty, } static void acm_tty_set_termios(struct tty_struct *tty, - struct ktermios *termios_old) + const struct ktermios *termios_old) { struct acm *acm = tty->driver_data; struct ktermios *termios = &tty->termios; @@ -827,29 +1101,17 @@ static void acm_tty_set_termios(struct tty_struct *tty, newline.bParityType = termios->c_cflag & PARENB ? (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; - switch (termios->c_cflag & CSIZE) { - case CS5: - newline.bDataBits = 5; - break; - case CS6: - newline.bDataBits = 6; - break; - case CS7: - newline.bDataBits = 7; - break; - case CS8: - default: - newline.bDataBits = 8; - break; - } + newline.bDataBits = tty_get_char_size(termios->c_cflag); + /* FIXME: Needs to clear unsupported bits in the termios */ acm->clocal = ((termios->c_cflag & CLOCAL) != 0); - if (!newline.dwDTERate) { + if (C_BAUD(tty) == B0) { newline.dwDTERate = acm->line.dwDTERate; - newctrl &= ~ACM_CTRL_DTR; - } else - newctrl |= ACM_CTRL_DTR; + newctrl &= ~USB_CDC_CTRL_DTR; + } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { + newctrl |= USB_CDC_CTRL_DTR; + } if (newctrl != acm->ctrlout) acm_set_control(acm, acm->ctrlout = newctrl); @@ -866,6 +1128,7 @@ static void acm_tty_set_termios(struct tty_struct *tty, } static const struct tty_port_operations acm_port_ops = { + .dtr_rts = acm_port_dtr_rts, .shutdown = acm_port_shutdown, .activate = acm_port_activate, .destruct = acm_port_destruct, @@ -880,19 +1143,17 @@ 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_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah); + usb_free_coherent(acm->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; for (i = 0; i < acm->rx_buflimit; i++) - usb_free_coherent(usb_dev, acm->readsize, + usb_free_coherent(acm->dev, acm->readsize, acm->read_buffers[i].base, acm->read_buffers[i].dma); } @@ -922,7 +1183,7 @@ 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; + struct usb_cdc_call_mgmt_descriptor *cmgmd = NULL; unsigned char *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; struct usb_interface *control_interface; @@ -931,20 +1192,20 @@ static int acm_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *epread = NULL; struct usb_endpoint_descriptor *epwrite = NULL; struct usb_device *usb_dev = interface_to_usbdev(intf); + struct usb_cdc_parsed_header h; struct acm *acm; int minor; int ctrlsize, readsize; u8 *buf; - u8 ac_management_function = 0; - u8 call_management_function = 0; - int call_interface_num = -1; - int data_interface_num = -1; + int call_intf_num = -1; + int data_intf_num = -1; unsigned long quirks; int num_rx_buf; int i; int combined_interfaces = 0; struct device *tty_dev; int rv = -ENOMEM; + int res; /* normal quirks */ quirks = (unsigned long)id->driver_info; @@ -952,12 +1213,17 @@ static int acm_probe(struct usb_interface *intf, if (quirks == IGNORE_DEVICE) return -ENODEV; + memset(&h, 0x00, sizeof(struct usb_cdc_parsed_header)); + num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; /* handle quirks deadly to normal probing*/ if (quirks == NO_UNION_NORMAL) { data_interface = usb_ifnum_to_if(usb_dev, 1); control_interface = usb_ifnum_to_if(usb_dev, 0); + /* we would crash */ + if (!data_interface || !control_interface) + return -ENODEV; goto skip_normal_probe; } @@ -982,79 +1248,51 @@ static int acm_probe(struct usb_interface *intf, } } - while (buflen > 0) { - 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 ((quirks & NOT_A_MODEM) == 0 && (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]; - } + cdc_parse_cdc_header(&h, intf, buffer, buflen); + union_header = h.usb_cdc_union_desc; + cmgmd = h.usb_cdc_call_mgmt_descriptor; + if (cmgmd) + call_intf_num = cmgmd->bDataInterface; if (!union_header) { - if (call_interface_num > 0) { + if (intf->cur_altsetting->desc.bNumEndpoints == 3) { + dev_dbg(&intf->dev, "No union descriptor, assuming single interface\n"); + combined_interfaces = 1; + control_interface = data_interface = intf; + goto look_for_collapsed_interface; + } else if (call_intf_num > 0) { dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); - /* quirks for Droids MuIn LCD */ - if (quirks & NO_DATA_INTERFACE) - data_interface = usb_ifnum_to_if(usb_dev, 0); - else - data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); + data_intf_num = call_intf_num; + data_interface = usb_ifnum_to_if(usb_dev, data_intf_num); control_interface = intf; } else { - 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; - } + dev_dbg(&intf->dev, "No union descriptor, giving up\n"); + return -ENODEV; } } else { + int class = -1; + + data_intf_num = union_header->bSlaveInterface0; 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"); - return -ENODEV; + data_interface = usb_ifnum_to_if(usb_dev, data_intf_num); + + if (control_interface) + class = control_interface->cur_altsetting->desc.bInterfaceClass; + + if (class != USB_CLASS_COMM && class != USB_CLASS_CDC_DATA) { + dev_dbg(&intf->dev, "Broken union descriptor, assuming single interface\n"); + combined_interfaces = 1; + control_interface = data_interface = intf; + goto look_for_collapsed_interface; } } - if (data_interface_num != call_interface_num) + if (!control_interface || !data_interface) { + dev_dbg(&intf->dev, "no interfaces\n"); + return -ENODEV; + } + + if (data_intf_num != call_intf_num) dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); if (control_interface == data_interface) { @@ -1068,38 +1306,22 @@ next_desc: 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; + res = usb_find_common_endpoints(data_interface->cur_altsetting, + &epread, &epwrite, &epctrl, NULL); + if (res) + return res; + + goto made_compressed_probe; } 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) { - struct usb_interface *t; + if (data_interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA) { + if (control_interface->cur_altsetting->desc.bInterfaceClass == USB_CLASS_CDC_DATA) { dev_dbg(&intf->dev, "Your device has switched interfaces.\n"); - t = control_interface; - control_interface = data_interface; - data_interface = t; + swap(control_interface, data_interface); } else { return -EINVAL; } @@ -1109,13 +1331,6 @@ skip_normal_probe: if (!combined_interfaces && intf != control_interface) return -ENODEV; - 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; - } - - if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 || control_interface->cur_altsetting->desc.bNumEndpoints == 0) return -EINVAL; @@ -1128,28 +1343,19 @@ skip_normal_probe: /* workaround for switched endpoints */ 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"); - t = epread; - epread = epwrite; - epwrite = t; + swap(epread, epwrite); } made_compressed_probe: dev_dbg(&intf->dev, "interfaces are valid\n"); acm = kzalloc(sizeof(struct acm), GFP_KERNEL); - if (acm == NULL) { - dev_err(&intf->dev, "out of memory (acm kzalloc)\n"); - goto alloc_fail; - } + if (!acm) + return -ENOMEM; - minor = acm_alloc_minor(acm); - if (minor == ACM_TTY_MINORS) { - dev_err(&intf->dev, "no more free acm devices\n"); - kfree(acm); - return -ENODEV; - } + tty_port_init(&acm->port); + acm->port.ops = &acm_port_ops; ctrlsize = usb_endpoint_maxp(epctrl); readsize = usb_endpoint_maxp(epread) * @@ -1158,78 +1364,80 @@ made_compressed_probe: acm->writesize = usb_endpoint_maxp(epwrite) * 20; acm->control = control_interface; acm->data = data_interface; + + usb_get_intf(acm->control); /* undone in destruct() */ + + minor = acm_alloc_minor(acm); + if (minor < 0) { + acm->minor = ACM_MINOR_INVALID; + goto err_put_port; + } + acm->minor = minor; acm->dev = usb_dev; - acm->ctrl_caps = ac_management_function; + if (h.usb_cdc_acm_descriptor) + acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities; if (quirks & NO_CAP_LINE) acm->ctrl_caps &= ~USB_CDC_CAP_LINE; acm->ctrlsize = ctrlsize; acm->readsize = readsize; acm->rx_buflimit = num_rx_buf; - INIT_WORK(&acm->work, acm_softint); + INIT_DELAYED_WORK(&acm->dwork, acm_softint); + init_waitqueue_head(&acm->wioctl); spin_lock_init(&acm->write_lock); spin_lock_init(&acm->read_lock); mutex_init(&acm->mutex); - acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); - acm->is_int_ep = usb_endpoint_xfer_int(epread); - if (acm->is_int_ep) + if (usb_endpoint_xfer_int(epread)) { acm->bInterval = epread->bInterval; - tty_port_init(&acm->port); - acm->port.ops = &acm_port_ops; + acm->in = usb_rcvintpipe(usb_dev, epread->bEndpointAddress); + } else { + acm->in = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); + } + if (usb_endpoint_xfer_int(epwrite)) + acm->out = usb_sndintpipe(usb_dev, epwrite->bEndpointAddress); + else + acm->out = usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress); + init_usb_anchor(&acm->delayed); + acm->quirks = quirks; buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); - if (!buf) { - dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n"); - goto alloc_fail2; - } + if (!buf) + goto err_put_port; acm->ctrl_buffer = buf; - if (acm_write_buffers_alloc(acm) < 0) { - dev_err(&intf->dev, "out of memory (write buffer alloc)\n"); - goto alloc_fail4; - } + if (acm_write_buffers_alloc(acm) < 0) + goto err_free_ctrl_buffer; acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); - if (!acm->ctrlurb) { - dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); - goto alloc_fail5; - } + if (!acm->ctrlurb) + goto err_free_write_buffers; + for (i = 0; i < num_rx_buf; i++) { struct acm_rb *rb = &(acm->read_buffers[i]); struct urb *urb; rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL, &rb->dma); - if (!rb->base) { - dev_err(&intf->dev, "out of memory " - "(read bufs usb_alloc_coherent)\n"); - goto alloc_fail6; - } + if (!rb->base) + goto err_free_read_urbs; rb->index = i; rb->instance = acm; urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - dev_err(&intf->dev, - "out of memory (read urbs usb_alloc_urb)\n"); - goto alloc_fail6; - } + if (!urb) + goto err_free_read_urbs; + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = rb->dma; - if (acm->is_int_ep) { - usb_fill_int_urb(urb, acm->dev, - acm->rx_endpoint, - rb->base, + if (usb_endpoint_xfer_int(epread)) + usb_fill_int_urb(urb, acm->dev, acm->in, rb->base, acm->readsize, acm_read_bulk_callback, rb, acm->bInterval); - } else { - usb_fill_bulk_urb(urb, acm->dev, - acm->rx_endpoint, - rb->base, + else + usb_fill_bulk_urb(urb, acm->dev, acm->in, rb->base, acm->readsize, acm_read_bulk_callback, rb); - } acm->read_urbs[i] = urb; __set_bit(i, &acm->read_urbs_free); @@ -1238,21 +1446,18 @@ made_compressed_probe: struct acm_wb *snd = &(acm->wb[i]); snd->urb = usb_alloc_urb(0, GFP_KERNEL); - if (snd->urb == NULL) { - dev_err(&intf->dev, - "out of memory (write urbs usb_alloc_urb)\n"); - goto alloc_fail7; - } + if (!snd->urb) + goto err_free_write_urbs; if (usb_endpoint_xfer_int(epwrite)) - usb_fill_int_urb(snd->urb, usb_dev, - usb_sndintpipe(usb_dev, epwrite->bEndpointAddress), + usb_fill_int_urb(snd->urb, usb_dev, acm->out, NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval); else - usb_fill_bulk_urb(snd->urb, usb_dev, - usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), + usb_fill_bulk_urb(snd->urb, usb_dev, acm->out, NULL, acm->writesize, acm_write_bulk, snd); snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + if (quirks & SEND_ZERO_PACKET) + snd->urb->transfer_flags |= URB_ZERO_PACKET; snd->instance = acm; } @@ -1260,14 +1465,17 @@ made_compressed_probe: i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); if (i < 0) - goto alloc_fail7; + goto err_free_write_urbs; + + if (h.usb_cdc_country_functional_desc) { /* export the country data */ + struct usb_cdc_country_functional_desc * cfd = + h.usb_cdc_country_functional_desc; - if (cfd) { /* export the country data */ acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); if (!acm->country_codes) goto skip_countries; acm->country_code_size = cfd->bLength - 4; - memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, + memcpy(acm->country_codes, cfd->wCountryCodes, cfd->bLength - 4); acm->country_rel_date = cfd->iCountryCodeRelDate; @@ -1295,31 +1503,47 @@ skip_countries: usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, /* works around buggy devices */ - epctrl->bInterval ? epctrl->bInterval : 0xff); + epctrl->bInterval ? epctrl->bInterval : 16); acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; acm->ctrlurb->transfer_dma = acm->ctrl_dma; - - dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); - - acm_set_control(acm, acm->ctrlout); + acm->notification_buffer = NULL; + acm->nb_index = 0; + acm->nb_size = 0; acm->line.dwDTERate = cpu_to_le32(9600); acm->line.bDataBits = 8; acm_set_line(acm, &acm->line); - usb_driver_claim_interface(&acm_driver, data_interface, acm); - usb_set_intfdata(data_interface, acm); + if (!acm->combined_interfaces) { + rv = usb_driver_claim_interface(&acm_driver, data_interface, acm); + if (rv) + goto err_remove_files; + } + + if (quirks & CLEAR_HALT_CONDITIONS) { + /* errors intentionally ignored */ + usb_clear_halt(usb_dev, acm->in); + usb_clear_halt(usb_dev, acm->out); + } - usb_get_intf(control_interface); tty_dev = tty_port_register_device(&acm->port, acm_tty_driver, minor, &control_interface->dev); if (IS_ERR(tty_dev)) { rv = PTR_ERR(tty_dev); - goto alloc_fail8; + goto err_release_data_interface; } + dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); + return 0; -alloc_fail8: + +err_release_data_interface: + if (!acm->combined_interfaces) { + /* Clear driver data so that disconnect() returns early. */ + usb_set_intfdata(data_interface, NULL); + usb_driver_release_interface(&acm_driver, data_interface); + } +err_remove_files: if (acm->country_codes) { device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); @@ -1327,74 +1551,56 @@ alloc_fail8: &dev_attr_iCountryCodeRelDate); } device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); -alloc_fail7: - usb_set_intfdata(intf, NULL); +err_free_write_urbs: for (i = 0; i < ACM_NW; i++) usb_free_urb(acm->wb[i].urb); -alloc_fail6: +err_free_read_urbs: for (i = 0; i < num_rx_buf; i++) usb_free_urb(acm->read_urbs[i]); acm_read_buffers_free(acm); usb_free_urb(acm->ctrlurb); -alloc_fail5: +err_free_write_buffers: acm_write_buffers_free(acm); -alloc_fail4: +err_free_ctrl_buffer: usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); -alloc_fail2: - acm_release_minor(acm); - kfree(acm); -alloc_fail: - return rv; -} - -static void stop_data_traffic(struct acm *acm) -{ - int i; - - dev_dbg(&acm->control->dev, "%s\n", __func__); - - usb_kill_urb(acm->ctrlurb); - 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->read_urbs[i]); +err_put_port: + tty_port_put(&acm->port); - cancel_work_sync(&acm->work); + return rv; } 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; int i; - dev_dbg(&intf->dev, "%s\n", __func__); - /* sibling interface is already cleaning up */ if (!acm) return; - mutex_lock(&acm->mutex); acm->disconnected = true; + /* + * there is a circular dependency. acm_softint() can resubmit + * the URBs in error handling so we need to block any + * submission right away + */ + acm_poison_urbs(acm); + mutex_lock(&acm->mutex); if (acm->country_codes) { device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); device_remove_file(&acm->control->dev, &dev_attr_iCountryCodeRelDate); } + wake_up_all(&acm->wioctl); device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); usb_set_intfdata(acm->control, NULL); usb_set_intfdata(acm->data, NULL); mutex_unlock(&acm->mutex); - tty = tty_port_tty_get(&acm->port); - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } + tty_port_tty_vhangup(&acm->port); - stop_data_traffic(acm); + cancel_delayed_work_sync(&acm->dwork); tty_unregister_device(acm_tty_driver, acm->minor); @@ -1404,9 +1610,11 @@ static void acm_disconnect(struct usb_interface *intf) for (i = 0; i < acm->rx_buflimit; i++) usb_free_urb(acm->read_urbs[i]); acm_write_buffers_free(acm); - usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); + usb_free_coherent(acm->dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); acm_read_buffers_free(acm); + kfree(acm->notification_buffer); + if (!acm->combined_interfaces) usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : acm->control); @@ -1420,27 +1628,22 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message) struct acm *acm = usb_get_intfdata(intf); int cnt; + spin_lock_irq(&acm->write_lock); if (PMSG_IS_AUTO(message)) { - int b; - - spin_lock_irq(&acm->write_lock); - b = acm->transmitting; - spin_unlock_irq(&acm->write_lock); - if (b) + if (acm->transmitting) { + spin_unlock_irq(&acm->write_lock); return -EBUSY; + } } - - 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); + spin_unlock_irq(&acm->write_lock); if (cnt) return 0; - if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) - stop_data_traffic(acm); + acm_poison_urbs(acm); + cancel_delayed_work_sync(&acm->dwork); + acm->urbs_in_error_delay = 0; return 0; } @@ -1448,29 +1651,25 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message) static int acm_resume(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); - struct acm_wb *wb; + struct urb *urb; 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); + spin_lock_irq(&acm->write_lock); - if (cnt) - return 0; + if (--acm->susp_count) + goto out; - if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) { - rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); + acm_unpoison_urbs(acm); - spin_lock_irq(&acm->write_lock); - if (acm->delayed_wb) { - wb = acm->delayed_wb; - acm->delayed_wb = NULL; - spin_unlock_irq(&acm->write_lock); - acm_start_wb(acm, wb); - } else { - spin_unlock_irq(&acm->write_lock); + if (tty_port_initialized(&acm->port)) { + rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC); + + for (;;) { + urb = usb_get_from_anchor(&acm->delayed); + if (!urb) + break; + + acm_start_wb(acm, urb->context); } /* @@ -1478,12 +1677,13 @@ static int acm_resume(struct usb_interface *intf) * do the write path at all cost */ if (rv < 0) - goto err_out; + goto out; - rv = acm_submit_read_urbs(acm, GFP_NOIO); + rv = acm_submit_read_urbs(acm, GFP_ATOMIC); } +out: + spin_unlock_irq(&acm->write_lock); -err_out: return rv; } @@ -1491,7 +1691,7 @@ static int acm_reset_resume(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); - if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) + if (tty_port_initialized(&acm->port)) tty_port_tty_hangup(&acm->port, false); return acm_resume(intf); @@ -1499,6 +1699,16 @@ static int acm_reset_resume(struct usb_interface *intf) #endif /* CONFIG_PM */ +static int acm_pre_reset(struct usb_interface *intf) +{ + struct acm *acm = usb_get_intfdata(intf); + + clear_bit(EVENT_RX_STALL, &acm->flags); + acm->nb_index = 0; /* pending control transfers are lost */ + + return 0; +} + #define NOKIA_PCSUITE_ACM_INFO(x) \ USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \ USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \ @@ -1515,12 +1725,33 @@ static int acm_reset_resume(struct usb_interface *intf) static const struct usb_device_id acm_ids[] = { /* quirky and broken devices */ + { USB_DEVICE(0x0424, 0x274e), /* Microchip Technology, Inc. (formerly SMSC) */ + .driver_info = DISABLE_ECHO, }, /* DISABLE ECHO in termios flag */ + { USB_DEVICE(0x076d, 0x0006), /* Denso Cradle CU-321 */ + .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */ + { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */ + .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */ { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x045b, 0x023c), /* Renesas R-Car H3 USB Download mode */ + .driver_info = DISABLE_ECHO, /* Don't echo banner */ + }, + { USB_DEVICE(0x045b, 0x0247), /* Renesas R-Car D3 USB Download mode */ + .driver_info = DISABLE_ECHO, /* Don't echo banner */ + }, + { USB_DEVICE(0x045b, 0x0248), /* Renesas R-Car M3-N USB Download mode */ + .driver_info = DISABLE_ECHO, /* Don't echo banner */ + }, + { USB_DEVICE(0x045b, 0x024D), /* Renesas R-Car E3 USB Download mode */ + .driver_info = DISABLE_ECHO, /* Don't echo banner */ + }, { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0e8d, 0x2000), /* MediaTek Inc Preloader */ + .driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */ + }, { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, @@ -1539,6 +1770,15 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ .driver_info = SINGLE_RX_URB, /* firmware bug */ }, + { USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */ + .driver_info = SINGLE_RX_URB, + }, + { USB_DEVICE(0x1901, 0x0006), /* GE Healthcare Patient Monitor UI Controller */ + .driver_info = DISABLE_ECHO, /* DISABLE ECHO in termios flag */ + }, + { USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, @@ -1554,17 +1794,38 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0572, 0x1349), /* Hiro (Conexant) USB MODEM H50228 */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, + { USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */ + .driver_info = QUIRK_CONTROL_LINE_STATE, }, + { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */ + { USB_DEVICE(0x2184, 0x0036) }, /* GW Instek AFG-125 */ { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */ }, /* Motorola H24 HSPA module: */ { USB_DEVICE(0x22b8, 0x2d91) }, /* modem */ - { USB_DEVICE(0x22b8, 0x2d92) }, /* modem + diagnostics */ - { USB_DEVICE(0x22b8, 0x2d93) }, /* modem + AT port */ - { USB_DEVICE(0x22b8, 0x2d95) }, /* modem + AT port + diagnostics */ - { USB_DEVICE(0x22b8, 0x2d96) }, /* modem + NMEA */ - { USB_DEVICE(0x22b8, 0x2d97) }, /* modem + diagnostics + NMEA */ - { USB_DEVICE(0x22b8, 0x2d99) }, /* modem + AT port + NMEA */ - { USB_DEVICE(0x22b8, 0x2d9a) }, /* modem + AT port + diagnostics + NMEA */ + { USB_DEVICE(0x22b8, 0x2d92), /* modem + diagnostics */ + .driver_info = NO_UNION_NORMAL, /* handle only modem interface */ + }, + { USB_DEVICE(0x22b8, 0x2d93), /* modem + AT port */ + .driver_info = NO_UNION_NORMAL, /* handle only modem interface */ + }, + { USB_DEVICE(0x22b8, 0x2d95), /* modem + AT port + diagnostics */ + .driver_info = NO_UNION_NORMAL, /* handle only modem interface */ + }, + { USB_DEVICE(0x22b8, 0x2d96), /* modem + NMEA */ + .driver_info = NO_UNION_NORMAL, /* handle only modem interface */ + }, + { USB_DEVICE(0x22b8, 0x2d97), /* modem + diagnostics + NMEA */ + .driver_info = NO_UNION_NORMAL, /* handle only modem interface */ + }, + { USB_DEVICE(0x22b8, 0x2d99), /* modem + AT port + NMEA */ + .driver_info = NO_UNION_NORMAL, /* handle only modem interface */ + }, + { USB_DEVICE(0x22b8, 0x2d9a), /* modem + AT port + diagnostics + NMEA */ + .driver_info = NO_UNION_NORMAL, /* handle only modem interface */ + }, { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */ .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on @@ -1585,6 +1846,22 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, + { USB_DEVICE(0xfff0, 0x0100), /* DATECS FP-2000 */ + .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ + }, + { USB_DEVICE(0x09d8, 0x0320), /* Elatec GmbH TWN3 */ + .driver_info = NO_UNION_NORMAL, /* has misplaced union descriptor */ + }, + { USB_DEVICE(0x0c26, 0x0020), /* Icom ICF3400 Serie */ + .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ + }, + { USB_DEVICE(0x0ca6, 0xa050), /* Castles VEGA3000 */ + .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ + }, + + { USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */ + .driver_info = CLEAR_HALT_CONDITIONS, + }, /* Nokia S60 phones expose two ACM channels. The first is * a modem and is picked up by the standard AT-command @@ -1623,7 +1900,6 @@ static const struct usb_device_id acm_ids[] = { { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */ { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */ { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */ - { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */ { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */ { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */ { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */ @@ -1655,16 +1931,6 @@ static const struct usb_device_id acm_ids[] = { /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */ - /* Support Lego NXT using pbLua firmware */ - { USB_DEVICE(0x0694, 0xff00), - .driver_info = NOT_A_MODEM, - }, - - /* Support for Droids MuIn LCD */ - { USB_DEVICE(0x04d8, 0x000b), - .driver_info = NO_DATA_INTERFACE, - }, - #if IS_ENABLED(CONFIG_INPUT_IMS_PCU) { USB_DEVICE(0x04d8, 0x0082), /* Application mode */ .driver_info = IGNORE_DEVICE, @@ -1674,6 +1940,68 @@ static const struct usb_device_id acm_ids[] = { }, #endif +#if IS_ENABLED(CONFIG_IR_TOY) + { USB_DEVICE(0x04d8, 0xfd08), + .driver_info = IGNORE_DEVICE, + }, + + { USB_DEVICE(0x04d8, 0xf58b), + .driver_info = IGNORE_DEVICE, + }, +#endif + +#if IS_ENABLED(CONFIG_USB_SERIAL_XR) + { USB_DEVICE(0x04e2, 0x1400), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1401), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1402), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1403), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1410), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1411), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1412), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1414), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1420), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1422), .driver_info = IGNORE_DEVICE }, + { USB_DEVICE(0x04e2, 0x1424), .driver_info = IGNORE_DEVICE }, +#endif + + /*Samsung phone in firmware update mode */ + { USB_DEVICE(0x04e8, 0x685d), + .driver_info = IGNORE_DEVICE, + }, + + /* Exclude Infineon Flash Loader utility */ + { USB_DEVICE(0x058b, 0x0041), + .driver_info = IGNORE_DEVICE, + }, + + /* Exclude ETAS ES58x */ + { USB_DEVICE(0x108c, 0x0159), /* ES581.4 */ + .driver_info = IGNORE_DEVICE, + }, + { USB_DEVICE(0x108c, 0x0168), /* ES582.1 */ + .driver_info = IGNORE_DEVICE, + }, + { USB_DEVICE(0x108c, 0x0169), /* ES584.1 */ + .driver_info = IGNORE_DEVICE, + }, + + { USB_DEVICE(0x1bc7, 0x0021), /* Telit 3G ACM only composition */ + .driver_info = SEND_ZERO_PACKET, + }, + { USB_DEVICE(0x1bc7, 0x0023), /* Telit 3G ACM + ECM composition */ + .driver_info = SEND_ZERO_PACKET, + }, + + /* Exclude Goodix Fingerprint Reader */ + { USB_DEVICE(0x27c6, 0x5395), + .driver_info = IGNORE_DEVICE, + }, + + /* Exclude Heimann Sensor GmbH USB appset demo */ + { USB_DEVICE(0x32a7, 0x0000), + .driver_info = IGNORE_DEVICE, + }, + /* control interfaces without any protocol set */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTO_NONE) }, @@ -1692,6 +2020,10 @@ static const struct usb_device_id acm_ids[] = { { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_CDMA) }, + { USB_DEVICE(0x1519, 0x0452), /* Intel 7260 modem */ + .driver_info = SEND_ZERO_PACKET, + }, + { } }; @@ -1706,6 +2038,7 @@ static struct usb_driver acm_driver = { .resume = acm_resume, .reset_resume = acm_reset_resume, #endif + .pre_reset = acm_pre_reset, .id_table = acm_ids, #ifdef CONFIG_PM .supports_autosuspend = 1, @@ -1725,6 +2058,7 @@ static const struct tty_operations acm_ops = { .hangup = acm_tty_hangup, .write = acm_tty_write, .write_room = acm_tty_write_room, + .flush_buffer = acm_tty_flush_buffer, .ioctl = acm_tty_ioctl, .throttle = acm_tty_throttle, .unthrottle = acm_tty_unthrottle, @@ -1733,6 +2067,9 @@ static const struct tty_operations acm_ops = { .set_termios = acm_tty_set_termios, .tiocmget = acm_tty_tiocmget, .tiocmset = acm_tty_tiocmset, + .get_serial = get_serial_info, + .set_serial = set_serial_info, + .get_icount = acm_tty_get_icount, }; /* @@ -1742,16 +2079,16 @@ static const struct tty_operations acm_ops = { static int __init acm_init(void) { int retval; - acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS); - if (!acm_tty_driver) - return -ENOMEM; + acm_tty_driver = tty_alloc_driver(ACM_TTY_MINORS, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(acm_tty_driver)) + return PTR_ERR(acm_tty_driver); acm_tty_driver->driver_name = "acm", acm_tty_driver->name = "ttyACM", acm_tty_driver->major = ACM_TTY_MAJOR, acm_tty_driver->minor_start = 0, acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, 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; @@ -1759,14 +2096,14 @@ static int __init acm_init(void) retval = tty_register_driver(acm_tty_driver); if (retval) { - put_tty_driver(acm_tty_driver); + tty_driver_kref_put(acm_tty_driver); return retval; } retval = usb_register(&acm_driver); if (retval) { tty_unregister_driver(acm_tty_driver); - put_tty_driver(acm_tty_driver); + tty_driver_kref_put(acm_tty_driver); return retval; } @@ -1779,7 +2116,8 @@ static void __exit acm_exit(void) { usb_deregister(&acm_driver); tty_unregister_driver(acm_tty_driver); - put_tty_driver(acm_tty_driver); + tty_driver_kref_put(acm_tty_driver); + idr_destroy(&acm_minors); } module_init(acm_init); diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 0f76e4af600e..759ac15631d3 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * * Includes for cdc-acm.c @@ -7,19 +8,13 @@ */ /* - * CMSPAR, some architectures can't have space and mark parity. - */ - -#ifndef CMSPAR -#define CMSPAR 0 -#endif - -/* * Major and minor numbers. */ #define ACM_TTY_MAJOR 166 -#define ACM_TTY_MINORS 32 +#define ACM_TTY_MINORS 256 + +#define ACM_MINOR_INVALID ACM_TTY_MINORS /* * Requests. @@ -28,26 +23,6 @@ #define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE) /* - * Output control lines. - */ - -#define ACM_CTRL_DTR 0x01 -#define ACM_CTRL_RTS 0x02 - -/* - * Input control lines and line errors. - */ - -#define ACM_CTRL_DCD 0x01 -#define ACM_CTRL_DSR 0x02 -#define ACM_CTRL_BRK 0x04 -#define ACM_CTRL_RI 0x08 - -#define ACM_CTRL_FRAMING 0x10 -#define ACM_CTRL_PARITY 0x20 -#define ACM_CTRL_OVERRUN 0x40 - -/* * Internal driver structures. */ @@ -63,12 +38,12 @@ #define ACM_NR 16 struct acm_wb { - unsigned char *buf; + u8 *buf; dma_addr_t dmah; - int len; - int use; + unsigned int len; struct urb *urb; struct acm *instance; + bool use; }; struct acm_rb { @@ -83,6 +58,7 @@ struct acm { struct usb_device *dev; /* the corresponding usb device */ struct usb_interface *control; /* control interface */ struct usb_interface *data; /* data interface */ + unsigned in, out; /* i/o pipes */ struct tty_port port; /* our tty port data */ struct urb *ctrlurb; /* urbs */ u8 *ctrl_buffer; /* buffers of urbs */ @@ -95,17 +71,27 @@ struct acm { struct urb *read_urbs[ACM_NR]; struct acm_rb read_buffers[ACM_NR]; int rx_buflimit; - int rx_endpoint; spinlock_t read_lock; - int write_used; /* number of non-empty write buffers */ + u8 *notification_buffer; /* to reassemble fragmented notifications */ + unsigned int nb_index; + unsigned int nb_size; int transmitting; spinlock_t write_lock; struct mutex mutex; bool disconnected; + unsigned long flags; +# define EVENT_TTY_WAKEUP 0 +# define EVENT_RX_STALL 1 +# define ACM_THROTTLED 2 +# define ACM_ERROR_DELAY 3 + unsigned long urbs_in_error_delay; /* these need to be restarted after a delay */ struct usb_cdc_line_coding line; /* bits, stop, parity */ - struct work_struct work; /* work queue entry for line discipline waking up */ + struct delayed_work dwork; /* work queue entry for various purposes */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */ + struct async_icount iocount; /* counters for control line changes */ + struct async_icount oldcount; /* for comparison of counter */ + wait_queue_head_t wioctl; /* for ioctl */ unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int readsize,ctrlsize; /* buffer sizes for freeing */ unsigned int minor; /* acm minor number */ @@ -113,19 +99,17 @@ struct acm { unsigned int ctrl_caps; /* control capabilities from the class specific header */ unsigned int susp_count; /* number of suspended interfaces */ unsigned int combined_interfaces:1; /* control and data collapsed */ - unsigned int is_int_ep:1; /* interrupt endpoints contrary to spec used */ - unsigned int throttled:1; /* actually throttled */ - unsigned int throttle_req:1; /* throttle requested */ u8 bInterval; - struct acm_wb *delayed_wb; /* write queued for a device about to be woken */ + struct usb_anchor delayed; /* writes queued for a device about to be woken */ + unsigned long quirks; }; -#define CDC_DATA_INTERFACE_TYPE 0x0a - /* constants describing various quirks and errors */ -#define NO_UNION_NORMAL 1 -#define SINGLE_RX_URB 2 -#define NO_CAP_LINE 4 -#define NOT_A_MODEM 8 -#define NO_DATA_INTERFACE 16 -#define IGNORE_DEVICE 32 +#define NO_UNION_NORMAL BIT(0) +#define SINGLE_RX_URB BIT(1) +#define NO_CAP_LINE BIT(2) +#define IGNORE_DEVICE BIT(3) +#define QUIRK_CONTROL_LINE_STATE BIT(4) +#define CLEAR_HALT_CONDITIONS BIT(5) +#define SEND_ZERO_PACKET BIT(6) +#define DISABLE_ECHO BIT(7) diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 8a230f0ef77c..ecd6d1f39e49 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * cdc-wdm.c * @@ -20,16 +21,14 @@ #include <linux/uaccess.h> #include <linux/bitops.h> #include <linux/poll.h> +#include <linux/skbuff.h> #include <linux/usb.h> #include <linux/usb/cdc.h> +#include <linux/wwan.h> #include <asm/byteorder.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/usb/cdc-wdm.h> -/* - * Version Information - */ -#define DRIVER_VERSION "v0.03" #define DRIVER_AUTHOR "Oliver Neukum" #define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management" @@ -58,9 +57,13 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); #define WDM_SUSPENDING 8 #define WDM_RESETTING 9 #define WDM_OVERFLOW 10 +#define WDM_WWAN_IN_USE 11 #define WDM_MAX 16 +/* we cannot wait forever at flush() */ +#define WDM_FLUSH_TIMEOUT (30 * HZ) + /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */ #define WDM_DEFAULT_BUFSIZE 256 @@ -89,7 +92,6 @@ struct wdm_device { u16 wMaxCommand; u16 wMaxPacketSize; __le16 inum; - int reslength; int length; int read; int count; @@ -99,11 +101,16 @@ struct wdm_device { struct mutex rlock; wait_queue_head_t wait; struct work_struct rxwork; + struct work_struct service_outs_intr; int werr; int rerr; + int resp_count; struct list_head device_list; int (*manage_power)(struct usb_interface *, int); + + enum wwan_port_type wwanp_type; + struct wwan_port *wwanp; }; static struct usb_driver wdm_driver; @@ -143,38 +150,43 @@ found: static void wdm_out_callback(struct urb *urb) { struct wdm_device *desc; + unsigned long flags; + desc = urb->context; - spin_lock(&desc->iuspin); + spin_lock_irqsave(&desc->iuspin, flags); desc->werr = urb->status; - spin_unlock(&desc->iuspin); + spin_unlock_irqrestore(&desc->iuspin, flags); kfree(desc->outbuf); desc->outbuf = NULL; clear_bit(WDM_IN_USE, &desc->flags); - wake_up(&desc->wait); + wake_up_all(&desc->wait); } +static void wdm_wwan_rx(struct wdm_device *desc, int length); + static void wdm_in_callback(struct urb *urb) { + unsigned long flags; struct wdm_device *desc = urb->context; int status = urb->status; int length = urb->actual_length; - spin_lock(&desc->iuspin); + spin_lock_irqsave(&desc->iuspin, flags); clear_bit(WDM_RESPONDING, &desc->flags); if (status) { switch (status) { case -ENOENT: dev_dbg(&desc->intf->dev, - "nonzero urb status received: -ENOENT"); + "nonzero urb status received: -ENOENT\n"); goto skip_error; case -ECONNRESET: dev_dbg(&desc->intf->dev, - "nonzero urb status received: -ECONNRESET"); + "nonzero urb status received: -ECONNRESET\n"); goto skip_error; case -ESHUTDOWN: dev_dbg(&desc->intf->dev, - "nonzero urb status received: -ESHUTDOWN"); + "nonzero urb status received: -ESHUTDOWN\n"); goto skip_error; case -EPIPE: dev_err(&desc->intf->dev, @@ -187,7 +199,25 @@ static void wdm_in_callback(struct urb *urb) } } - desc->rerr = status; + if (test_bit(WDM_WWAN_IN_USE, &desc->flags)) { + wdm_wwan_rx(desc, length); + goto out; + } + + /* + * only set a new error if there is no previous error. + * Errors are only cleared during read/open + * Avoid propagating -EPIPE (stall) to userspace since it is + * better handled as an empty read + */ + if (desc->rerr == 0 && status != -EPIPE) + desc->rerr = status; + + if (length == 0) { + dev_dbg(&desc->intf->dev, "received ZLP\n"); + goto skip_zlp; + } + if (length + desc->length > desc->wMaxCommand) { /* The buffer would overflow */ set_bit(WDM_OVERFLOW, &desc->flags); @@ -196,19 +226,32 @@ static void wdm_in_callback(struct urb *urb) if (!test_bit(WDM_OVERFLOW, &desc->flags)) { memmove(desc->ubuf + desc->length, desc->inbuf, length); desc->length += length; - desc->reslength = length; } } skip_error: - wake_up(&desc->wait); - set_bit(WDM_READ, &desc->flags); - spin_unlock(&desc->iuspin); + if (desc->rerr) { + /* + * If there was a ZLP or an error, userspace may decide to not + * read any data after poll'ing. + * We should respond to further attempts from the device to send + * data, so that we can get unstuck. + */ +skip_zlp: + schedule_work(&desc->service_outs_intr); + } else { + set_bit(WDM_READ, &desc->flags); + wake_up(&desc->wait); + } +out: + spin_unlock_irqrestore(&desc->iuspin, flags); } static void wdm_int_callback(struct urb *urb) { + unsigned long flags; int rv = 0; + int responding; int status = urb->status; struct wdm_device *desc; struct usb_cdc_notification *dr; @@ -227,14 +270,14 @@ static void wdm_int_callback(struct urb *urb) dev_err(&desc->intf->dev, "Stall on int endpoint\n"); goto sw; /* halt is cleared in work */ default: - dev_err(&desc->intf->dev, + dev_err_ratelimited(&desc->intf->dev, "nonzero urb status received: %d\n", status); break; } } if (urb->actual_length < sizeof(struct usb_cdc_notification)) { - dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n", + dev_err_ratelimited(&desc->intf->dev, "wdm_int_callback - %d bytes\n", urb->actual_length); goto exit; } @@ -242,34 +285,39 @@ static void wdm_int_callback(struct urb *urb) switch (dr->bNotificationType) { case USB_CDC_NOTIFY_RESPONSE_AVAILABLE: dev_dbg(&desc->intf->dev, - "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d", - dr->wIndex, dr->wLength); + "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d\n", + le16_to_cpu(dr->wIndex), le16_to_cpu(dr->wLength)); break; case USB_CDC_NOTIFY_NETWORK_CONNECTION: dev_dbg(&desc->intf->dev, - "NOTIFY_NETWORK_CONNECTION %s network", + "NOTIFY_NETWORK_CONNECTION %s network\n", dr->wValue ? "connected to" : "disconnected from"); goto exit; + case USB_CDC_NOTIFY_SPEED_CHANGE: + dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)\n", + urb->actual_length); + goto exit; default: clear_bit(WDM_POLL_RUNNING, &desc->flags); dev_err(&desc->intf->dev, "unknown notification %d received: index %d len %d\n", - dr->bNotificationType, dr->wIndex, dr->wLength); + dr->bNotificationType, + le16_to_cpu(dr->wIndex), + le16_to_cpu(dr->wLength)); goto exit; } - spin_lock(&desc->iuspin); - clear_bit(WDM_READ, &desc->flags); - set_bit(WDM_RESPONDING, &desc->flags); - if (!test_bit(WDM_DISCONNECTING, &desc->flags) + spin_lock_irqsave(&desc->iuspin, flags); + responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); + if (!desc->resp_count++ && !responding + && !test_bit(WDM_DISCONNECTING, &desc->flags) && !test_bit(WDM_SUSPENDING, &desc->flags)) { rv = usb_submit_urb(desc->response, GFP_ATOMIC); - dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", - __func__, rv); + dev_dbg(&desc->intf->dev, "submit response URB %d\n", rv); } - spin_unlock(&desc->iuspin); + spin_unlock_irqrestore(&desc->iuspin, flags); if (rv < 0) { clear_bit(WDM_RESPONDING, &desc->flags); if (rv == -EPERM) @@ -291,12 +339,23 @@ exit: } -static void kill_urbs(struct wdm_device *desc) +static void poison_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); + usb_poison_urb(desc->command); + usb_poison_urb(desc->validity); + usb_poison_urb(desc->response); +} + +static void unpoison_urbs(struct wdm_device *desc) +{ + /* + * the order here is not essential + * it is symmetrical just to be nice + */ + usb_unpoison_urb(desc->response); + usb_unpoison_urb(desc->validity); + usb_unpoison_urb(desc->command); } static void free_urbs(struct wdm_device *desc) @@ -333,40 +392,27 @@ static ssize_t wdm_write desc->werr = 0; spin_unlock_irq(&desc->iuspin); if (we < 0) - return -EIO; + return usb_translate_errors(we); - buf = kmalloc(count, GFP_KERNEL); - if (!buf) { - rv = -ENOMEM; - goto outnl; - } - - r = copy_from_user(buf, buffer, count); - if (r > 0) { - kfree(buf); - rv = -EFAULT; - goto outnl; - } + buf = memdup_user(buffer, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); /* concurrent writes and disconnect */ r = mutex_lock_interruptible(&desc->wlock); rv = -ERESTARTSYS; - if (r) { - kfree(buf); - goto outnl; - } + if (r) + goto out_free_mem; if (test_bit(WDM_DISCONNECTING, &desc->flags)) { - kfree(buf); rv = -ENODEV; - goto outnp; + goto out_free_mem_lock; } r = usb_autopm_get_interface(desc->intf); if (r < 0) { - kfree(buf); rv = usb_translate_errors(r); - goto outnp; + goto out_free_mem_lock; } if (!(file->f_flags & O_NONBLOCK)) @@ -379,10 +425,12 @@ static ssize_t wdm_write if (test_bit(WDM_RESETTING, &desc->flags)) r = -EIO; + if (test_bit(WDM_DISCONNECTING, &desc->flags)) + r = -ENODEV; + if (r < 0) { - kfree(buf); rv = r; - goto out; + goto out_free_mem_pm; } req = desc->orq; @@ -402,28 +450,74 @@ static ssize_t wdm_write USB_RECIP_INTERFACE); req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; req->wValue = 0; - req->wIndex = desc->inum; + req->wIndex = desc->inum; /* already converted */ req->wLength = cpu_to_le16(count); set_bit(WDM_IN_USE, &desc->flags); desc->outbuf = buf; rv = usb_submit_urb(desc->command, GFP_KERNEL); if (rv < 0) { - kfree(buf); desc->outbuf = NULL; clear_bit(WDM_IN_USE, &desc->flags); + wake_up_all(&desc->wait); /* for wdm_wait_for_response() */ dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv); rv = usb_translate_errors(rv); + goto out_free_mem_pm; } else { - dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d", - req->wIndex); + dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d\n", + le16_to_cpu(req->wIndex)); } -out: + + usb_autopm_put_interface(desc->intf); + mutex_unlock(&desc->wlock); + return count; + +out_free_mem_pm: usb_autopm_put_interface(desc->intf); -outnp: +out_free_mem_lock: mutex_unlock(&desc->wlock); -outnl: - return rv < 0 ? rv : count; +out_free_mem: + kfree(buf); + return rv; +} + +/* + * Submit the read urb if resp_count is non-zero. + * + * Called with desc->iuspin locked + */ +static int service_outstanding_interrupt(struct wdm_device *desc) +{ + int rv = 0; + + /* submit read urb only if the device is waiting for it */ + if (!desc->resp_count || !--desc->resp_count) + goto out; + + if (test_bit(WDM_DISCONNECTING, &desc->flags)) { + rv = -ENODEV; + goto out; + } + if (test_bit(WDM_RESETTING, &desc->flags)) { + rv = -EIO; + goto out; + } + + set_bit(WDM_RESPONDING, &desc->flags); + spin_unlock_irq(&desc->iuspin); + rv = usb_submit_urb(desc->response, GFP_KERNEL); + spin_lock_irq(&desc->iuspin); + if (rv) { + if (!test_bit(WDM_DISCONNECTING, &desc->flags)) + dev_err(&desc->intf->dev, + "usb_submit_urb failed with result %d\n", rv); + + /* make sure the next notification trigger a submit */ + clear_bit(WDM_RESPONDING, &desc->flags); + desc->resp_count = 0; + } +out: + return rv; } static ssize_t wdm_read @@ -438,7 +532,7 @@ static ssize_t wdm_read if (rv < 0) return -ERESTARTSYS; - cntr = ACCESS_ONCE(desc->length); + cntr = READ_ONCE(desc->length); if (cntr == 0) { desc->read = 0; retry: @@ -454,7 +548,7 @@ retry: i++; if (file->f_flags & O_NONBLOCK) { if (!test_bit(WDM_READ, &desc->flags)) { - rv = cntr ? cntr : -EAGAIN; + rv = -EAGAIN; goto err; } rv = 0; @@ -481,9 +575,9 @@ retry: spin_lock_irq(&desc->iuspin); if (desc->rerr) { /* read completed, error happened */ + rv = usb_translate_errors(desc->rerr); desc->rerr = 0; spin_unlock_irq(&desc->iuspin); - rv = -EIO; goto err; } /* @@ -495,12 +589,6 @@ retry: goto retry; } - if (!desc->reslength) { /* zero length read */ - dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); - clear_bit(WDM_READ, &desc->flags); - spin_unlock_irq(&desc->iuspin); - goto retry; - } cntr = desc->length; spin_unlock_irq(&desc->iuspin); } @@ -520,11 +608,11 @@ retry: desc->length -= cntr; /* in case we had outstanding data */ - if (!desc->length) + if (!desc->length) { clear_bit(WDM_READ, &desc->flags); - + service_outstanding_interrupt(desc); + } spin_unlock_irq(&desc->iuspin); - rv = cntr; err: @@ -532,38 +620,78 @@ err: return rv; } -static int wdm_flush(struct file *file, fl_owner_t id) +static int wdm_wait_for_response(struct file *file, long timeout) { struct wdm_device *desc = file->private_data; + long rv; /* Use long here because (int) MAX_SCHEDULE_TIMEOUT < 0. */ - wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); + /* + * Needs both flags. We cannot do with one because resetting it would + * cause a race with write() yet we need to signal a disconnect. + */ + rv = wait_event_interruptible_timeout(desc->wait, + !test_bit(WDM_IN_USE, &desc->flags) || + test_bit(WDM_DISCONNECTING, &desc->flags), + timeout); - /* cannot dereference desc->intf if WDM_DISCONNECTING */ - if (desc->werr < 0 && !test_bit(WDM_DISCONNECTING, &desc->flags)) - dev_err(&desc->intf->dev, "Error in flush path: %d\n", - desc->werr); + /* + * To report the correct error. This is best effort. + * We are inevitably racing with the hardware. + */ + if (test_bit(WDM_DISCONNECTING, &desc->flags)) + return -ENODEV; + if (!rv) + return -EIO; + if (rv < 0) + return -EINTR; + + spin_lock_irq(&desc->iuspin); + rv = desc->werr; + desc->werr = 0; + spin_unlock_irq(&desc->iuspin); + + return usb_translate_errors(rv); + +} + +/* + * You need to send a signal when you react to malicious or defective hardware. + * Also, don't abort when fsync() returned -EINVAL, for older kernels which do + * not implement wdm_flush() will return -EINVAL. + */ +static int wdm_fsync(struct file *file, loff_t start, loff_t end, int datasync) +{ + return wdm_wait_for_response(file, MAX_SCHEDULE_TIMEOUT); +} - return usb_translate_errors(desc->werr); +/* + * Same with wdm_fsync(), except it uses finite timeout in order to react to + * malicious or defective hardware which ceased communication after close() was + * implicitly called due to process termination. + */ +static int wdm_flush(struct file *file, fl_owner_t id) +{ + return wdm_wait_for_response(file, WDM_FLUSH_TIMEOUT); } -static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait) +static __poll_t wdm_poll(struct file *file, struct poll_table_struct *wait) { struct wdm_device *desc = file->private_data; unsigned long flags; - unsigned int mask = 0; + __poll_t mask = 0; spin_lock_irqsave(&desc->iuspin, flags); if (test_bit(WDM_DISCONNECTING, &desc->flags)) { - mask = POLLHUP | POLLERR; + mask = EPOLLHUP | EPOLLERR; spin_unlock_irqrestore(&desc->iuspin, flags); goto desc_out; } if (test_bit(WDM_READ, &desc->flags)) - mask = POLLIN | POLLRDNORM; + mask = EPOLLIN | EPOLLRDNORM; if (desc->rerr || desc->werr) - mask |= POLLERR; + mask |= EPOLLERR; if (!test_bit(WDM_IN_USE, &desc->flags)) - mask |= POLLOUT | POLLWRNORM; + mask |= EPOLLOUT | EPOLLWRNORM; spin_unlock_irqrestore(&desc->iuspin, flags); poll_wait(file, &desc->wait, wait); @@ -589,6 +717,11 @@ static int wdm_open(struct inode *inode, struct file *file) goto out; file->private_data = desc; + if (test_bit(WDM_WWAN_IN_USE, &desc->flags)) { + rv = -EBUSY; + goto out; + } + smp_rmb(); /* ordered against wdm_wwan_port_stop() */ rv = usb_autopm_get_interface(desc->intf); if (rv < 0) { dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); @@ -632,9 +765,14 @@ static int wdm_release(struct inode *inode, struct file *file) if (!desc->count) { if (!test_bit(WDM_DISCONNECTING, &desc->flags)) { - dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); - kill_urbs(desc); + dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n"); + poison_urbs(desc); + spin_lock_irq(&desc->iuspin); + desc->resp_count = 0; + clear_bit(WDM_RESPONDING, &desc->flags); + spin_unlock_irq(&desc->iuspin); desc->manage_power(desc->intf, 0); + unpoison_urbs(desc); } else { /* must avoid dev_printk here as desc->intf is invalid */ pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__); @@ -665,12 +803,13 @@ static const struct file_operations wdm_fops = { .owner = THIS_MODULE, .read = wdm_read, .write = wdm_write, + .fsync = wdm_fsync, .open = wdm_open, .flush = wdm_flush, .release = wdm_release, .poll = wdm_poll, .unlocked_ioctl = wdm_ioctl, - .compat_ioctl = wdm_ioctl, + .compat_ioctl = compat_ptr_ioctl, .llseek = noop_llseek, }; @@ -680,21 +819,183 @@ static struct usb_class_driver wdm_class = { .minor_base = WDM_MINOR_BASE, }; +/* --- WWAN framework integration --- */ +#ifdef CONFIG_WWAN +static int wdm_wwan_port_start(struct wwan_port *port) +{ + struct wdm_device *desc = wwan_port_get_drvdata(port); + int rv; + + /* The interface is both exposed via the WWAN framework and as a + * legacy usbmisc chardev. If chardev is already open, just fail + * to prevent concurrent usage. Otherwise, switch to WWAN mode. + */ + mutex_lock(&wdm_mutex); + if (desc->count) { + mutex_unlock(&wdm_mutex); + return -EBUSY; + } + set_bit(WDM_WWAN_IN_USE, &desc->flags); + mutex_unlock(&wdm_mutex); + + desc->manage_power(desc->intf, 1); + + /* tx is allowed */ + wwan_port_txon(port); + + /* Start getting events */ + rv = usb_submit_urb(desc->validity, GFP_KERNEL); + if (rv < 0) { + wwan_port_txoff(port); + desc->manage_power(desc->intf, 0); + /* this must be last lest we race with chardev open */ + clear_bit(WDM_WWAN_IN_USE, &desc->flags); + } + + return rv; +} + +static void wdm_wwan_port_stop(struct wwan_port *port) +{ + struct wdm_device *desc = wwan_port_get_drvdata(port); + + /* Stop all transfers and disable WWAN mode */ + poison_urbs(desc); + desc->manage_power(desc->intf, 0); + clear_bit(WDM_READ, &desc->flags); + unpoison_urbs(desc); + smp_wmb(); /* ordered against wdm_open() */ + /* this must be last lest we open a poisoned device */ + clear_bit(WDM_WWAN_IN_USE, &desc->flags); +} + +static void wdm_wwan_port_tx_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct wdm_device *desc = skb_shinfo(skb)->destructor_arg; + + usb_autopm_put_interface_async(desc->intf); + wwan_port_txon(desc->wwanp); + kfree_skb(skb); +} + +static int wdm_wwan_port_tx(struct wwan_port *port, struct sk_buff *skb) +{ + struct wdm_device *desc = wwan_port_get_drvdata(port); + struct usb_interface *intf = desc->intf; + struct usb_ctrlrequest *req = desc->orq; + int rv; + + rv = usb_autopm_get_interface(intf); + if (rv) + return rv; + + usb_fill_control_urb( + desc->command, + interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + (unsigned char *)req, + skb->data, + skb->len, + wdm_wwan_port_tx_complete, + skb + ); + + req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE); + req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; + req->wValue = 0; + req->wIndex = desc->inum; /* already converted */ + req->wLength = cpu_to_le16(skb->len); + + skb_shinfo(skb)->destructor_arg = desc; + + rv = usb_submit_urb(desc->command, GFP_KERNEL); + if (rv) + usb_autopm_put_interface(intf); + else /* One transfer at a time, stop TX until URB completion */ + wwan_port_txoff(port); + + return rv; +} + +static const struct wwan_port_ops wdm_wwan_port_ops = { + .start = wdm_wwan_port_start, + .stop = wdm_wwan_port_stop, + .tx = wdm_wwan_port_tx, +}; + +static void wdm_wwan_init(struct wdm_device *desc) +{ + struct usb_interface *intf = desc->intf; + struct wwan_port *port; + + /* Only register to WWAN core if protocol/type is known */ + if (desc->wwanp_type == WWAN_PORT_UNKNOWN) { + dev_info(&intf->dev, "Unknown control protocol\n"); + return; + } + + port = wwan_create_port(&intf->dev, desc->wwanp_type, &wdm_wwan_port_ops, + NULL, desc); + if (IS_ERR(port)) { + dev_err(&intf->dev, "%s: Unable to create WWAN port\n", + dev_name(intf->usb_dev)); + return; + } + + desc->wwanp = port; +} + +static void wdm_wwan_deinit(struct wdm_device *desc) +{ + if (!desc->wwanp) + return; + + wwan_remove_port(desc->wwanp); + desc->wwanp = NULL; +} + +static void wdm_wwan_rx(struct wdm_device *desc, int length) +{ + struct wwan_port *port = desc->wwanp; + struct sk_buff *skb; + + /* Forward data to WWAN port */ + skb = alloc_skb(length, GFP_ATOMIC); + if (!skb) + return; + + skb_put_data(skb, desc->inbuf, length); + wwan_port_rx(port, skb); + + /* inbuf has been copied, it is safe to check for outstanding data */ + schedule_work(&desc->service_outs_intr); +} +#else /* CONFIG_WWAN */ +static void wdm_wwan_init(struct wdm_device *desc) {} +static void wdm_wwan_deinit(struct wdm_device *desc) {} +static void wdm_wwan_rx(struct wdm_device *desc, int length) {} +#endif /* CONFIG_WWAN */ + /* --- error handling --- */ static void wdm_rxwork(struct work_struct *work) { struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); unsigned long flags; - int rv; + int rv = 0; + int responding; spin_lock_irqsave(&desc->iuspin, flags); if (test_bit(WDM_DISCONNECTING, &desc->flags)) { spin_unlock_irqrestore(&desc->iuspin, flags); } else { + responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); spin_unlock_irqrestore(&desc->iuspin, flags); - rv = usb_submit_urb(desc->response, GFP_KERNEL); + if (!responding) + rv = usb_submit_urb(desc->response, GFP_KERNEL); if (rv < 0 && rv != -EPERM) { spin_lock_irqsave(&desc->iuspin, flags); + clear_bit(WDM_RESPONDING, &desc->flags); if (!test_bit(WDM_DISCONNECTING, &desc->flags)) schedule_work(&desc->rxwork); spin_unlock_irqrestore(&desc->iuspin, flags); @@ -702,10 +1003,26 @@ static void wdm_rxwork(struct work_struct *work) } } +static void service_interrupt_work(struct work_struct *work) +{ + struct wdm_device *desc; + + desc = container_of(work, struct wdm_device, service_outs_intr); + + spin_lock_irq(&desc->iuspin); + service_outstanding_interrupt(desc); + if (!desc->resp_count && (desc->length || desc->rerr)) { + set_bit(WDM_READ, &desc->flags); + wake_up(&desc->wait); + } + spin_unlock_irq(&desc->iuspin); +} + /* --- hotplug --- */ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, - u16 bufsize, int (*manage_power)(struct usb_interface *, int)) + u16 bufsize, enum wwan_port_type type, + int (*manage_power)(struct usb_interface *, int)) { int rv = -ENOMEM; struct wdm_device *desc; @@ -722,11 +1039,14 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor /* this will be expanded and needed in hardware endianness */ desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); desc->intf = intf; + desc->wwanp_type = type; INIT_WORK(&desc->rxwork, wdm_rxwork); + INIT_WORK(&desc->service_outs_intr, service_interrupt_work); - rv = -EINVAL; - if (!usb_endpoint_is_int_in(ep)) + if (!usb_endpoint_is_int_in(ep)) { + rv = -EINVAL; goto err; + } desc->wMaxPacketSize = usb_endpoint_maxp(ep); @@ -775,7 +1095,7 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE; desc->irq->wValue = 0; - desc->irq->wIndex = desc->inum; + desc->irq->wIndex = desc->inum; /* already converted */ desc->irq->wLength = cpu_to_le16(desc->wMaxCommand); usb_fill_control_urb( @@ -801,6 +1121,9 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor goto err; else dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev)); + + wdm_wwan_init(desc); + out: return rv; err: @@ -815,13 +1138,11 @@ static int wdm_manage_power(struct usb_interface *intf, int on) { /* need autopm_get/put here to ensure the usbcore sees the new value */ int rv = usb_autopm_get_interface(intf); - if (rv < 0) - goto err; intf->needs_remote_wakeup = on; - usb_autopm_put_interface(intf); -err: - return rv; + if (!rv) + usb_autopm_put_interface(intf); + return 0; } static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -829,45 +1150,25 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id) int rv = -EINVAL; struct usb_host_interface *iface; struct usb_endpoint_descriptor *ep; - struct usb_cdc_dmm_desc *dmhd; + struct usb_cdc_parsed_header hdr; u8 *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; u16 maxcom = WDM_DEFAULT_BUFSIZE; if (!buffer) goto err; - while (buflen > 2) { - if (buffer[1] != USB_DT_CS_INTERFACE) { - dev_err(&intf->dev, "skipping garbage\n"); - goto next_desc; - } - switch (buffer[2]) { - case USB_CDC_HEADER_TYPE: - break; - case USB_CDC_DMM_TYPE: - dmhd = (struct usb_cdc_dmm_desc *)buffer; - maxcom = le16_to_cpu(dmhd->wMaxCommand); - dev_dbg(&intf->dev, - "Finding maximum buffer length: %d", maxcom); - break; - default: - dev_err(&intf->dev, - "Ignoring extra header, type %d, length %d\n", - buffer[2], buffer[0]); - break; - } -next_desc: - buflen -= buffer[0]; - buffer += buffer[0]; - } + cdc_parse_cdc_header(&hdr, intf, buffer, buflen); + + if (hdr.usb_cdc_dmm_desc) + maxcom = le16_to_cpu(hdr.usb_cdc_dmm_desc->wMaxCommand); iface = intf->cur_altsetting; if (iface->desc.bNumEndpoints != 1) goto err; ep = &iface->endpoint[0].desc; - rv = wdm_create(intf, ep, maxcom, &wdm_manage_power); + rv = wdm_create(intf, ep, maxcom, WWAN_PORT_UNKNOWN, &wdm_manage_power); err: return rv; @@ -878,7 +1179,9 @@ err: * @intf: usb interface the subdriver will associate with * @ep: interrupt endpoint to monitor for notifications * @bufsize: maximum message size to support for read/write - * + * @type: Type/protocol of the transported data (MBIM, QMI...) + * @manage_power: call-back invoked during open and release to + * manage the device's power * Create WDM usb class character device and associate it with intf * without binding, allowing another driver to manage the interface. * @@ -894,12 +1197,12 @@ err: */ struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, - int bufsize, + int bufsize, enum wwan_port_type type, int (*manage_power)(struct usb_interface *, int)) { - int rv = -EINVAL; + int rv; - rv = wdm_create(intf, ep, bufsize, manage_power); + rv = wdm_create(intf, ep, bufsize, type, manage_power); if (rv < 0) goto err; @@ -918,18 +1221,19 @@ static void wdm_disconnect(struct usb_interface *intf) desc = wdm_find_device(intf); mutex_lock(&wdm_mutex); + wdm_wwan_deinit(desc); + /* the spinlock makes sure no new urbs are generated in the callbacks */ 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); wake_up_all(&desc->wait); mutex_lock(&desc->rlock); mutex_lock(&desc->wlock); - kill_urbs(desc); + poison_urbs(desc); cancel_work_sync(&desc->rxwork); + cancel_work_sync(&desc->service_outs_intr); mutex_unlock(&desc->wlock); mutex_unlock(&desc->rlock); @@ -941,7 +1245,7 @@ static void wdm_disconnect(struct usb_interface *intf) if (!desc->count) cleanup(desc); else - dev_dbg(&intf->dev, "%s: %d open files - postponing cleanup\n", __func__, desc->count); + dev_dbg(&intf->dev, "%d open files - postponing cleanup\n", desc->count); mutex_unlock(&wdm_mutex); } @@ -970,8 +1274,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) set_bit(WDM_SUSPENDING, &desc->flags); spin_unlock_irq(&desc->iuspin); /* callback submits work - order is essential */ - kill_urbs(desc); + poison_urbs(desc); cancel_work_sync(&desc->rxwork); + cancel_work_sync(&desc->service_outs_intr); + unpoison_urbs(desc); } if (!PMSG_IS_AUTO(message)) { mutex_unlock(&desc->wlock); @@ -1029,8 +1335,9 @@ static int wdm_pre_reset(struct usb_interface *intf) wake_up_all(&desc->wait); mutex_lock(&desc->rlock); mutex_lock(&desc->wlock); - kill_urbs(desc); + poison_urbs(desc); cancel_work_sync(&desc->rxwork); + cancel_work_sync(&desc->service_outs_intr); return 0; } @@ -1039,12 +1346,13 @@ static int wdm_post_reset(struct usb_interface *intf) struct wdm_device *desc = wdm_find_device(intf); int rv; + unpoison_urbs(desc); clear_bit(WDM_OVERFLOW, &desc->flags); clear_bit(WDM_RESETTING, &desc->flags); rv = recover_from_urb_loss(desc); mutex_unlock(&desc->wlock); mutex_unlock(&desc->rlock); - return 0; + return rv; } static struct usb_driver wdm_driver = { diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index d4c47d5d7625..a7a1d38b6bef 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * usblp.c * @@ -31,33 +32,18 @@ * none - Maintained in Linux kernel after v0.13 */ -/* - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - #include <linux/module.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/minmax.h> +#include <linux/sched/signal.h> #include <linux/signal.h> #include <linux/poll.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/lp.h> #include <linux/mutex.h> #undef DEBUG #include <linux/usb.h> +#include <linux/usb/ch9.h> #include <linux/ratelimit.h> /* @@ -80,12 +66,20 @@ #define IOCNR_SOFT_RESET 7 /* Get device_id string: */ #define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len) -/* The following ioctls were added for http://hpoj.sourceforge.net: */ -/* Get two-int array: - * [0]=current protocol (1=7/1/1, 2=7/1/2, 3=7/1/3), - * [1]=supported protocol mask (mask&(1<<n)!=0 means 7/1/n supported): */ +/* The following ioctls were added for http://hpoj.sourceforge.net: + * Get two-int array: + * [0]=current protocol + * (1=USB_CLASS_PRINTER/1/1, 2=USB_CLASS_PRINTER/1/2, + * 3=USB_CLASS_PRINTER/1/3), + * [1]=supported protocol mask (mask&(1<<n)!=0 means + * USB_CLASS_PRINTER/1/n supported): + */ #define LPIOC_GET_PROTOCOLS(len) _IOC(_IOC_READ, 'P', IOCNR_GET_PROTOCOLS, len) -/* Set protocol (arg: 1=7/1/1, 2=7/1/2, 3=7/1/3): */ +/* + * Set protocol + * (arg: 1=USB_CLASS_PRINTER/1/1, 2=USB_CLASS_PRINTER/1/2, + * 3=USB_CLASS_PRINTER/1/3): + */ #define LPIOC_SET_PROTOCOL _IOC(_IOC_WRITE, 'P', IOCNR_SET_PROTOCOL, 0) /* Set channel number (HP Vendor-specific command): */ #define LPIOC_HP_SET_CHANNEL _IOC(_IOC_WRITE, 'P', IOCNR_HP_SET_CHANNEL, 0) @@ -94,7 +88,7 @@ /* Get two-int array: [0]=vendor ID, [1]=product ID: */ #define LPIOC_GET_VID_PID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_VID_PID, len) /* Perform class specific soft reset */ -#define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0); +#define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0) /* * A DEVICE_ID string may include the printer's serial number. @@ -147,8 +141,10 @@ struct usblp { int readcount; /* Counter for reads */ int ifnum; /* Interface number */ struct usb_interface *intf; /* The interface */ - /* Alternate-setting numbers and endpoints for each protocol - * (7/1/{index=1,2,3}) that the device supports: */ + /* + * Alternate-setting numbers and endpoints for each protocol + * (USB_CLASS_PRINTER/1/{index=1,2,3}) that the device supports: + */ struct { int alt_setting; struct usb_endpoint_descriptor *epwrite; @@ -279,12 +275,29 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i #define usblp_reset(usblp)\ usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_TYPE_CLASS, USB_DIR_OUT, USB_RECIP_OTHER, 0, NULL, 0) -#define usblp_hp_channel_change_request(usblp, channel, buffer) \ - usblp_ctrl_msg(usblp, USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST, USB_TYPE_VENDOR, USB_DIR_IN, USB_RECIP_INTERFACE, channel, buffer, 1) +static int usblp_hp_channel_change_request(struct usblp *usblp, int channel, u8 *new_channel) +{ + u8 *buf; + int ret; + + buf = kzalloc(1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usblp_ctrl_msg(usblp, USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST, + USB_TYPE_VENDOR, USB_DIR_IN, USB_RECIP_INTERFACE, + channel, buf, 1); + if (ret == 0) + *new_channel = buf[0]; + + kfree(buf); + + return ret; +} /* * See the description for usblp_select_alts() below for the usage - * explanation. Look into your /proc/bus/usb/devices and dmesg in + * explanation. Look into your /sys/kernel/debug/usb/devices and dmesg in * case of any trouble. */ static int proto_bias = -1; @@ -297,6 +310,7 @@ static void usblp_bulk_read(struct urb *urb) { struct usblp *usblp = urb->context; int status = urb->status; + unsigned long flags; if (usblp->present && usblp->used) { if (status) @@ -304,14 +318,14 @@ static void usblp_bulk_read(struct urb *urb) "nonzero read bulk status received: %d\n", usblp->minor, status); } - spin_lock(&usblp->lock); + spin_lock_irqsave(&usblp->lock, flags); if (status < 0) usblp->rstatus = status; else usblp->rstatus = urb->actual_length; usblp->rcomplete = 1; wake_up(&usblp->rwait); - spin_unlock(&usblp->lock); + spin_unlock_irqrestore(&usblp->lock, flags); usb_free_urb(urb); } @@ -320,6 +334,7 @@ static void usblp_bulk_write(struct urb *urb) { struct usblp *usblp = urb->context; int status = urb->status; + unsigned long flags; if (usblp->present && usblp->used) { if (status) @@ -327,7 +342,7 @@ static void usblp_bulk_write(struct urb *urb) "nonzero write bulk status received: %d\n", usblp->minor, status); } - spin_lock(&usblp->lock); + spin_lock_irqsave(&usblp->lock, flags); if (status < 0) usblp->wstatus = status; else @@ -335,7 +350,7 @@ static void usblp_bulk_write(struct urb *urb) usblp->no_paper = 0; usblp->wcomplete = 1; wake_up(&usblp->wwait); - spin_unlock(&usblp->lock); + spin_unlock_irqrestore(&usblp->lock, flags); usb_free_urb(urb); } @@ -352,7 +367,8 @@ static int usblp_check_status(struct usblp *usblp, int err) int error; mutex_lock(&usblp->mut); - if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) { + error = usblp_read_status(usblp, usblp->statusbuf); + if (error < 0) { mutex_unlock(&usblp->mut); printk_ratelimited(KERN_ERR "usblp%d: error %d reading printer status\n", @@ -448,6 +464,7 @@ static void usblp_cleanup(struct usblp *usblp) kfree(usblp->readbuf); kfree(usblp->device_id_string); kfree(usblp->statusbuf); + usb_put_intf(usblp->intf); kfree(usblp); } @@ -464,28 +481,39 @@ static int usblp_release(struct inode *inode, struct file *file) mutex_lock(&usblp_mutex); usblp->used = 0; - if (usblp->present) { + if (usblp->present) usblp_unlink_urbs(usblp); - usb_autopm_put_interface(usblp->intf); - } else /* finish cleanup from disconnect */ - usblp_cleanup(usblp); + + usb_autopm_put_interface(usblp->intf); + + if (!usblp->present) /* finish cleanup from disconnect */ + usblp_cleanup(usblp); /* any URBs must be dead */ + mutex_unlock(&usblp_mutex); return 0; } /* No kernel lock - fine */ -static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait) +static __poll_t usblp_poll(struct file *file, struct poll_table_struct *wait) { - int ret; + struct usblp *usblp = file->private_data; + __poll_t ret = 0; unsigned long flags; - struct usblp *usblp = file->private_data; /* Should we check file->f_mode & FMODE_WRITE before poll_wait()? */ poll_wait(file, &usblp->rwait, wait); poll_wait(file, &usblp->wwait, wait); + + mutex_lock(&usblp->mut); + if (!usblp->present) + ret |= EPOLLHUP; + mutex_unlock(&usblp->mut); + spin_lock_irqsave(&usblp->lock, flags); - ret = ((usblp->bidir && usblp->rcomplete) ? POLLIN | POLLRDNORM : 0) | - ((usblp->no_paper || usblp->wcomplete) ? POLLOUT | POLLWRNORM : 0); + if (usblp->bidir && usblp->rcomplete) + ret |= EPOLLIN | EPOLLRDNORM; + if (usblp->no_paper || usblp->wcomplete) + ret |= EPOLLOUT | EPOLLWRNORM; spin_unlock_irqrestore(&usblp->lock, flags); return ret; } @@ -661,7 +689,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case LPGETSTATUS: - if ((retval = usblp_read_status(usblp, usblp->statusbuf))) { + retval = usblp_read_status(usblp, usblp->statusbuf); + if (retval) { printk_ratelimited(KERN_ERR "usblp%d:" "failed reading printer status (%d)\n", usblp->minor, retval); @@ -694,9 +723,11 @@ static struct urb *usblp_new_writeurb(struct usblp *usblp, int transfer_length) struct urb *urb; char *writebuf; - if ((writebuf = kmalloc(transfer_length, GFP_KERNEL)) == NULL) + writebuf = kmalloc(transfer_length, GFP_KERNEL); + if (writebuf == NULL) return NULL; - if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) { kfree(writebuf); return NULL; } @@ -722,18 +753,21 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t rv = -EINTR; goto raise_biglock; } - if ((rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK))) < 0) + rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK)); + if (rv < 0) goto raise_wait; while (writecount < count) { /* * Step 1: Submit next block. */ - if ((transfer_length = count - writecount) > USBLP_BUF_SIZE) + transfer_length = count - writecount; + if (transfer_length > USBLP_BUF_SIZE) transfer_length = USBLP_BUF_SIZE; rv = -ENOMEM; - if ((writeurb = usblp_new_writeurb(usblp, transfer_length)) == NULL) + writeurb = usblp_new_writeurb(usblp, transfer_length); + if (writeurb == NULL) goto raise_urb; usb_anchor_urb(writeurb, &usblp->urbs); @@ -746,7 +780,9 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t spin_lock_irq(&usblp->lock); usblp->wcomplete = 0; spin_unlock_irq(&usblp->lock); - if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) { + + rv = usb_submit_urb(writeurb, GFP_KERNEL); + if (rv < 0) { usblp->wstatus = 0; spin_lock_irq(&usblp->lock); usblp->no_paper = 0; @@ -822,22 +858,29 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, lo if (rv < 0) return rv; - if ((avail = usblp->rstatus) < 0) { + if (!usblp->present) { + count = -ENODEV; + goto done; + } + + avail = usblp->rstatus; + if (avail < 0) { printk(KERN_ERR "usblp%d: error %d reading from printer\n", - usblp->minor, (int)avail); + usblp->minor, (int)avail); usblp_submit_read(usblp); count = -EIO; goto done; } - count = len < avail - usblp->readcount ? len : avail - usblp->readcount; + count = min_t(ssize_t, len, avail - usblp->readcount); if (count != 0 && copy_to_user(buffer, usblp->readbuf + usblp->readcount, count)) { count = -EFAULT; goto done; } - if ((usblp->readcount += count) == avail) { + usblp->readcount += count; + if (usblp->readcount == avail) { if (usblp_submit_read(usblp) < 0) { /* We don't want to leak USB return codes into errno. */ if (count == 0) @@ -870,11 +913,11 @@ static int usblp_wwait(struct usblp *usblp, int nonblock) add_wait_queue(&usblp->wwait, &waita); for (;;) { - set_current_state(TASK_INTERRUPTIBLE); if (mutex_lock_interruptible(&usblp->mut)) { rc = -EINTR; break; } + set_current_state(TASK_INTERRUPTIBLE); rc = usblp_wtest(usblp, nonblock); mutex_unlock(&usblp->mut); if (rc <= 0) @@ -938,7 +981,8 @@ static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock) break; } set_current_state(TASK_INTERRUPTIBLE); - if ((rc = usblp_rtest(usblp, nonblock)) < 0) { + rc = usblp_rtest(usblp, nonblock); + if (rc < 0) { mutex_unlock(&usblp->mut); break; } @@ -981,7 +1025,8 @@ static int usblp_submit_read(struct usblp *usblp) int rc; rc = -ENOMEM; - if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) goto raise_urb; usb_fill_bulk_urb(urb, usblp->dev, @@ -995,7 +1040,8 @@ static int usblp_submit_read(struct usblp *usblp) usblp->readcount = 0; /* XXX Why here? */ usblp->rcomplete = 0; spin_unlock_irqrestore(&usblp->lock, flags); - if ((rc = usb_submit_urb(urb, GFP_KERNEL)) < 0) { + rc = usb_submit_urb(urb, GFP_KERNEL); + if (rc < 0) { dev_dbg(&usblp->intf->dev, "error submitting urb (%d)\n", rc); spin_lock_irqsave(&usblp->lock, flags); usblp->rstatus = rc; @@ -1054,7 +1100,7 @@ static const struct file_operations usblp_fops = { .llseek = noop_llseek, }; -static char *usblp_devnode(struct device *dev, umode_t *mode) +static char *usblp_devnode(const struct device *dev, umode_t *mode) { return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); } @@ -1066,7 +1112,7 @@ static struct usb_class_driver usblp_class = { .minor_base = USBLP_MINOR_BASE, }; -static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t ieee1284_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct usblp *usblp = usb_get_intfdata(intf); @@ -1078,7 +1124,13 @@ static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribut return sprintf(buf, "%s", usblp->device_id_string+2); } -static DEVICE_ATTR(ieee1284_id, S_IRUGO, usblp_show_ieee1284_id, NULL); +static DEVICE_ATTR_RO(ieee1284_id); + +static struct attribute *usblp_attrs[] = { + &dev_attr_ieee1284_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(usblp); static int usblp_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -1103,12 +1155,13 @@ static int usblp_probe(struct usb_interface *intf, init_waitqueue_head(&usblp->wwait); init_usb_anchor(&usblp->urbs); usblp->ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - usblp->intf = intf; + usblp->intf = usb_get_intf(intf); /* Malloc device ID string buffer to the largest expected length, * since we can re-query it on an ioctl and a dynamic string * could change in length. */ - if (!(usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL))) { + usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL); + if (!usblp->device_id_string) { retval = -ENOMEM; goto abort; } @@ -1118,7 +1171,8 @@ static int usblp_probe(struct usb_interface *intf, * malloc both regardless of bidirectionality, because the * alternate setting can be changed later via an ioctl. */ - if (!(usblp->readbuf = kmalloc(USBLP_BUF_SIZE_IN, GFP_KERNEL))) { + usblp->readbuf = kmalloc(USBLP_BUF_SIZE_IN, GFP_KERNEL); + if (!usblp->readbuf) { retval = -ENOMEM; goto abort; } @@ -1154,9 +1208,6 @@ static int usblp_probe(struct usb_interface *intf, /* Retrieve and store the device ID string. */ usblp_cache_device_id_string(usblp); - retval = device_create_file(&intf->dev, &dev_attr_ieee1284_id); - if (retval) - goto abort_intfdata; #ifdef DEBUG usblp_check_status(usblp, 0); @@ -1187,11 +1238,11 @@ static int usblp_probe(struct usb_interface *intf, abort_intfdata: usb_set_intfdata(intf, NULL); - device_remove_file(&intf->dev, &dev_attr_ieee1284_id); abort: kfree(usblp->readbuf); kfree(usblp->statusbuf); kfree(usblp->device_id_string); + usb_put_intf(usblp->intf); kfree(usblp); abort_ret: return retval; @@ -1202,26 +1253,31 @@ abort_ret: * but our requirements are too intricate for simple match to handle. * * The "proto_bias" option may be used to specify the preferred protocol - * for all USB printers (1=7/1/1, 2=7/1/2, 3=7/1/3). If the device - * supports the preferred protocol, then we bind to it. + * for all USB printers (1=USB_CLASS_PRINTER/1/1, 2=USB_CLASS_PRINTER/1/2, + * 3=USB_CLASS_PRINTER/1/3). If the device supports the preferred protocol, + * then we bind to it. * - * The best interface for us is 7/1/2, because it is compatible - * with a stream of characters. If we find it, we bind to it. + * The best interface for us is USB_CLASS_PRINTER/1/2, because it + * is compatible with a stream of characters. If we find it, we bind to it. * * Note that the people from hpoj.sourceforge.net need to be able to - * bind to 7/1/3 (MLC/1284.4), so we provide them ioctls for this purpose. + * bind to USB_CLASS_PRINTER/1/3 (MLC/1284.4), so we provide them ioctls + * for this purpose. * - * Failing 7/1/2, we look for 7/1/3, even though it's probably not - * stream-compatible, because this matches the behaviour of the old code. + * Failing USB_CLASS_PRINTER/1/2, we look for USB_CLASS_PRINTER/1/3, + * even though it's probably not stream-compatible, because this matches + * the behaviour of the old code. * - * If nothing else, we bind to 7/1/1 - the unidirectional interface. + * If nothing else, we bind to USB_CLASS_PRINTER/1/1 + * - the unidirectional interface. */ static int usblp_select_alts(struct usblp *usblp) { struct usb_interface *if_alt; struct usb_host_interface *ifd; - struct usb_endpoint_descriptor *epd, *epwrite, *epread; - int p, i, e; + struct usb_endpoint_descriptor *epwrite, *epread; + int p, i; + int res; if_alt = usblp->intf; @@ -1232,7 +1288,8 @@ static int usblp_select_alts(struct usblp *usblp) for (i = 0; i < if_alt->num_altsetting; i++) { ifd = &if_alt->altsetting[i]; - if (ifd->desc.bInterfaceClass != 7 || ifd->desc.bInterfaceSubClass != 1) + if (ifd->desc.bInterfaceClass != USB_CLASS_PRINTER || + ifd->desc.bInterfaceSubClass != 1) if (!(usblp->quirks & USBLP_QUIRK_BAD_CLASS)) continue; @@ -1240,29 +1297,21 @@ static int usblp_select_alts(struct usblp *usblp) ifd->desc.bInterfaceProtocol > USBLP_LAST_PROTOCOL) continue; - /* Look for bulk OUT and IN endpoints. */ - epwrite = epread = NULL; - for (e = 0; e < ifd->desc.bNumEndpoints; e++) { - epd = &ifd->endpoint[e].desc; - - if (usb_endpoint_is_bulk_out(epd)) - if (!epwrite) - epwrite = epd; - - if (usb_endpoint_is_bulk_in(epd)) - if (!epread) - epread = epd; + /* Look for the expected bulk endpoints. */ + if (ifd->desc.bInterfaceProtocol > 1) { + res = usb_find_common_endpoints(ifd, + &epread, &epwrite, NULL, NULL); + } else { + epread = NULL; + res = usb_find_bulk_out_endpoint(ifd, &epwrite); } /* Ignore buggy hardware without the right endpoints. */ - if (!epwrite || (ifd->desc.bInterfaceProtocol > 1 && !epread)) + if (res) continue; - /* Turn off reads for 7/1/1 (unidirectional) interfaces - * and buggy bidirectional printers. */ - if (ifd->desc.bInterfaceProtocol == 1) { - epread = NULL; - } else if (usblp->quirks & USBLP_QUIRK_BIDIR) { + /* Turn off reads for buggy bidirectional printers. */ + if (usblp->quirks & USBLP_QUIRK_BIDIR) { printk(KERN_INFO "usblp%d: Disabling reads from " "problematic bidirectional printer\n", usblp->minor); @@ -1303,11 +1352,15 @@ static int usblp_set_protocol(struct usblp *usblp, int protocol) alts = usblp->protocol[protocol].alt_setting; if (alts < 0) return -EINVAL; - r = usb_set_interface(usblp->dev, usblp->ifnum, alts); - if (r < 0) { - printk(KERN_ERR "usblp: can't set desired altsetting %d on interface %d\n", - alts, usblp->ifnum); - return r; + + /* Don't unnecessarily set the interface if there's a single alt. */ + if (usblp->intf->num_altsetting > 1) { + r = usb_set_interface(usblp->dev, usblp->ifnum, alts); + if (r < 0) { + printk(KERN_ERR "usblp: can't set desired altsetting %d on interface %d\n", + alts, usblp->ifnum); + return r; + } } usblp->bidir = (usblp->protocol[protocol].epread != NULL); @@ -1360,8 +1413,6 @@ static void usblp_disconnect(struct usb_interface *intf) BUG(); } - device_remove_file(&intf->dev, &dev_attr_ieee1284_id); - mutex_lock(&usblp_mutex); mutex_lock(&usblp->mut); usblp->present = 0; @@ -1371,9 +1422,11 @@ static void usblp_disconnect(struct usb_interface *intf) usblp_unlink_urbs(usblp); mutex_unlock(&usblp->mut); + usb_poison_anchored_urbs(&usblp->urbs); if (!usblp->used) usblp_cleanup(usblp); + mutex_unlock(&usblp_mutex); } @@ -1402,12 +1455,12 @@ static int usblp_resume(struct usb_interface *intf) } static const struct usb_device_id usblp_ids[] = { - { USB_DEVICE_INFO(7, 1, 1) }, - { USB_DEVICE_INFO(7, 1, 2) }, - { USB_DEVICE_INFO(7, 1, 3) }, - { USB_INTERFACE_INFO(7, 1, 1) }, - { USB_INTERFACE_INFO(7, 1, 2) }, - { USB_INTERFACE_INFO(7, 1, 3) }, + { USB_DEVICE_INFO(USB_CLASS_PRINTER, 1, 1) }, + { USB_DEVICE_INFO(USB_CLASS_PRINTER, 1, 2) }, + { USB_DEVICE_INFO(USB_CLASS_PRINTER, 1, 3) }, + { USB_INTERFACE_INFO(USB_CLASS_PRINTER, 1, 1) }, + { USB_INTERFACE_INFO(USB_CLASS_PRINTER, 1, 2) }, + { USB_INTERFACE_INFO(USB_CLASS_PRINTER, 1, 3) }, { USB_DEVICE(0x04b8, 0x0202) }, /* Seiko Epson Receipt Printer M129C */ { } /* Terminating entry */ }; @@ -1421,6 +1474,7 @@ static struct usb_driver usblp_driver = { .suspend = usblp_suspend, .resume = usblp_resume, .id_table = usblp_ids, + .dev_groups = usblp_groups, .supports_autosuspend = 1, }; diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 609dbc2f7151..206f1b738ed3 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1,49 +1,45 @@ -/** +// SPDX-License-Identifier: GPL-2.0+ +/* * drivers/usb/class/usbtmc.c - USB Test & Measurement 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. + * Copyright (C) 2018 IVI Foundation, Inc. */ -#include <linux/init.h> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/kref.h> #include <linux/slab.h> +#include <linux/poll.h> #include <linux/mutex.h> #include <linux/usb.h> +#include <linux/compat.h> #include <linux/usb/tmc.h> +/* Increment API VERSION when changing tmc.h with new flags or ioctls + * or when changing a significant behavior of the driver. + */ +#define USBTMC_API_VERSION (3) -#define RIGOL 1 #define USBTMC_HEADER_SIZE 12 #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 - +/* Minimum USB timeout (in milliseconds) */ +#define USBTMC_MIN_TIMEOUT 100 /* Default USB timeout (in milliseconds) */ #define USBTMC_TIMEOUT 5000 +/* Max number of urbs used in write transfers */ +#define MAX_URBS_IN_FLIGHT 16 +/* I/O buffer size used in generic read/write functions */ +#define USBTMC_BUFSIZE (4096) + /* * 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 @@ -78,6 +74,7 @@ struct usbtmc_device_data { const struct usb_device_id *id; struct usb_device *usb_dev; struct usb_interface *intf; + struct list_head file_list; unsigned int bulk_in; unsigned int bulk_out; @@ -86,33 +83,74 @@ struct usbtmc_device_data { u8 bTag_last_write; /* needed for abort */ u8 bTag_last_read; /* needed for abort */ - u8 rigol_quirk; - - /* attributes from the USB TMC spec for this device */ - u8 TermChar; - bool TermCharEnabled; - bool auto_abort; + /* packet size of IN bulk */ + u16 wMaxPacketSize; + + /* data for interrupt in endpoint handling */ + u8 bNotify1; + u8 bNotify2; + u16 ifnum; + u8 iin_bTag; + u8 *iin_buffer; + atomic_t iin_data_valid; + unsigned int iin_ep; + int iin_ep_present; + int iin_interval; + struct urb *iin_urb; + u16 iin_wMaxPacketSize; + + /* coalesced usb488_caps from usbtmc_dev_capabilities */ + __u8 usb488_caps; bool zombie; /* fd of disconnected device */ struct usbtmc_dev_capabilities capabilities; struct kref kref; struct mutex io_mutex; /* only one i/o function running at a time */ + wait_queue_head_t waitq; + struct fasync_struct *fasync; + spinlock_t dev_lock; /* lock for file_list */ }; #define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref) -struct usbtmc_ID_rigol_quirk { - __u16 idVendor; - __u16 idProduct; -}; - -static const struct usbtmc_ID_rigol_quirk usbtmc_id_quirk[] = { - { 0x1ab1, 0x0588 }, - { 0, 0 } +/* + * This structure holds private data for each USBTMC file handle. + */ +struct usbtmc_file_data { + struct usbtmc_device_data *data; + struct list_head file_elem; + + u32 timeout; + u8 srq_byte; + atomic_t srq_asserted; + atomic_t closing; + u8 bmTransferAttributes; /* member of DEV_DEP_MSG_IN */ + + u8 eom_val; + u8 term_char; + bool term_char_enabled; + bool auto_abort; + + spinlock_t err_lock; /* lock for errors */ + + struct usb_anchor submitted; + + /* data for generic_write */ + struct semaphore limit_write_sem; + u32 out_transfer_size; + int out_status; + + /* data for generic_read */ + u32 in_transfer_size; + int in_status; + int in_urbs_used; + struct usb_anchor in_anchor; + wait_queue_head_t wait_bulk_in; }; /* Forward declarations */ static struct usb_driver usbtmc_driver; +static void usbtmc_draw_down(struct usbtmc_file_data *file_data); static void usbtmc_delete(struct kref *kref) { @@ -126,46 +164,115 @@ static int usbtmc_open(struct inode *inode, struct file *filp) { struct usb_interface *intf; struct usbtmc_device_data *data; - int retval = 0; + struct usbtmc_file_data *file_data; intf = usb_find_interface(&usbtmc_driver, iminor(inode)); if (!intf) { - printk(KERN_ERR KBUILD_MODNAME - ": can not find device for minor %d", iminor(inode)); - retval = -ENODEV; - goto exit; + pr_err("can not find device for minor %d", iminor(inode)); + return -ENODEV; } + file_data = kzalloc(sizeof(*file_data), GFP_KERNEL); + if (!file_data) + return -ENOMEM; + + spin_lock_init(&file_data->err_lock); + sema_init(&file_data->limit_write_sem, MAX_URBS_IN_FLIGHT); + init_usb_anchor(&file_data->submitted); + init_usb_anchor(&file_data->in_anchor); + init_waitqueue_head(&file_data->wait_bulk_in); + data = usb_get_intfdata(intf); + /* Protect reference to data from file structure until release */ kref_get(&data->kref); + mutex_lock(&data->io_mutex); + file_data->data = data; + + atomic_set(&file_data->closing, 0); + + file_data->timeout = USBTMC_TIMEOUT; + file_data->term_char = '\n'; + file_data->term_char_enabled = 0; + file_data->auto_abort = 0; + file_data->eom_val = 1; + + INIT_LIST_HEAD(&file_data->file_elem); + spin_lock_irq(&data->dev_lock); + list_add_tail(&file_data->file_elem, &data->file_list); + spin_unlock_irq(&data->dev_lock); + mutex_unlock(&data->io_mutex); + /* Store pointer in file structure's private data field */ - filp->private_data = data; + filp->private_data = file_data; -exit: - return retval; + return 0; +} + +/* + * usbtmc_flush - called before file handle is closed + */ +static int usbtmc_flush(struct file *file, fl_owner_t id) +{ + struct usbtmc_file_data *file_data; + struct usbtmc_device_data *data; + + file_data = file->private_data; + if (file_data == NULL) + return -ENODEV; + + atomic_set(&file_data->closing, 1); + data = file_data->data; + + /* wait for io to stop */ + mutex_lock(&data->io_mutex); + + usbtmc_draw_down(file_data); + + spin_lock_irq(&file_data->err_lock); + file_data->in_status = 0; + file_data->in_transfer_size = 0; + file_data->in_urbs_used = 0; + file_data->out_status = 0; + file_data->out_transfer_size = 0; + spin_unlock_irq(&file_data->err_lock); + + wake_up_interruptible_all(&data->waitq); + mutex_unlock(&data->io_mutex); + + return 0; } static int usbtmc_release(struct inode *inode, struct file *file) { - struct usbtmc_device_data *data = file->private_data; + struct usbtmc_file_data *file_data = file->private_data; - kref_put(&data->kref, usbtmc_delete); + /* prevent IO _AND_ usbtmc_interrupt */ + mutex_lock(&file_data->data->io_mutex); + spin_lock_irq(&file_data->data->dev_lock); + + list_del(&file_data->file_elem); + + spin_unlock_irq(&file_data->data->dev_lock); + mutex_unlock(&file_data->data->io_mutex); + + kref_put(&file_data->data->kref, usbtmc_delete); + file_data->data = NULL; + kfree(file_data); return 0; } -static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data) +static int usbtmc_ioctl_abort_bulk_in_tag(struct usbtmc_device_data *data, + u8 tag) { u8 *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); + buffer = kmalloc(USBTMC_BUFSIZE, GFP_KERNEL); if (!buffer) return -ENOMEM; @@ -173,86 +280,88 @@ static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data) 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); + tag, data->bulk_in, + buffer, 2, USB_CTRL_GET_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]); + dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x with tag %02x\n", + buffer[0], buffer[1]); if (buffer[0] == USBTMC_STATUS_FAILED) { + /* No transfer in progress and the Bulk-OUT FIFO is empty. */ rv = 0; goto exit; } - if (buffer[0] != USBTMC_STATUS_SUCCESS) { - dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", - buffer[0]); - rv = -EPERM; + if (buffer[0] == USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS) { + /* The device returns this status if either: + * - There is a transfer in progress, but the specified bTag + * does not match. + * - There is no transfer in progress, but the Bulk-OUT FIFO + * is not empty. + */ + rv = -ENOMSG; 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 = usb_endpoint_maxp(¤t_setting->endpoint[n].desc); - - if (max_size == 0) { - dev_err(dev, "Couldn't get wMaxPacketSize\n"); + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", + buffer[0]); 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"); +usbtmc_abort_bulk_in_status: + 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); + /* Data must be present. So use low timeout 300 ms */ + actual = 0; + rv = usb_bulk_msg(data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, + data->bulk_in), + buffer, USBTMC_BUFSIZE, + &actual, 300); - n++; + print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE, 16, 1, + buffer, actual, true); - if (rv < 0) { - dev_err(dev, "usb_bulk_msg returned %d\n", rv); + n++; + + if (rv < 0) { + dev_err(dev, "usb_bulk_msg returned %d\n", rv); + if (rv != -ETIMEDOUT) goto exit; - } - } while ((actual == max_size) && - (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + } + + if (actual == USBTMC_BUFSIZE) + goto usbtmc_abort_bulk_in_status; - if (actual == max_size) { + if (n >= USBTMC_MAX_READS_TO_CLEAR_BULK_IN) { 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); + USB_CTRL_GET_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]); + dev_dbg(dev, "CHECK_ABORT_BULK_IN returned %x\n", buffer[0]); if (buffer[0] == USBTMC_STATUS_SUCCESS) { rv = 0; @@ -260,46 +369,30 @@ usbtmc_abort_bulk_in_status: } if (buffer[0] != USBTMC_STATUS_PENDING) { - dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]); + dev_err(dev, "CHECK_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; + if ((buffer[1] & 1) > 0) { + /* The device has 1 or more queued packets the Host can read */ + goto usbtmc_abort_bulk_in_status; } - goto usbtmc_abort_bulk_in_status; - + /* The Host must send CHECK_ABORT_BULK_IN_STATUS at a later time. */ + rv = -EAGAIN; exit: kfree(buffer); return rv; +} +static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data) +{ + return usbtmc_ioctl_abort_bulk_in_tag(data, data->bTag_last_read); } -static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data) +static int usbtmc_ioctl_abort_bulk_out_tag(struct usbtmc_device_data *data, + u8 tag) { struct device *dev; u8 *buffer; @@ -316,8 +409,8 @@ static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data) 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); + tag, data->bulk_out, + buffer, 2, USB_CTRL_GET_TIMEOUT); if (rv < 0) { dev_err(dev, "usb_control_msg returned %d\n", rv); @@ -336,12 +429,14 @@ static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data) n = 0; usbtmc_abort_bulk_out_check_status: + /* do not stress device with subsequent requests */ + msleep(50); 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); + USB_CTRL_GET_TIMEOUT); n++; if (rv < 0) { dev_err(dev, "usb_control_msg returned %d\n", rv); @@ -375,34 +470,879 @@ exit: return rv; } +static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data) +{ + return usbtmc_ioctl_abort_bulk_out_tag(data, data->bTag_last_write); +} + +static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb) +{ + struct usbtmc_device_data *data = file_data->data; + struct device *dev = &data->intf->dev; + u8 *buffer; + u8 tag; + int rv; + long wait_rv; + unsigned long expire; + + dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n", + data->iin_ep_present); + + buffer = kmalloc(8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + atomic_set(&data->iin_data_valid, 0); + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + USBTMC488_REQUEST_READ_STATUS_BYTE, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + data->iin_bTag, + data->ifnum, + buffer, 0x03, USB_CTRL_GET_TIMEOUT); + if (rv < 0) { + dev_err(dev, "stb usb_control_msg returned %d\n", rv); + goto exit; + } + + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "control status returned %x\n", buffer[0]); + rv = -EIO; + goto exit; + } + + if (data->iin_ep_present) { + expire = msecs_to_jiffies(file_data->timeout); + wait_rv = wait_event_interruptible_timeout( + data->waitq, + atomic_read(&data->iin_data_valid) != 0, + expire); + if (wait_rv < 0) { + dev_dbg(dev, "wait interrupted %ld\n", wait_rv); + rv = wait_rv; + goto exit; + } + + if (wait_rv == 0) { + dev_dbg(dev, "wait timed out\n"); + rv = -ETIMEDOUT; + goto exit; + } + + tag = data->bNotify1 & 0x7f; + if (tag != data->iin_bTag) { + dev_err(dev, "expected bTag %x got %x\n", + data->iin_bTag, tag); + } + + *stb = data->bNotify2; + } else { + *stb = buffer[2]; + } + + dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)*stb, rv); + + rv = 0; + + exit: + /* bump interrupt bTag */ + data->iin_bTag += 1; + if (data->iin_bTag > 127) + /* 1 is for SRQ see USBTMC-USB488 subclass spec section 4.3.1 */ + data->iin_bTag = 2; + + kfree(buffer); + return rv; +} + +static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data, + void __user *arg) +{ + int srq_asserted = 0; + __u8 stb; + int rv; + + rv = usbtmc_get_stb(file_data, &stb); + + if (rv < 0) + return rv; + + srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted); + if (srq_asserted) + stb |= 0x40; /* Set RQS bit */ + + rv = put_user(stb, (__u8 __user *)arg); + + return rv; + +} + +static int usbtmc_ioctl_get_srq_stb(struct usbtmc_file_data *file_data, + void __user *arg) +{ + struct usbtmc_device_data *data = file_data->data; + struct device *dev = &data->intf->dev; + int srq_asserted = 0; + __u8 stb = 0; + int rv; + + spin_lock_irq(&data->dev_lock); + srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted); + + if (srq_asserted) { + stb = file_data->srq_byte; + spin_unlock_irq(&data->dev_lock); + rv = put_user(stb, (__u8 __user *)arg); + } else { + spin_unlock_irq(&data->dev_lock); + rv = -ENOMSG; + } + + dev_dbg(dev, "stb:0x%02x with srq received %d\n", (unsigned int)stb, rv); + + return rv; +} + +static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data, + __u32 __user *arg) +{ + struct usbtmc_device_data *data = file_data->data; + struct device *dev = &data->intf->dev; + u32 timeout; + unsigned long expire; + long wait_rv; + + if (!data->iin_ep_present) { + dev_dbg(dev, "no interrupt endpoint present\n"); + return -EFAULT; + } + + if (get_user(timeout, arg)) + return -EFAULT; + + expire = msecs_to_jiffies(timeout); + + mutex_unlock(&data->io_mutex); + + wait_rv = wait_event_interruptible_timeout( + data->waitq, + atomic_read(&file_data->srq_asserted) != 0 || + atomic_read(&file_data->closing), + expire); + + mutex_lock(&data->io_mutex); + + /* Note! disconnect or close could be called in the meantime */ + if (atomic_read(&file_data->closing) || data->zombie) + return -ENODEV; + + if (wait_rv < 0) { + dev_dbg(dev, "%s - wait interrupted %ld\n", __func__, wait_rv); + return wait_rv; + } + + if (wait_rv == 0) { + dev_dbg(dev, "%s - wait timed out\n", __func__); + return -ETIMEDOUT; + } + + dev_dbg(dev, "%s - srq asserted\n", __func__); + return 0; +} + +static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data, + void __user *arg, unsigned int cmd) +{ + struct device *dev = &data->intf->dev; + __u8 val; + u8 *buffer; + u16 wValue; + int rv; + + if (!(data->usb488_caps & USBTMC488_CAPABILITY_SIMPLE)) + return -EINVAL; + + buffer = kmalloc(8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + if (cmd == USBTMC488_REQUEST_REN_CONTROL) { + rv = copy_from_user(&val, arg, sizeof(val)); + if (rv) { + rv = -EFAULT; + goto exit; + } + wValue = val ? 1 : 0; + } else { + wValue = 0; + } + + rv = usb_control_msg(data->usb_dev, + usb_rcvctrlpipe(data->usb_dev, 0), + cmd, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + wValue, + data->ifnum, + buffer, 0x01, USB_CTRL_GET_TIMEOUT); + if (rv < 0) { + dev_err(dev, "simple usb_control_msg failed %d\n", rv); + goto exit; + } else if (rv != 1) { + dev_warn(dev, "simple usb_control_msg returned %d\n", rv); + rv = -EIO; + goto exit; + } + + if (buffer[0] != USBTMC_STATUS_SUCCESS) { + dev_err(dev, "simple control status returned %x\n", buffer[0]); + rv = -EIO; + goto exit; + } + rv = 0; + + exit: + kfree(buffer); + return rv; +} + +/* + * Sends a TRIGGER Bulk-OUT command message + * See the USBTMC-USB488 specification, Table 2. + * + * Also updates bTag_last_write. + */ +static int usbtmc488_ioctl_trigger(struct usbtmc_file_data *file_data) +{ + struct usbtmc_device_data *data = file_data->data; + int retval; + u8 *buffer; + int actual; + + buffer = kzalloc(USBTMC_HEADER_SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = 128; + buffer[1] = data->bTag; + buffer[2] = ~data->bTag; + + retval = usb_bulk_msg(data->usb_dev, + usb_sndbulkpipe(data->usb_dev, + data->bulk_out), + buffer, USBTMC_HEADER_SIZE, + &actual, file_data->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++; + + kfree(buffer); + if (retval < 0) { + dev_err(&data->intf->dev, "%s returned %d\n", + __func__, retval); + return retval; + } + + return 0; +} + +static struct urb *usbtmc_create_urb(void) +{ + const size_t bufsize = USBTMC_BUFSIZE; + u8 *dmabuf = NULL; + struct urb *urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!urb) + return NULL; + + dmabuf = kzalloc(bufsize, GFP_KERNEL); + if (!dmabuf) { + usb_free_urb(urb); + return NULL; + } + + urb->transfer_buffer = dmabuf; + urb->transfer_buffer_length = bufsize; + urb->transfer_flags |= URB_FREE_BUFFER; + return urb; +} + +static void usbtmc_read_bulk_cb(struct urb *urb) +{ + struct usbtmc_file_data *file_data = urb->context; + int status = urb->status; + unsigned long flags; + + /* sync/async unlink faults aren't errors */ + if (status) { + if (!(/* status == -ENOENT || */ + status == -ECONNRESET || + status == -EREMOTEIO || /* Short packet */ + status == -ESHUTDOWN)) + dev_err(&file_data->data->intf->dev, + "%s - nonzero read bulk status received: %d\n", + __func__, status); + + spin_lock_irqsave(&file_data->err_lock, flags); + if (!file_data->in_status) + file_data->in_status = status; + spin_unlock_irqrestore(&file_data->err_lock, flags); + } + + spin_lock_irqsave(&file_data->err_lock, flags); + file_data->in_transfer_size += urb->actual_length; + dev_dbg(&file_data->data->intf->dev, + "%s - total size: %u current: %d status: %d\n", + __func__, file_data->in_transfer_size, + urb->actual_length, status); + spin_unlock_irqrestore(&file_data->err_lock, flags); + usb_anchor_urb(urb, &file_data->in_anchor); + + wake_up_interruptible(&file_data->wait_bulk_in); + wake_up_interruptible(&file_data->data->waitq); +} + +static inline bool usbtmc_do_transfer(struct usbtmc_file_data *file_data) +{ + bool data_or_error; + + spin_lock_irq(&file_data->err_lock); + data_or_error = !usb_anchor_empty(&file_data->in_anchor) + || file_data->in_status; + spin_unlock_irq(&file_data->err_lock); + dev_dbg(&file_data->data->intf->dev, "%s: returns %d\n", __func__, + data_or_error); + return data_or_error; +} + +static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data, + void __user *user_buffer, + u32 transfer_size, + u32 *transferred, + u32 flags) +{ + struct usbtmc_device_data *data = file_data->data; + struct device *dev = &data->intf->dev; + u32 done = 0; + u32 remaining; + const u32 bufsize = USBTMC_BUFSIZE; + int retval = 0; + u32 max_transfer_size; + unsigned long expire; + int bufcount = 1; + int again = 0; + long wait_rv; + + /* mutex already locked */ + + *transferred = done; + + max_transfer_size = transfer_size; + + if (flags & USBTMC_FLAG_IGNORE_TRAILER) { + /* The device may send extra alignment bytes (up to + * wMaxPacketSize – 1) to avoid sending a zero-length + * packet + */ + remaining = transfer_size; + if ((max_transfer_size % data->wMaxPacketSize) == 0) + max_transfer_size += (data->wMaxPacketSize - 1); + } else { + /* round down to bufsize to avoid truncated data left */ + if (max_transfer_size > bufsize) { + max_transfer_size = + roundup(max_transfer_size + 1 - bufsize, + bufsize); + } + remaining = max_transfer_size; + } + + spin_lock_irq(&file_data->err_lock); + + if (file_data->in_status) { + /* return the very first error */ + retval = file_data->in_status; + spin_unlock_irq(&file_data->err_lock); + goto error; + } + + if (flags & USBTMC_FLAG_ASYNC) { + if (usb_anchor_empty(&file_data->in_anchor)) + again = 1; + + if (file_data->in_urbs_used == 0) { + file_data->in_transfer_size = 0; + file_data->in_status = 0; + } + } else { + file_data->in_transfer_size = 0; + file_data->in_status = 0; + } + + if (max_transfer_size == 0) { + bufcount = 0; + } else { + bufcount = roundup(max_transfer_size, bufsize) / bufsize; + if (bufcount > file_data->in_urbs_used) + bufcount -= file_data->in_urbs_used; + else + bufcount = 0; + + if (bufcount + file_data->in_urbs_used > MAX_URBS_IN_FLIGHT) { + bufcount = MAX_URBS_IN_FLIGHT - + file_data->in_urbs_used; + } + } + spin_unlock_irq(&file_data->err_lock); + + dev_dbg(dev, "%s: requested=%u flags=0x%X size=%u bufs=%d used=%d\n", + __func__, transfer_size, flags, + max_transfer_size, bufcount, file_data->in_urbs_used); + + while (bufcount > 0) { + u8 *dmabuf = NULL; + struct urb *urb = usbtmc_create_urb(); + + if (!urb) { + retval = -ENOMEM; + goto error; + } + + dmabuf = urb->transfer_buffer; + + usb_fill_bulk_urb(urb, data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, data->bulk_in), + dmabuf, bufsize, + usbtmc_read_bulk_cb, file_data); + + usb_anchor_urb(urb, &file_data->submitted); + retval = usb_submit_urb(urb, GFP_KERNEL); + /* urb is anchored. We can release our reference. */ + usb_free_urb(urb); + if (unlikely(retval)) { + usb_unanchor_urb(urb); + goto error; + } + file_data->in_urbs_used++; + bufcount--; + } + + if (again) { + dev_dbg(dev, "%s: ret=again\n", __func__); + return -EAGAIN; + } + + if (user_buffer == NULL) + return -EINVAL; + + expire = msecs_to_jiffies(file_data->timeout); + + while (max_transfer_size > 0) { + u32 this_part; + struct urb *urb = NULL; + + if (!(flags & USBTMC_FLAG_ASYNC)) { + dev_dbg(dev, "%s: before wait time %lu\n", + __func__, expire); + wait_rv = wait_event_interruptible_timeout( + file_data->wait_bulk_in, + usbtmc_do_transfer(file_data), + expire); + + dev_dbg(dev, "%s: wait returned %ld\n", + __func__, wait_rv); + + if (wait_rv < 0) { + retval = wait_rv; + goto error; + } + + if (wait_rv == 0) { + retval = -ETIMEDOUT; + goto error; + } + + } + + urb = usb_get_from_anchor(&file_data->in_anchor); + if (!urb) { + if (!(flags & USBTMC_FLAG_ASYNC)) { + /* synchronous case: must not happen */ + retval = -EFAULT; + goto error; + } + + /* asynchronous case: ready, do not block or wait */ + *transferred = done; + dev_dbg(dev, "%s: (async) done=%u ret=0\n", + __func__, done); + return 0; + } + + file_data->in_urbs_used--; + + if (max_transfer_size > urb->actual_length) + max_transfer_size -= urb->actual_length; + else + max_transfer_size = 0; + + if (remaining > urb->actual_length) + this_part = urb->actual_length; + else + this_part = remaining; + + print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE, 16, 1, + urb->transfer_buffer, urb->actual_length, true); + + if (copy_to_user(user_buffer + done, + urb->transfer_buffer, this_part)) { + usb_free_urb(urb); + retval = -EFAULT; + goto error; + } + + remaining -= this_part; + done += this_part; + + spin_lock_irq(&file_data->err_lock); + if (urb->status) { + /* return the very first error */ + retval = file_data->in_status; + spin_unlock_irq(&file_data->err_lock); + usb_free_urb(urb); + goto error; + } + spin_unlock_irq(&file_data->err_lock); + + if (urb->actual_length < bufsize) { + /* short packet or ZLP received => ready */ + usb_free_urb(urb); + retval = 1; + break; + } + + if (!(flags & USBTMC_FLAG_ASYNC) && + max_transfer_size > (bufsize * file_data->in_urbs_used)) { + /* resubmit, since other buffers still not enough */ + usb_anchor_urb(urb, &file_data->submitted); + retval = usb_submit_urb(urb, GFP_KERNEL); + if (unlikely(retval)) { + usb_unanchor_urb(urb); + usb_free_urb(urb); + goto error; + } + file_data->in_urbs_used++; + } + usb_free_urb(urb); + retval = 0; + } + +error: + *transferred = done; + + dev_dbg(dev, "%s: before kill\n", __func__); + /* Attention: killing urbs can take long time (2 ms) */ + usb_kill_anchored_urbs(&file_data->submitted); + dev_dbg(dev, "%s: after kill\n", __func__); + usb_scuttle_anchored_urbs(&file_data->in_anchor); + file_data->in_urbs_used = 0; + file_data->in_status = 0; /* no spinlock needed here */ + dev_dbg(dev, "%s: done=%u ret=%d\n", __func__, done, retval); + + return retval; +} + +static ssize_t usbtmc_ioctl_generic_read(struct usbtmc_file_data *file_data, + void __user *arg) +{ + struct usbtmc_message msg; + ssize_t retval = 0; + + /* mutex already locked */ + + if (copy_from_user(&msg, arg, sizeof(struct usbtmc_message))) + return -EFAULT; + + retval = usbtmc_generic_read(file_data, msg.message, + msg.transfer_size, &msg.transferred, + msg.flags); + + if (put_user(msg.transferred, + &((struct usbtmc_message __user *)arg)->transferred)) + return -EFAULT; + + return retval; +} + +static void usbtmc_write_bulk_cb(struct urb *urb) +{ + struct usbtmc_file_data *file_data = urb->context; + int wakeup = 0; + unsigned long flags; + + spin_lock_irqsave(&file_data->err_lock, flags); + file_data->out_transfer_size += urb->actual_length; + + /* sync/async unlink faults aren't errors */ + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + dev_err(&file_data->data->intf->dev, + "%s - nonzero write bulk status received: %d\n", + __func__, urb->status); + + if (!file_data->out_status) { + file_data->out_status = urb->status; + wakeup = 1; + } + } + spin_unlock_irqrestore(&file_data->err_lock, flags); + + dev_dbg(&file_data->data->intf->dev, + "%s - write bulk total size: %u\n", + __func__, file_data->out_transfer_size); + + up(&file_data->limit_write_sem); + if (usb_anchor_empty(&file_data->submitted) || wakeup) + wake_up_interruptible(&file_data->data->waitq); +} + +static ssize_t usbtmc_generic_write(struct usbtmc_file_data *file_data, + const void __user *user_buffer, + u32 transfer_size, + u32 *transferred, + u32 flags) +{ + struct usbtmc_device_data *data = file_data->data; + struct device *dev; + u32 done = 0; + u32 remaining; + unsigned long expire; + const u32 bufsize = USBTMC_BUFSIZE; + struct urb *urb = NULL; + int retval = 0; + u32 timeout; + + *transferred = 0; + + /* Get pointer to private data structure */ + dev = &data->intf->dev; + + dev_dbg(dev, "%s: size=%u flags=0x%X sema=%u\n", + __func__, transfer_size, flags, + file_data->limit_write_sem.count); + + if (flags & USBTMC_FLAG_APPEND) { + spin_lock_irq(&file_data->err_lock); + retval = file_data->out_status; + spin_unlock_irq(&file_data->err_lock); + if (retval < 0) + return retval; + } else { + spin_lock_irq(&file_data->err_lock); + file_data->out_transfer_size = 0; + file_data->out_status = 0; + spin_unlock_irq(&file_data->err_lock); + } + + remaining = transfer_size; + if (remaining > INT_MAX) + remaining = INT_MAX; + + timeout = file_data->timeout; + expire = msecs_to_jiffies(timeout); + + while (remaining > 0) { + u32 this_part, aligned; + u8 *buffer = NULL; + + if (flags & USBTMC_FLAG_ASYNC) { + if (down_trylock(&file_data->limit_write_sem)) { + retval = (done)?(0):(-EAGAIN); + goto exit; + } + } else { + retval = down_timeout(&file_data->limit_write_sem, + expire); + if (retval < 0) { + retval = -ETIMEDOUT; + goto error; + } + } + + spin_lock_irq(&file_data->err_lock); + retval = file_data->out_status; + spin_unlock_irq(&file_data->err_lock); + if (retval < 0) { + up(&file_data->limit_write_sem); + goto error; + } + + /* prepare next urb to send */ + urb = usbtmc_create_urb(); + if (!urb) { + retval = -ENOMEM; + up(&file_data->limit_write_sem); + goto error; + } + buffer = urb->transfer_buffer; + + if (remaining > bufsize) + this_part = bufsize; + else + this_part = remaining; + + if (copy_from_user(buffer, user_buffer + done, this_part)) { + retval = -EFAULT; + up(&file_data->limit_write_sem); + goto error; + } + + print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE, + 16, 1, buffer, this_part, true); + + /* fill bulk with 32 bit alignment to meet USBTMC specification + * (size + 3 & ~3) rounds up and simplifies user code + */ + aligned = (this_part + 3) & ~3; + dev_dbg(dev, "write(size:%u align:%u done:%u)\n", + (unsigned int)this_part, + (unsigned int)aligned, + (unsigned int)done); + + usb_fill_bulk_urb(urb, data->usb_dev, + usb_sndbulkpipe(data->usb_dev, data->bulk_out), + urb->transfer_buffer, aligned, + usbtmc_write_bulk_cb, file_data); + + usb_anchor_urb(urb, &file_data->submitted); + retval = usb_submit_urb(urb, GFP_KERNEL); + if (unlikely(retval)) { + usb_unanchor_urb(urb); + up(&file_data->limit_write_sem); + goto error; + } + + usb_free_urb(urb); + urb = NULL; /* urb will be finally released by usb driver */ + + remaining -= this_part; + done += this_part; + } + + /* All urbs are on the fly */ + if (!(flags & USBTMC_FLAG_ASYNC)) { + if (!usb_wait_anchor_empty_timeout(&file_data->submitted, + timeout)) { + retval = -ETIMEDOUT; + goto error; + } + } + + retval = 0; + goto exit; + +error: + usb_kill_anchored_urbs(&file_data->submitted); +exit: + usb_free_urb(urb); + + spin_lock_irq(&file_data->err_lock); + if (!(flags & USBTMC_FLAG_ASYNC)) + done = file_data->out_transfer_size; + if (!retval && file_data->out_status) + retval = file_data->out_status; + spin_unlock_irq(&file_data->err_lock); + + *transferred = done; + + dev_dbg(dev, "%s: done=%u, retval=%d, urbstat=%d\n", + __func__, done, retval, file_data->out_status); + + return retval; +} + +static ssize_t usbtmc_ioctl_generic_write(struct usbtmc_file_data *file_data, + void __user *arg) +{ + struct usbtmc_message msg; + ssize_t retval = 0; + + /* mutex already locked */ + + if (copy_from_user(&msg, arg, sizeof(struct usbtmc_message))) + return -EFAULT; + + retval = usbtmc_generic_write(file_data, msg.message, + msg.transfer_size, &msg.transferred, + msg.flags); + + if (put_user(msg.transferred, + &((struct usbtmc_message __user *)arg)->transferred)) + return -EFAULT; + + return retval; +} + /* - * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-IN endpoint. + * Get the generic write result + */ +static ssize_t usbtmc_ioctl_write_result(struct usbtmc_file_data *file_data, + void __user *arg) +{ + u32 transferred; + int retval; + + spin_lock_irq(&file_data->err_lock); + transferred = file_data->out_transfer_size; + retval = file_data->out_status; + spin_unlock_irq(&file_data->err_lock); + + if (put_user(transferred, (__u32 __user *)arg)) + return -EFAULT; + + return retval; +} + +/* + * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-OUT endpoint. * @transfer_size: number of bytes to request from the device. * * See the USBTMC specification, Table 4. * * Also updates bTag_last_write. */ -static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t transfer_size) +static int send_request_dev_dep_msg_in(struct usbtmc_file_data *file_data, + u32 transfer_size) { + struct usbtmc_device_data *data = file_data->data; int retval; - u8 buffer[USBTMC_HEADER_SIZE]; + u8 *buffer; int actual; + buffer = kmalloc(USBTMC_HEADER_SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; /* Setup IO buffer for REQUEST_DEV_DEP_MSG_IN message * Refer to class specs for details */ buffer[0] = 2; buffer[1] = data->bTag; - buffer[2] = ~(data->bTag); + buffer[2] = ~data->bTag; buffer[3] = 0; /* Reserved */ - buffer[4] = (transfer_size) & 255; - buffer[5] = ((transfer_size) >> 8) & 255; - buffer[6] = ((transfer_size) >> 16) & 255; - buffer[7] = ((transfer_size) >> 24) & 255; - buffer[8] = data->TermCharEnabled * 2; + buffer[4] = transfer_size >> 0; + buffer[5] = transfer_size >> 8; + buffer[6] = transfer_size >> 16; + buffer[7] = transfer_size >> 24; + buffer[8] = file_data->term_char_enabled * 2; /* Use term character? */ - buffer[9] = data->TermChar; + buffer[9] = file_data->term_char; buffer[10] = 0; /* Reserved */ buffer[11] = 0; /* Reserved */ @@ -410,7 +1350,8 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t retval = usb_bulk_msg(data->usb_dev, usb_sndbulkpipe(data->usb_dev, data->bulk_out), - buffer, USBTMC_HEADER_SIZE, &actual, USBTMC_TIMEOUT); + buffer, USBTMC_HEADER_SIZE, + &actual, file_data->timeout); /* Store bTag (in case we need to abort) */ data->bTag_last_write = data->bTag; @@ -418,189 +1359,158 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t /* Increment bTag -- and increment again if zero */ data->bTag++; if (!data->bTag) - (data->bTag)++; + data->bTag++; - if (retval < 0) { - dev_err(&data->intf->dev, "usb_bulk_msg in send_request_dev_dep_msg_in() returned %d\n", retval); - return retval; - } + kfree(buffer); + if (retval < 0) + dev_err(&data->intf->dev, "%s returned %d\n", + __func__, retval); - return 0; + return retval; } static ssize_t usbtmc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { + struct usbtmc_file_data *file_data; struct usbtmc_device_data *data; struct device *dev; + const u32 bufsize = USBTMC_BUFSIZE; u32 n_characters; u8 *buffer; int actual; - size_t done; - size_t remaining; + u32 done = 0; + u32 remaining; int retval; - size_t this_part; /* Get pointer to private data structure */ - data = filp->private_data; + file_data = filp->private_data; + data = file_data->data; dev = &data->intf->dev; - buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); + buffer = kmalloc(bufsize, GFP_KERNEL); if (!buffer) return -ENOMEM; - mutex_lock(&data->io_mutex); + retval = mutex_lock_interruptible(&data->io_mutex); + if (retval < 0) + goto exit_nolock; + if (data->zombie) { retval = -ENODEV; goto exit; } - if (data->rigol_quirk) { - dev_dbg(dev, "usb_bulk_msg_in: count(%zu)\n", count); + if (count > INT_MAX) + count = INT_MAX; - retval = send_request_dev_dep_msg_in(data, count); + dev_dbg(dev, "%s(count:%zu)\n", __func__, count); - if (retval < 0) { - if (data->auto_abort) - usbtmc_ioctl_abort_bulk_out(data); - goto exit; - } + retval = send_request_dev_dep_msg_in(file_data, count); + + if (retval < 0) { + if (file_data->auto_abort) + usbtmc_ioctl_abort_bulk_out(data); + goto exit; } /* Loop until we have fetched everything we requested */ remaining = count; - this_part = remaining; - done = 0; + actual = 0; - while (remaining > 0) { - if (!(data->rigol_quirk)) { - dev_dbg(dev, "usb_bulk_msg_in: remaining(%zu), count(%zu)\n", remaining, count); - - if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE - 3) - this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE - 3; - else - this_part = remaining; - - retval = send_request_dev_dep_msg_in(data, this_part); - 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); - - dev_dbg(dev, "usb_bulk_msg: retval(%u), done(%zu), remaining(%zu), actual(%d)\n", retval, done, remaining, actual); + /* Send bulk URB */ + retval = usb_bulk_msg(data->usb_dev, + usb_rcvbulkpipe(data->usb_dev, + data->bulk_in), + buffer, bufsize, &actual, + file_data->timeout); - /* Store bTag (in case we need to abort) */ - data->bTag_last_read = data->bTag; + dev_dbg(dev, "%s: bulk_msg retval(%u), actual(%d)\n", + __func__, retval, actual); - if (retval < 0) { - dev_dbg(dev, "Unable to read data, error %d\n", retval); - if (data->auto_abort) - usbtmc_ioctl_abort_bulk_in(data); - goto exit; - } - - /* Parse header in first packet */ - if ((done == 0) || (!(data->rigol_quirk))) { - /* Sanity checks for the header */ - if (actual < USBTMC_HEADER_SIZE) { - dev_err(dev, "Device sent too small first packet: %u < %u\n", actual, USBTMC_HEADER_SIZE); - if (data->auto_abort) - usbtmc_ioctl_abort_bulk_in(data); - goto exit; - } + /* Store bTag (in case we need to abort) */ + data->bTag_last_read = data->bTag; - if (buffer[0] != 2) { - dev_err(dev, "Device sent reply with wrong MsgID: %u != 2\n", buffer[0]); - if (data->auto_abort) - usbtmc_ioctl_abort_bulk_in(data); - goto exit; - } + if (retval < 0) { + if (file_data->auto_abort) + usbtmc_ioctl_abort_bulk_in(data); + goto exit; + } - if (buffer[1] != data->bTag_last_write) { - dev_err(dev, "Device sent reply with wrong bTag: %u != %u\n", buffer[1], data->bTag_last_write); - if (data->auto_abort) - usbtmc_ioctl_abort_bulk_in(data); - goto exit; - } + /* Sanity checks for the header */ + if (actual < USBTMC_HEADER_SIZE) { + dev_err(dev, "Device sent too small first packet: %u < %u\n", + actual, USBTMC_HEADER_SIZE); + if (file_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); + if (buffer[0] != 2) { + dev_err(dev, "Device sent reply with wrong MsgID: %u != 2\n", + buffer[0]); + if (file_data->auto_abort) + usbtmc_ioctl_abort_bulk_in(data); + goto exit; + } - if (n_characters > this_part) { - dev_err(dev, "Device wants to return more data than requested: %u > %zu\n", n_characters, count); - if (data->auto_abort) - usbtmc_ioctl_abort_bulk_in(data); - goto exit; - } + if (buffer[1] != data->bTag_last_write) { + dev_err(dev, "Device sent reply with wrong bTag: %u != %u\n", + buffer[1], data->bTag_last_write); + if (file_data->auto_abort) + usbtmc_ioctl_abort_bulk_in(data); + goto exit; + } - /* Remove the USBTMC header */ - actual -= USBTMC_HEADER_SIZE; + /* How many characters did the instrument send? */ + n_characters = buffer[4] + + (buffer[5] << 8) + + (buffer[6] << 16) + + (buffer[7] << 24); - /* Check if the message is smaller than requested */ - if (data->rigol_quirk) { - if (remaining > n_characters) - remaining = n_characters; - /* Remove padding if it exists */ - if (actual > remaining) - actual = remaining; - } - else { - if (this_part > n_characters) - this_part = n_characters; - /* Remove padding if it exists */ - if (actual > this_part) - actual = this_part; - } + file_data->bmTransferAttributes = buffer[8]; - dev_dbg(dev, "Bulk-IN header: N_characters(%u), bTransAttr(%u)\n", n_characters, buffer[8]); + dev_dbg(dev, "Bulk-IN header: N_characters(%u), bTransAttr(%u)\n", + n_characters, buffer[8]); - remaining -= actual; + if (n_characters > remaining) { + dev_err(dev, "Device wants to return more data than requested: %u > %zu\n", + n_characters, count); + if (file_data->auto_abort) + usbtmc_ioctl_abort_bulk_in(data); + goto exit; + } - /* Terminate if end-of-message bit received from device */ - if ((buffer[8] & 0x01) && (actual >= n_characters)) - remaining = 0; + print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE, + 16, 1, buffer, actual, true); - dev_dbg(dev, "Bulk-IN header: remaining(%zu), buf(%p), buffer(%p) done(%zu)\n", remaining,buf,buffer,done); + remaining = n_characters; + /* Remove the USBTMC header */ + actual -= USBTMC_HEADER_SIZE; - /* Copy buffer to user space */ - if (copy_to_user(buf + done, &buffer[USBTMC_HEADER_SIZE], actual)) { - /* There must have been an addressing problem */ - retval = -EFAULT; - goto exit; - } - done += actual; - } - else { - if (actual > remaining) - actual = remaining; + /* Remove padding if it exists */ + if (actual > remaining) + actual = remaining; - remaining -= actual; + remaining -= actual; - dev_dbg(dev, "Bulk-IN header cont: actual(%u), done(%zu), remaining(%zu), buf(%p), buffer(%p)\n", actual, done, remaining,buf,buffer); + /* Copy buffer to user space */ + if (copy_to_user(buf, &buffer[USBTMC_HEADER_SIZE], actual)) { + /* There must have been an addressing problem */ + retval = -EFAULT; + goto exit; + } - /* Copy buffer to user space */ - if (copy_to_user(buf + done, buffer, actual)) { - /* There must have been an addressing problem */ - retval = -EFAULT; - goto exit; - } - done += actual; - } + if ((actual + USBTMC_HEADER_SIZE) == bufsize) { + retval = usbtmc_generic_read(file_data, buf + actual, + remaining, + &done, + USBTMC_FLAG_IGNORE_TRAILER); + if (retval < 0) + goto exit; } + done += actual; /* Update file position value */ *f_pos = *f_pos + done; @@ -608,6 +1518,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, exit: mutex_unlock(&data->io_mutex); +exit_nolock: kfree(buffer); return retval; } @@ -615,113 +1526,154 @@ exit: static ssize_t usbtmc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { + struct usbtmc_file_data *file_data; struct usbtmc_device_data *data; + struct urb *urb = NULL; + ssize_t retval = 0; u8 *buffer; - int retval; - int actual; - unsigned long int n_bytes; - int remaining; - int done; - int this_part; - - data = filp->private_data; + u32 remaining, done; + u32 transfersize, aligned, buflen; - buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); - if (!buffer) - return -ENOMEM; + file_data = filp->private_data; + data = file_data->data; mutex_lock(&data->io_mutex); + if (data->zombie) { retval = -ENODEV; goto exit; } - remaining = count; done = 0; - while (remaining > 0) { - if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE) { - this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE; - buffer[8] = 0; - } else { - this_part = remaining; - buffer[8] = 1; - } + spin_lock_irq(&file_data->err_lock); + file_data->out_transfer_size = 0; + file_data->out_status = 0; + spin_unlock_irq(&file_data->err_lock); - /* 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[USBTMC_HEADER_SIZE], buf + done, this_part)) { - retval = -EFAULT; - goto exit; - } + if (!count) + goto exit; - n_bytes = roundup(USBTMC_HEADER_SIZE + this_part, 4); - memset(buffer + USBTMC_HEADER_SIZE + this_part, 0, n_bytes - (USBTMC_HEADER_SIZE + this_part)); + if (down_trylock(&file_data->limit_write_sem)) { + /* previous calls were async */ + retval = -EBUSY; + goto exit; + } - do { - retval = usb_bulk_msg(data->usb_dev, - usb_sndbulkpipe(data->usb_dev, - data->bulk_out), - buffer, n_bytes, - &actual, USBTMC_TIMEOUT); - if (retval != 0) - break; - n_bytes -= actual; - } while (n_bytes); - - data->bTag_last_write = data->bTag; + urb = usbtmc_create_urb(); + if (!urb) { + retval = -ENOMEM; + up(&file_data->limit_write_sem); + goto exit; + } + + buffer = urb->transfer_buffer; + buflen = urb->transfer_buffer_length; + + if (count > INT_MAX) { + transfersize = INT_MAX; + buffer[8] = 0; + } else { + transfersize = count; + buffer[8] = file_data->eom_val; + } + + /* 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] = transfersize >> 0; + buffer[5] = transfersize >> 8; + buffer[6] = transfersize >> 16; + buffer[7] = transfersize >> 24; + /* buffer[8] is set above... */ + buffer[9] = 0; /* Reserved */ + buffer[10] = 0; /* Reserved */ + buffer[11] = 0; /* Reserved */ + + remaining = transfersize; + + if (transfersize + USBTMC_HEADER_SIZE > buflen) { + transfersize = buflen - USBTMC_HEADER_SIZE; + aligned = buflen; + } else { + aligned = (transfersize + (USBTMC_HEADER_SIZE + 3)) & ~3; + } + + if (copy_from_user(&buffer[USBTMC_HEADER_SIZE], buf, transfersize)) { + retval = -EFAULT; + up(&file_data->limit_write_sem); + goto exit; + } + + dev_dbg(&data->intf->dev, "%s(size:%u align:%u)\n", __func__, + (unsigned int)transfersize, (unsigned int)aligned); + + print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE, + 16, 1, buffer, aligned, true); + + usb_fill_bulk_urb(urb, data->usb_dev, + usb_sndbulkpipe(data->usb_dev, data->bulk_out), + urb->transfer_buffer, aligned, + usbtmc_write_bulk_cb, file_data); + + usb_anchor_urb(urb, &file_data->submitted); + retval = usb_submit_urb(urb, GFP_KERNEL); + if (unlikely(retval)) { + usb_unanchor_urb(urb); + up(&file_data->limit_write_sem); + goto exit; + } + + remaining -= transfersize; + + data->bTag_last_write = data->bTag; + data->bTag++; + + if (!data->bTag) data->bTag++; - if (!data->bTag) - data->bTag++; + /* call generic_write even when remaining = 0 */ + retval = usbtmc_generic_write(file_data, buf + transfersize, remaining, + &done, USBTMC_FLAG_APPEND); + /* truncate alignment bytes */ + if (done > remaining) + done = remaining; - 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; - } + /*add size of first urb*/ + done += transfersize; - remaining -= this_part; - done += this_part; + if (retval < 0) { + usb_kill_anchored_urbs(&file_data->submitted); + + dev_err(&data->intf->dev, + "Unable to send data, error %d\n", (int)retval); + if (file_data->auto_abort) + usbtmc_ioctl_abort_bulk_out(data); + goto exit; } - retval = count; + retval = done; exit: + usb_free_urb(urb); 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; + int actual = 0; dev = &data->intf->dev; dev_dbg(dev, "Sending INITIATE_CLEAR request\n"); - buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); + buffer = kmalloc(USBTMC_BUFSIZE, GFP_KERNEL); if (!buffer) return -ENOMEM; @@ -729,7 +1681,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) 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); + 0, 0, buffer, 1, USB_CTRL_GET_TIMEOUT); if (rv < 0) { dev_err(dev, "usb_control_msg returned %d\n", rv); goto exit; @@ -743,22 +1695,6 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) 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 = usb_endpoint_maxp(desc); - } - - 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: @@ -769,7 +1705,7 @@ usbtmc_clear_check_status: 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); + 0, 0, buffer, 2, USB_CTRL_GET_TIMEOUT); if (rv < 0) { dev_err(dev, "usb_control_msg returned %d\n", rv); goto exit; @@ -786,15 +1722,20 @@ usbtmc_clear_check_status: goto exit; } - if (buffer[1] == 1) + if ((buffer[1] & 1) != 0) { do { dev_dbg(dev, "Reading from bulk in EP\n"); + actual = 0; rv = usb_bulk_msg(data->usb_dev, usb_rcvbulkpipe(data->usb_dev, data->bulk_in), - buffer, USBTMC_SIZE_IOBUFFER, - &actual, USBTMC_TIMEOUT); + buffer, USBTMC_BUFSIZE, + &actual, USB_CTRL_GET_TIMEOUT); + + print_hex_dump_debug("usbtmc ", DUMP_PREFIX_NONE, + 16, 1, buffer, actual, true); + n++; if (rv < 0) { @@ -802,10 +1743,15 @@ usbtmc_clear_check_status: rv); goto exit; } - } while ((actual == max_size) && + } while ((actual == USBTMC_BUFSIZE) && (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + } else { + /* do not stress device with subsequent requests */ + msleep(50); + n++; + } - if (actual == max_size) { + if (n >= USBTMC_MAX_READS_TO_CLEAR_BULK_IN) { dev_err(dev, "Couldn't clear device buffer within %d cycles\n", USBTMC_MAX_READS_TO_CLEAR_BULK_IN); rv = -EPERM; @@ -819,7 +1765,7 @@ usbtmc_clear_bulk_out_halt: rv = usb_clear_halt(data->usb_dev, usb_sndbulkpipe(data->usb_dev, data->bulk_out)); if (rv < 0) { - dev_err(dev, "usb_control_msg returned %d\n", rv); + dev_err(dev, "usb_clear_halt returned %d\n", rv); goto exit; } rv = 0; @@ -836,12 +1782,9 @@ static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data) rv = usb_clear_halt(data->usb_dev, usb_sndbulkpipe(data->usb_dev, data->bulk_out)); - if (rv < 0) { - dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n", - rv); - return rv; - } - return 0; + if (rv < 0) + dev_err(&data->usb_dev->dev, "%s returned %d\n", __func__, rv); + return rv; } static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data) @@ -851,11 +1794,33 @@ static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data) rv = usb_clear_halt(data->usb_dev, usb_rcvbulkpipe(data->usb_dev, data->bulk_in)); - if (rv < 0) { - dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n", - rv); - return rv; - } + if (rv < 0) + dev_err(&data->usb_dev->dev, "%s returned %d\n", __func__, rv); + return rv; +} + +static int usbtmc_ioctl_cancel_io(struct usbtmc_file_data *file_data) +{ + spin_lock_irq(&file_data->err_lock); + file_data->in_status = -ECANCELED; + file_data->out_status = -ECANCELED; + spin_unlock_irq(&file_data->err_lock); + usb_kill_anchored_urbs(&file_data->submitted); + return 0; +} + +static int usbtmc_ioctl_cleanup_io(struct usbtmc_file_data *file_data) +{ + usb_kill_anchored_urbs(&file_data->submitted); + usb_scuttle_anchored_urbs(&file_data->in_anchor); + spin_lock_irq(&file_data->err_lock); + file_data->in_status = 0; + file_data->in_transfer_size = 0; + file_data->out_status = 0; + file_data->out_transfer_size = 0; + spin_unlock_irq(&file_data->err_lock); + + file_data->in_urbs_used = 0; return 0; } @@ -872,7 +1837,7 @@ static int get_capabilities(struct usbtmc_device_data *data) 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); + 0, 0, buffer, 0x18, USB_CTRL_GET_TIMEOUT); if (rv < 0) { dev_err(dev, "usb_control_msg returned %d\n", rv); goto err_out; @@ -893,6 +1858,7 @@ static int get_capabilities(struct usbtmc_device_data *data) data->capabilities.device_capabilities = buffer[5]; data->capabilities.usb488_interface_capabilities = buffer[14]; data->capabilities.usb488_device_capabilities = buffer[15]; + data->usb488_caps = (buffer[14] & 0x07) | ((buffer[15] & 0x0f) << 4); rv = 0; err_out: @@ -901,7 +1867,7 @@ err_out: } #define capability_attribute(name) \ -static ssize_t show_##name(struct device *dev, \ +static ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ @@ -909,90 +1875,21 @@ static ssize_t show_##name(struct device *dev, \ \ return sprintf(buf, "%d\n", data->capabilities.name); \ } \ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) +static DEVICE_ATTR_RO(name) capability_attribute(interface_capabilities); capability_attribute(device_capabilities); capability_attribute(usb488_interface_capabilities); capability_attribute(usb488_device_capabilities); -static struct attribute *capability_attrs[] = { +static struct attribute *usbtmc_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, -}; +ATTRIBUTE_GROUPS(usbtmc); static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data) { @@ -1010,7 +1907,7 @@ static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data) 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); + 0, 0, buffer, 0x01, USB_CTRL_GET_TIMEOUT); if (rv < 0) { dev_err(dev, "usb_control_msg returned %d\n", rv); @@ -1031,12 +1928,154 @@ exit: return rv; } +static int usbtmc_ioctl_request(struct usbtmc_device_data *data, + void __user *arg) +{ + struct device *dev = &data->intf->dev; + struct usbtmc_ctrlrequest request; + u8 *buffer = NULL; + int rv; + unsigned int is_in, pipe; + + if (copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest))) + return -EFAULT; + + if (request.req.wLength > USBTMC_BUFSIZE) + return -EMSGSIZE; + if (request.req.wLength == 0) /* Length-0 requests are never IN */ + request.req.bRequestType &= ~USB_DIR_IN; + + is_in = request.req.bRequestType & USB_DIR_IN; + + if (request.req.wLength) { + buffer = kmalloc(request.req.wLength, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + if (!is_in) { + /* Send control data to device */ + if (copy_from_user(buffer, request.data, + request.req.wLength)) { + rv = -EFAULT; + goto exit; + } + } + } + + if (is_in) + pipe = usb_rcvctrlpipe(data->usb_dev, 0); + else + pipe = usb_sndctrlpipe(data->usb_dev, 0); + rv = usb_control_msg(data->usb_dev, + pipe, + request.req.bRequest, + request.req.bRequestType, + request.req.wValue, + request.req.wIndex, + buffer, request.req.wLength, USB_CTRL_GET_TIMEOUT); + + if (rv < 0) { + dev_err(dev, "%s failed %d\n", __func__, rv); + goto exit; + } + + if (rv && is_in) { + /* Read control data from device */ + if (copy_to_user(request.data, buffer, rv)) + rv = -EFAULT; + } + + exit: + kfree(buffer); + return rv; +} + +/* + * Get the usb timeout value + */ +static int usbtmc_ioctl_get_timeout(struct usbtmc_file_data *file_data, + void __user *arg) +{ + u32 timeout; + + timeout = file_data->timeout; + + return put_user(timeout, (__u32 __user *)arg); +} + +/* + * Set the usb timeout value + */ +static int usbtmc_ioctl_set_timeout(struct usbtmc_file_data *file_data, + void __user *arg) +{ + u32 timeout; + + if (get_user(timeout, (__u32 __user *)arg)) + return -EFAULT; + + /* Note that timeout = 0 means + * MAX_SCHEDULE_TIMEOUT in usb_control_msg + */ + if (timeout < USBTMC_MIN_TIMEOUT) + return -EINVAL; + + file_data->timeout = timeout; + + return 0; +} + +/* + * enables/disables sending EOM on write + */ +static int usbtmc_ioctl_eom_enable(struct usbtmc_file_data *file_data, + void __user *arg) +{ + u8 eom_enable; + + if (copy_from_user(&eom_enable, arg, sizeof(eom_enable))) + return -EFAULT; + + if (eom_enable > 1) + return -EINVAL; + + file_data->eom_val = eom_enable; + + return 0; +} + +/* + * Configure termination character for read() + */ +static int usbtmc_ioctl_config_termc(struct usbtmc_file_data *file_data, + void __user *arg) +{ + struct usbtmc_termchar termc; + + if (copy_from_user(&termc, arg, sizeof(termc))) + return -EFAULT; + + if ((termc.term_char_enabled > 1) || + (termc.term_char_enabled && + !(file_data->data->capabilities.device_capabilities & 1))) + return -EINVAL; + + file_data->term_char = termc.term_char; + file_data->term_char_enabled = termc.term_char_enabled; + + return 0; +} + static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct usbtmc_file_data *file_data; struct usbtmc_device_data *data; int retval = -EBADRQC; + __u8 tmp_byte; + + file_data = file->private_data; + data = file_data->data; - data = file->private_data; mutex_lock(&data->io_mutex); if (data->zombie) { retval = -ENODEV; @@ -1067,6 +2106,114 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case USBTMC_IOCTL_ABORT_BULK_IN: retval = usbtmc_ioctl_abort_bulk_in(data); break; + + case USBTMC_IOCTL_CTRL_REQUEST: + retval = usbtmc_ioctl_request(data, (void __user *)arg); + break; + + case USBTMC_IOCTL_GET_TIMEOUT: + retval = usbtmc_ioctl_get_timeout(file_data, + (void __user *)arg); + break; + + case USBTMC_IOCTL_SET_TIMEOUT: + retval = usbtmc_ioctl_set_timeout(file_data, + (void __user *)arg); + break; + + case USBTMC_IOCTL_EOM_ENABLE: + retval = usbtmc_ioctl_eom_enable(file_data, + (void __user *)arg); + break; + + case USBTMC_IOCTL_CONFIG_TERMCHAR: + retval = usbtmc_ioctl_config_termc(file_data, + (void __user *)arg); + break; + + case USBTMC_IOCTL_WRITE: + retval = usbtmc_ioctl_generic_write(file_data, + (void __user *)arg); + break; + + case USBTMC_IOCTL_READ: + retval = usbtmc_ioctl_generic_read(file_data, + (void __user *)arg); + break; + + case USBTMC_IOCTL_WRITE_RESULT: + retval = usbtmc_ioctl_write_result(file_data, + (void __user *)arg); + break; + + case USBTMC_IOCTL_API_VERSION: + retval = put_user(USBTMC_API_VERSION, + (__u32 __user *)arg); + break; + + case USBTMC488_IOCTL_GET_CAPS: + retval = put_user(data->usb488_caps, + (unsigned char __user *)arg); + break; + + case USBTMC488_IOCTL_READ_STB: + retval = usbtmc488_ioctl_read_stb(file_data, + (void __user *)arg); + break; + + case USBTMC488_IOCTL_REN_CONTROL: + retval = usbtmc488_ioctl_simple(data, (void __user *)arg, + USBTMC488_REQUEST_REN_CONTROL); + break; + + case USBTMC488_IOCTL_GOTO_LOCAL: + retval = usbtmc488_ioctl_simple(data, (void __user *)arg, + USBTMC488_REQUEST_GOTO_LOCAL); + break; + + case USBTMC488_IOCTL_LOCAL_LOCKOUT: + retval = usbtmc488_ioctl_simple(data, (void __user *)arg, + USBTMC488_REQUEST_LOCAL_LOCKOUT); + break; + + case USBTMC488_IOCTL_TRIGGER: + retval = usbtmc488_ioctl_trigger(file_data); + break; + + case USBTMC488_IOCTL_WAIT_SRQ: + retval = usbtmc488_ioctl_wait_srq(file_data, + (__u32 __user *)arg); + break; + + case USBTMC_IOCTL_MSG_IN_ATTR: + retval = put_user(file_data->bmTransferAttributes, + (__u8 __user *)arg); + break; + + case USBTMC_IOCTL_AUTO_ABORT: + retval = get_user(tmp_byte, (unsigned char __user *)arg); + if (retval == 0) + file_data->auto_abort = !!tmp_byte; + break; + + case USBTMC_IOCTL_GET_STB: + retval = usbtmc_get_stb(file_data, &tmp_byte); + if (!retval) + retval = put_user(tmp_byte, (__u8 __user *)arg); + break; + + case USBTMC_IOCTL_GET_SRQ_STB: + retval = usbtmc_ioctl_get_srq_stb(file_data, + (void __user *)arg); + break; + + case USBTMC_IOCTL_CANCEL_IO: + retval = usbtmc_ioctl_cancel_io(file_data); + break; + + case USBTMC_IOCTL_CLEANUP_IO: + retval = usbtmc_ioctl_cleanup_io(file_data); + break; } skip_io_on_zombie: @@ -1074,13 +2221,67 @@ skip_io_on_zombie: return retval; } +static int usbtmc_fasync(int fd, struct file *file, int on) +{ + struct usbtmc_file_data *file_data = file->private_data; + + return fasync_helper(fd, file, on, &file_data->data->fasync); +} + +static __poll_t usbtmc_poll(struct file *file, poll_table *wait) +{ + struct usbtmc_file_data *file_data = file->private_data; + struct usbtmc_device_data *data = file_data->data; + __poll_t mask; + + mutex_lock(&data->io_mutex); + + if (data->zombie) { + mask = EPOLLHUP | EPOLLERR; + goto no_poll; + } + + poll_wait(file, &data->waitq, wait); + + /* Note that EPOLLPRI is now assigned to SRQ, and + * EPOLLIN|EPOLLRDNORM to normal read data. + */ + mask = 0; + if (atomic_read(&file_data->srq_asserted)) + mask |= EPOLLPRI; + + /* Note that the anchor submitted includes all urbs for BULK IN + * and OUT. So EPOLLOUT is signaled when BULK OUT is empty and + * all BULK IN urbs are completed and moved to in_anchor. + */ + if (usb_anchor_empty(&file_data->submitted)) + mask |= (EPOLLOUT | EPOLLWRNORM); + if (!usb_anchor_empty(&file_data->in_anchor)) + mask |= (EPOLLIN | EPOLLRDNORM); + + spin_lock_irq(&file_data->err_lock); + if (file_data->in_status || file_data->out_status) + mask |= EPOLLERR; + spin_unlock_irq(&file_data->err_lock); + + dev_dbg(&data->intf->dev, "poll mask = %x\n", mask); + +no_poll: + mutex_unlock(&data->io_mutex); + return mask; +} + static const struct file_operations fops = { .owner = THIS_MODULE, .read = usbtmc_read, .write = usbtmc_write, .open = usbtmc_open, .release = usbtmc_release, + .flush = usbtmc_flush, .unlocked_ioctl = usbtmc_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .fasync = usbtmc_fasync, + .poll = usbtmc_poll, .llseek = default_llseek, }; @@ -1090,23 +2291,96 @@ static struct usb_class_driver usbtmc_class = { .minor_base = USBTMC_MINOR_BASE, }; +static void usbtmc_interrupt(struct urb *urb) +{ + struct usbtmc_device_data *data = urb->context; + struct device *dev = &data->intf->dev; + int status = urb->status; + int rv; + + dev_dbg(&data->intf->dev, "int status: %d len %d\n", + status, urb->actual_length); + + switch (status) { + case 0: /* SUCCESS */ + /* check for valid STB notification */ + if (data->iin_buffer[0] > 0x81) { + data->bNotify1 = data->iin_buffer[0]; + data->bNotify2 = data->iin_buffer[1]; + atomic_set(&data->iin_data_valid, 1); + wake_up_interruptible(&data->waitq); + goto exit; + } + /* check for SRQ notification */ + if (data->iin_buffer[0] == 0x81) { + unsigned long flags; + struct list_head *elem; + + if (data->fasync) + kill_fasync(&data->fasync, + SIGIO, POLL_PRI); + + spin_lock_irqsave(&data->dev_lock, flags); + list_for_each(elem, &data->file_list) { + struct usbtmc_file_data *file_data; + + file_data = list_entry(elem, + struct usbtmc_file_data, + file_elem); + file_data->srq_byte = data->iin_buffer[1]; + atomic_set(&file_data->srq_asserted, 1); + } + spin_unlock_irqrestore(&data->dev_lock, flags); + + dev_dbg(dev, "srq received bTag %x stb %x\n", + (unsigned int)data->iin_buffer[0], + (unsigned int)data->iin_buffer[1]); + wake_up_interruptible_all(&data->waitq); + goto exit; + } + dev_warn(dev, "invalid notification: %x\n", + data->iin_buffer[0]); + break; + case -EOVERFLOW: + dev_err(dev, "overflow with length %d, actual length is %d\n", + data->iin_wMaxPacketSize, urb->actual_length); + fallthrough; + default: + /* urb terminated, clean up */ + dev_dbg(dev, "urb terminated, status: %d\n", status); + return; + } +exit: + rv = usb_submit_urb(urb, GFP_ATOMIC); + if (rv) + dev_err(dev, "usb_submit_urb failed: %d\n", rv); +} + +static void usbtmc_free_int(struct usbtmc_device_data *data) +{ + if (!data->iin_ep_present || !data->iin_urb) + return; + usb_kill_urb(data->iin_urb); + kfree(data->iin_buffer); + data->iin_buffer = NULL; + usb_free_urb(data->iin_urb); + data->iin_urb = NULL; + kref_put(&data->kref, usbtmc_delete); +} 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; + struct usb_endpoint_descriptor *bulk_in, *bulk_out, *int_in; 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"); + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) return -ENOMEM; - } data->intf = intf; data->id = id; @@ -1114,67 +2388,92 @@ static int usbtmc_probe(struct usb_interface *intf, usb_set_intfdata(intf, data); kref_init(&data->kref); mutex_init(&data->io_mutex); - data->zombie = 0; + init_waitqueue_head(&data->waitq); + atomic_set(&data->iin_data_valid, 0); + INIT_LIST_HEAD(&data->file_list); + spin_lock_init(&data->dev_lock); - /* Determine if it is a Rigol or not */ - data->rigol_quirk = 0; - dev_dbg(&intf->dev, "Trying to find if device Vendor 0x%04X Product 0x%04X has the RIGOL quirk\n", - data->usb_dev->descriptor.idVendor, - data->usb_dev->descriptor.idProduct); - for(n = 0; usbtmc_id_quirk[n].idVendor > 0; n++) { - if ((usbtmc_id_quirk[n].idVendor == data->usb_dev->descriptor.idVendor) && - (usbtmc_id_quirk[n].idProduct == data->usb_dev->descriptor.idProduct)) { - dev_dbg(&intf->dev, "Setting this device as having the RIGOL quirk\n"); - data->rigol_quirk = 1; - break; - } - } + data->zombie = 0; /* Initialize USBTMC bTag and other fields */ data->bTag = 1; - data->TermCharEnabled = 0; - data->TermChar = '\n'; + /* 2 <= bTag <= 127 USBTMC-USB488 subclass specification 4.3.1 */ + data->iin_bTag = 2; /* USBTMC devices have only one setting, so use that */ iface_desc = data->intf->cur_altsetting; + data->ifnum = iface_desc->desc.bInterfaceNumber; - /* 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 endpoints */ + retcode = usb_find_common_endpoints(iface_desc, + &bulk_in, &bulk_out, NULL, NULL); + if (retcode) { + dev_err(&intf->dev, "bulk endpoints not found\n"); + goto err_put; } - /* 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 = -EINVAL; + data->bulk_in = bulk_in->bEndpointAddress; + data->wMaxPacketSize = usb_endpoint_maxp(bulk_in); + if (!data->wMaxPacketSize) + goto err_put; + dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n", data->bulk_in); + + data->bulk_out = bulk_out->bEndpointAddress; + dev_dbg(&intf->dev, "Found Bulk out endpoint at %u\n", data->bulk_out); + + /* Find int endpoint */ + retcode = usb_find_int_in_endpoint(iface_desc, &int_in); + if (!retcode) { + data->iin_ep_present = 1; + data->iin_ep = int_in->bEndpointAddress; + data->iin_wMaxPacketSize = usb_endpoint_maxp(int_in); + data->iin_interval = int_in->bInterval; + dev_dbg(&intf->dev, "Found Int in endpoint at %u\n", + data->iin_ep); } 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); + if (data->iin_ep_present) { + /* allocate int urb */ + data->iin_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!data->iin_urb) { + retcode = -ENOMEM; + goto error_register; + } + + /* Protect interrupt in endpoint data until iin_urb is freed */ + kref_get(&data->kref); + + /* allocate buffer for interrupt in */ + data->iin_buffer = kmalloc(data->iin_wMaxPacketSize, + GFP_KERNEL); + if (!data->iin_buffer) { + retcode = -ENOMEM; + goto error_register; + } + + /* fill interrupt urb */ + usb_fill_int_urb(data->iin_urb, data->usb_dev, + usb_rcvintpipe(data->usb_dev, data->iin_ep), + data->iin_buffer, data->iin_wMaxPacketSize, + usbtmc_interrupt, + data, data->iin_interval); + + retcode = usb_submit_urb(data->iin_urb, GFP_KERNEL); + if (retcode) { + dev_err(&intf->dev, "Failed to submit iin_urb\n"); + goto error_register; + } + } 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, + dev_err(&intf->dev, "Not able to get a minor (base %u, slice default): %d\n", + USBTMC_MINOR_BASE, retcode); goto error_register; } @@ -1183,36 +2482,111 @@ static int usbtmc_probe(struct usb_interface *intf, return 0; error_register: - sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); - sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); + usbtmc_free_int(data); +err_put: 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"); + struct usbtmc_device_data *data = usb_get_intfdata(intf); + struct list_head *elem; - 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); mutex_lock(&data->io_mutex); data->zombie = 1; + wake_up_interruptible_all(&data->waitq); + list_for_each(elem, &data->file_list) { + struct usbtmc_file_data *file_data; + + file_data = list_entry(elem, + struct usbtmc_file_data, + file_elem); + usb_kill_anchored_urbs(&file_data->submitted); + usb_scuttle_anchored_urbs(&file_data->in_anchor); + } mutex_unlock(&data->io_mutex); + usbtmc_free_int(data); kref_put(&data->kref, usbtmc_delete); } +static void usbtmc_draw_down(struct usbtmc_file_data *file_data) +{ + int time; + + time = usb_wait_anchor_empty_timeout(&file_data->submitted, 1000); + if (!time) + usb_kill_anchored_urbs(&file_data->submitted); + usb_scuttle_anchored_urbs(&file_data->in_anchor); +} + static int usbtmc_suspend(struct usb_interface *intf, pm_message_t message) { - /* this driver does not have pending URBs */ + struct usbtmc_device_data *data = usb_get_intfdata(intf); + struct list_head *elem; + + if (!data) + return 0; + + mutex_lock(&data->io_mutex); + list_for_each(elem, &data->file_list) { + struct usbtmc_file_data *file_data; + + file_data = list_entry(elem, + struct usbtmc_file_data, + file_elem); + usbtmc_draw_down(file_data); + } + + if (data->iin_ep_present && data->iin_urb) + usb_kill_urb(data->iin_urb); + + mutex_unlock(&data->io_mutex); return 0; } static int usbtmc_resume(struct usb_interface *intf) { + struct usbtmc_device_data *data = usb_get_intfdata(intf); + int retcode = 0; + + if (data->iin_ep_present && data->iin_urb) + retcode = usb_submit_urb(data->iin_urb, GFP_KERNEL); + if (retcode) + dev_err(&intf->dev, "Failed to submit iin_urb\n"); + + return retcode; +} + +static int usbtmc_pre_reset(struct usb_interface *intf) +{ + struct usbtmc_device_data *data = usb_get_intfdata(intf); + struct list_head *elem; + + if (!data) + return 0; + + mutex_lock(&data->io_mutex); + + list_for_each(elem, &data->file_list) { + struct usbtmc_file_data *file_data; + + file_data = list_entry(elem, + struct usbtmc_file_data, + file_elem); + usbtmc_ioctl_cancel_io(file_data); + } + + return 0; +} + +static int usbtmc_post_reset(struct usb_interface *intf) +{ + struct usbtmc_device_data *data = usb_get_intfdata(intf); + + mutex_unlock(&data->io_mutex); + return 0; } @@ -1223,8 +2597,12 @@ static struct usb_driver usbtmc_driver = { .disconnect = usbtmc_disconnect, .suspend = usbtmc_suspend, .resume = usbtmc_resume, + .pre_reset = usbtmc_pre_reset, + .post_reset = usbtmc_post_reset, + .dev_groups = usbtmc_groups, }; module_usb_driver(usbtmc_driver); +MODULE_DESCRIPTION("USB Test & Measurement class driver"); MODULE_LICENSE("GPL"); |
