diff options
Diffstat (limited to 'drivers/usb/class')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 40 | ||||
-rw-r--r-- | drivers/usb/class/cdc-wdm.c | 47 | ||||
-rw-r--r-- | drivers/usb/class/usblp.c | 10 | ||||
-rw-r--r-- | drivers/usb/class/usbtmc.c | 320 |
4 files changed, 339 insertions, 78 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 75c4623ad779..27346d69f393 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -276,6 +276,7 @@ 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); @@ -303,7 +304,7 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) } difference = acm->ctrlin ^ newctrl; - spin_lock(&acm->read_lock); + spin_lock_irqsave(&acm->read_lock, flags); acm->ctrlin = newctrl; acm->oldcount = acm->iocount; @@ -321,7 +322,7 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) acm->iocount.parity++; if (difference & ACM_CTRL_OVERRUN) acm->iocount.overrun++; - spin_unlock(&acm->read_lock); + spin_unlock_irqrestore(&acm->read_lock, flags); if (difference) wake_up_all(&acm->wioctl); @@ -1378,6 +1379,9 @@ made_compressed_probe: if (acm == NULL) goto alloc_fail; + tty_port_init(&acm->port); + acm->port.ops = &acm_port_ops; + minor = acm_alloc_minor(acm); if (minor < 0) goto alloc_fail1; @@ -1413,22 +1417,20 @@ made_compressed_probe: acm->out = usb_sndintpipe(usb_dev, epwrite->bEndpointAddress); else acm->out = usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress); - tty_port_init(&acm->port); - acm->port.ops = &acm_port_ops; init_usb_anchor(&acm->delayed); acm->quirks = quirks; buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); if (!buf) - goto alloc_fail2; + goto alloc_fail1; acm->ctrl_buffer = buf; if (acm_write_buffers_alloc(acm) < 0) - goto alloc_fail4; + goto alloc_fail2; acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); if (!acm->ctrlurb) - goto alloc_fail5; + goto alloc_fail3; for (i = 0; i < num_rx_buf; i++) { struct acm_rb *rb = &(acm->read_buffers[i]); @@ -1437,13 +1439,13 @@ made_compressed_probe: rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL, &rb->dma); if (!rb->base) - goto alloc_fail6; + goto alloc_fail4; rb->index = i; rb->instance = acm; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) - goto alloc_fail6; + goto alloc_fail4; urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = rb->dma; @@ -1465,7 +1467,7 @@ made_compressed_probe: snd->urb = usb_alloc_urb(0, GFP_KERNEL); if (snd->urb == NULL) - goto alloc_fail7; + goto alloc_fail5; if (usb_endpoint_xfer_int(epwrite)) usb_fill_int_urb(snd->urb, usb_dev, acm->out, @@ -1483,7 +1485,7 @@ made_compressed_probe: i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); if (i < 0) - goto alloc_fail7; + goto alloc_fail5; if (h.usb_cdc_country_functional_desc) { /* export the country data */ struct usb_cdc_country_functional_desc * cfd = @@ -1542,7 +1544,7 @@ skip_countries: &control_interface->dev); if (IS_ERR(tty_dev)) { rv = PTR_ERR(tty_dev); - goto alloc_fail8; + goto alloc_fail6; } if (quirks & CLEAR_HALT_CONDITIONS) { @@ -1551,7 +1553,7 @@ skip_countries: } return 0; -alloc_fail8: +alloc_fail6: if (acm->country_codes) { device_remove_file(&acm->control->dev, &dev_attr_wCountryCodes); @@ -1560,23 +1562,21 @@ alloc_fail8: kfree(acm->country_codes); } device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities); -alloc_fail7: +alloc_fail5: usb_set_intfdata(intf, NULL); for (i = 0; i < ACM_NW; i++) usb_free_urb(acm->wb[i].urb); -alloc_fail6: +alloc_fail4: 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: +alloc_fail3: acm_write_buffers_free(acm); -alloc_fail4: - usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); alloc_fail2: - acm_release_minor(acm); + usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); alloc_fail1: - kfree(acm); + tty_port_put(&acm->port); alloc_fail: return rv; } diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index a0d284ef3f40..bec581fb7c63 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -96,6 +96,7 @@ 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; @@ -141,26 +142,26 @@ 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); } -/* forward declaration */ -static int service_outstanding_interrupt(struct wdm_device *desc); - 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) { @@ -209,8 +210,6 @@ static void wdm_in_callback(struct urb *urb) } } skip_error: - set_bit(WDM_READ, &desc->flags); - wake_up(&desc->wait); if (desc->rerr) { /* @@ -219,14 +218,17 @@ skip_error: * We should respond to further attempts from the device to send * data, so that we can get unstuck. */ - service_outstanding_interrupt(desc); + schedule_work(&desc->service_outs_intr); + } else { + set_bit(WDM_READ, &desc->flags); + wake_up(&desc->wait); } - - spin_unlock(&desc->iuspin); + 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; @@ -286,7 +288,7 @@ static void wdm_int_callback(struct urb *urb) goto exit; } - spin_lock(&desc->iuspin); + 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) @@ -294,7 +296,7 @@ static void wdm_int_callback(struct urb *urb) rv = usb_submit_urb(desc->response, GFP_ATOMIC); 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) @@ -758,6 +760,21 @@ 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) { + 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, @@ -779,6 +796,7 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber); desc->intf = intf; INIT_WORK(&desc->rxwork, wdm_rxwork); + INIT_WORK(&desc->service_outs_intr, service_interrupt_work); rv = -EINVAL; if (!usb_endpoint_is_int_in(ep)) @@ -964,6 +982,7 @@ static void wdm_disconnect(struct usb_interface *intf) mutex_lock(&desc->wlock); kill_urbs(desc); cancel_work_sync(&desc->rxwork); + cancel_work_sync(&desc->service_outs_intr); mutex_unlock(&desc->wlock); mutex_unlock(&desc->rlock); @@ -1006,6 +1025,7 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) /* callback submits work - order is essential */ kill_urbs(desc); cancel_work_sync(&desc->rxwork); + cancel_work_sync(&desc->service_outs_intr); } if (!PMSG_IS_AUTO(message)) { mutex_unlock(&desc->wlock); @@ -1065,6 +1085,7 @@ static int wdm_pre_reset(struct usb_interface *intf) mutex_lock(&desc->wlock); kill_urbs(desc); cancel_work_sync(&desc->rxwork); + cancel_work_sync(&desc->service_outs_intr); return 0; } diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index d058d7a31e7c..407a7a6198a2 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -292,6 +292,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) @@ -299,14 +300,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); } @@ -315,6 +316,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) @@ -322,7 +324,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 @@ -330,7 +332,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); } diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 529295a17579..83ffa5a14c3d 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -18,6 +18,7 @@ #include <linux/poll.h> #include <linux/mutex.h> #include <linux/usb.h> +#include <linux/compat.h> #include <linux/usb/tmc.h> @@ -30,6 +31,8 @@ */ #define USBTMC_SIZE_IOBUFFER 2048 +/* Minimum USB timeout (in milliseconds) */ +#define USBTMC_MIN_TIMEOUT 100 /* Default USB timeout (in milliseconds) */ #define USBTMC_TIMEOUT 5000 @@ -67,6 +70,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; @@ -87,7 +91,6 @@ struct usbtmc_device_data { int iin_interval; struct urb *iin_urb; u16 iin_wMaxPacketSize; - atomic_t srq_asserted; /* coalesced usb488_caps from usbtmc_dev_capabilities */ __u8 usb488_caps; @@ -104,9 +107,25 @@ struct usbtmc_device_data { 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) +/* + * 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; + u8 eom_val; + u8 term_char; + bool term_char_enabled; +}; + /* Forward declarations */ static struct usb_driver usbtmc_driver; @@ -122,7 +141,7 @@ 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) { @@ -130,21 +149,51 @@ static int usbtmc_open(struct inode *inode, struct file *filp) return -ENODEV; } + file_data = kzalloc(sizeof(*file_data), GFP_KERNEL); + if (!file_data) + return -ENOMEM; + 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; + + /* copy default values from device settings */ + file_data->timeout = USBTMC_TIMEOUT; + file_data->term_char = data->TermChar; + file_data->term_char_enabled = data->TermCharEnabled; + 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; - return retval; + 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; } @@ -369,10 +418,12 @@ exit: return rv; } -static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data, +static int usbtmc488_ioctl_read_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 *buffer; u8 tag; __u8 stb; @@ -381,15 +432,25 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data, dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n", data->iin_ep_present); + spin_lock_irq(&data->dev_lock); + srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted); + if (srq_asserted) { + /* a STB with SRQ is already received */ + stb = file_data->srq_byte; + spin_unlock_irq(&data->dev_lock); + rv = put_user(stb, (__u8 __user *)arg); + dev_dbg(dev, "stb:0x%02x with srq received %d\n", + (unsigned int)stb, rv); + return rv; + } + spin_unlock_irq(&data->dev_lock); + buffer = kmalloc(8, GFP_KERNEL); if (!buffer) return -ENOMEM; atomic_set(&data->iin_data_valid, 0); - /* must issue read_stb before using poll or select */ - atomic_set(&data->srq_asserted, 0); - rv = usb_control_msg(data->usb_dev, usb_rcvctrlpipe(data->usb_dev, 0), USBTMC488_REQUEST_READ_STATUS_BYTE, @@ -412,7 +473,7 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data, rv = wait_event_interruptible_timeout( data->waitq, atomic_read(&data->iin_data_valid) != 0, - USBTMC_TIMEOUT); + file_data->timeout); if (rv < 0) { dev_dbg(dev, "wait interrupted %d\n", rv); goto exit; @@ -420,7 +481,7 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data, if (rv == 0) { dev_dbg(dev, "wait timed out\n"); - rv = -ETIME; + rv = -ETIMEDOUT; goto exit; } @@ -435,9 +496,8 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data, stb = buffer[2]; } - rv = copy_to_user(arg, &stb, sizeof(stb)); - if (rv) - rv = -EFAULT; + rv = put_user(stb, (__u8 __user *)arg); + dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)stb, rv); exit: /* bump interrupt bTag */ @@ -506,6 +566,51 @@ static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data, } /* + * 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; +} + +/* * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-OUT endpoint. * @transfer_size: number of bytes to request from the device. * @@ -513,8 +618,10 @@ static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data, * * 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, + size_t transfer_size) { + struct usbtmc_device_data *data = file_data->data; int retval; u8 *buffer; int actual; @@ -533,9 +640,9 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t buffer[5] = transfer_size >> 8; buffer[6] = transfer_size >> 16; buffer[7] = transfer_size >> 24; - buffer[8] = data->TermCharEnabled * 2; + 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 */ @@ -543,7 +650,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; @@ -565,6 +673,7 @@ static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t t 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; u32 n_characters; @@ -576,7 +685,8 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, 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); @@ -591,7 +701,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, dev_dbg(dev, "usb_bulk_msg_in: count(%zu)\n", count); - retval = send_request_dev_dep_msg_in(data, count); + retval = send_request_dev_dep_msg_in(file_data, count); if (retval < 0) { if (data->auto_abort) @@ -610,7 +720,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, usb_rcvbulkpipe(data->usb_dev, data->bulk_in), buffer, USBTMC_SIZE_IOBUFFER, &actual, - USBTMC_TIMEOUT); + file_data->timeout); dev_dbg(dev, "usb_bulk_msg: retval(%u), done(%zu), remaining(%zu), actual(%d)\n", retval, done, remaining, actual); @@ -721,6 +831,7 @@ 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; u8 *buffer; int retval; @@ -730,7 +841,8 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, int done; int this_part; - data = filp->private_data; + file_data = filp->private_data; + data = file_data->data; buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); if (!buffer) @@ -751,7 +863,7 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, buffer[8] = 0; } else { this_part = remaining; - buffer[8] = 1; + buffer[8] = file_data->eom_val; } /* Setup IO buffer for DEV_DEP_MSG_OUT message */ @@ -781,7 +893,7 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, usb_sndbulkpipe(data->usb_dev, data->bulk_out), buffer, n_bytes, - &actual, USBTMC_TIMEOUT); + &actual, file_data->timeout); if (retval != 0) break; n_bytes -= actual; @@ -1138,12 +1250,91 @@ exit: 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; - data = file->private_data; + file_data = file->private_data; + data = file_data->data; + mutex_lock(&data->io_mutex); if (data->zombie) { retval = -ENODEV; @@ -1175,6 +1366,26 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) retval = usbtmc_ioctl_abort_bulk_in(data); 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 USBTMC488_IOCTL_GET_CAPS: retval = copy_to_user((void __user *)arg, &data->usb488_caps, @@ -1184,7 +1395,8 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case USBTMC488_IOCTL_READ_STB: - retval = usbtmc488_ioctl_read_stb(data, (void __user *)arg); + retval = usbtmc488_ioctl_read_stb(file_data, + (void __user *)arg); break; case USBTMC488_IOCTL_REN_CONTROL: @@ -1201,6 +1413,10 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) retval = usbtmc488_ioctl_simple(data, (void __user *)arg, USBTMC488_REQUEST_LOCAL_LOCKOUT); break; + + case USBTMC488_IOCTL_TRIGGER: + retval = usbtmc488_ioctl_trigger(file_data); + break; } skip_io_on_zombie: @@ -1210,14 +1426,15 @@ skip_io_on_zombie: static int usbtmc_fasync(int fd, struct file *file, int on) { - struct usbtmc_device_data *data = file->private_data; + struct usbtmc_file_data *file_data = file->private_data; - return fasync_helper(fd, file, on, &data->fasync); + return fasync_helper(fd, file, on, &file_data->data->fasync); } static __poll_t usbtmc_poll(struct file *file, poll_table *wait) { - struct usbtmc_device_data *data = file->private_data; + 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); @@ -1229,7 +1446,7 @@ static __poll_t usbtmc_poll(struct file *file, poll_table *wait) poll_wait(file, &data->waitq, wait); - mask = (atomic_read(&data->srq_asserted)) ? EPOLLIN | EPOLLRDNORM : 0; + mask = (atomic_read(&file_data->srq_asserted)) ? EPOLLPRI : 0; no_poll: mutex_unlock(&data->io_mutex); @@ -1243,6 +1460,9 @@ static const struct file_operations fops = { .open = usbtmc_open, .release = usbtmc_release, .unlocked_ioctl = usbtmc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = usbtmc_ioctl, +#endif .fasync = usbtmc_fasync, .poll = usbtmc_poll, .llseek = default_llseek, @@ -1276,15 +1496,33 @@ static void usbtmc_interrupt(struct urb *urb) } /* 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_IN); + SIGIO, POLL_PRI); - atomic_set(&data->srq_asserted, 1); - wake_up_interruptible(&data->waitq); + 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]); + 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", @@ -1295,6 +1533,7 @@ static void usbtmc_interrupt(struct urb *urb) case -ESHUTDOWN: case -EILSEQ: case -ETIME: + case -EPIPE: /* urb terminated, clean up */ dev_dbg(dev, "urb terminated, status: %d\n", status); return; @@ -1339,7 +1578,9 @@ static int usbtmc_probe(struct usb_interface *intf, mutex_init(&data->io_mutex); init_waitqueue_head(&data->waitq); atomic_set(&data->iin_data_valid, 0); - atomic_set(&data->srq_asserted, 0); + INIT_LIST_HEAD(&data->file_list); + spin_lock_init(&data->dev_lock); + data->zombie = 0; /* Initialize USBTMC bTag and other fields */ @@ -1442,17 +1683,14 @@ err_put: 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); - 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_all(&data->waitq); + wake_up_interruptible_all(&data->waitq); mutex_unlock(&data->io_mutex); usbtmc_free_int(data); kref_put(&data->kref, usbtmc_delete); |