Commit dbf3e7f6 authored by Dave Penkler's avatar Dave Penkler Committed by Greg Kroah-Hartman

Implement an ioctl to support the USMTMC-USB488 READ_STATUS_BYTE operation.

Background:
When performing a read on an instrument that is executing a function
that runs longer than the USB timeout the instrument may hang and
require a device reset to recover. The READ_STATUS_BYTE operation
always returns even when the instrument is busy permitting to poll
for the appropriate condition. This capability is referred to in
instrument application notes on synchronizing acquisitions for other
platforms.
Signed-off-by: default avatarDave Penkler <dpenkler@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 21619792
...@@ -87,6 +87,19 @@ struct usbtmc_device_data { ...@@ -87,6 +87,19 @@ struct usbtmc_device_data {
u8 bTag_last_write; /* needed for abort */ u8 bTag_last_write; /* needed for abort */
u8 bTag_last_read; /* needed for abort */ u8 bTag_last_read; /* needed for abort */
/* 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;
u8 rigol_quirk; u8 rigol_quirk;
/* attributes from the USB TMC spec for this device */ /* attributes from the USB TMC spec for this device */
...@@ -99,6 +112,7 @@ struct usbtmc_device_data { ...@@ -99,6 +112,7 @@ struct usbtmc_device_data {
struct usbtmc_dev_capabilities capabilities; struct usbtmc_dev_capabilities capabilities;
struct kref kref; struct kref kref;
struct mutex io_mutex; /* only one i/o function running at a time */ struct mutex io_mutex; /* only one i/o function running at a time */
wait_queue_head_t waitq;
}; };
#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref) #define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref)
...@@ -373,6 +387,84 @@ static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data) ...@@ -373,6 +387,84 @@ static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
return rv; return rv;
} }
static int usbtmc488_ioctl_read_stb(struct usbtmc_device_data *data,
void __user *arg)
{
struct device *dev = &data->intf->dev;
u8 *buffer;
u8 tag;
__u8 stb;
int rv;
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, USBTMC_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) {
rv = wait_event_interruptible_timeout(
data->waitq,
atomic_read(&data->iin_data_valid) != 0,
USBTMC_TIMEOUT);
if (rv < 0) {
dev_dbg(dev, "wait interrupted %d\n", rv);
goto exit;
}
if (rv == 0) {
dev_dbg(dev, "wait timed out\n");
rv = -ETIME;
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];
}
rv = copy_to_user(arg, &stb, sizeof(stb));
if (rv)
rv = -EFAULT;
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;
}
/* /*
* Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-IN endpoint. * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-IN endpoint.
* @transfer_size: number of bytes to request from the device. * @transfer_size: number of bytes to request from the device.
...@@ -1069,6 +1161,10 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -1069,6 +1161,10 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case USBTMC_IOCTL_ABORT_BULK_IN: case USBTMC_IOCTL_ABORT_BULK_IN:
retval = usbtmc_ioctl_abort_bulk_in(data); retval = usbtmc_ioctl_abort_bulk_in(data);
break; break;
case USBTMC488_IOCTL_READ_STB:
retval = usbtmc488_ioctl_read_stb(data, (void __user *)arg);
break;
} }
skip_io_on_zombie: skip_io_on_zombie:
...@@ -1092,6 +1188,57 @@ static struct usb_class_driver usbtmc_class = { ...@@ -1092,6 +1188,57 @@ static struct usb_class_driver usbtmc_class = {
.minor_base = USBTMC_MINOR_BASE, .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;
}
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);
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
case -EILSEQ:
case -ETIME:
/* urb terminated, clean up */
dev_dbg(dev, "urb terminated, status: %d\n", status);
return;
default:
dev_err(dev, "unknown status received: %d\n", status);
}
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);
usb_free_urb(data->iin_urb);
kref_put(&data->kref, usbtmc_delete);
}
static int usbtmc_probe(struct usb_interface *intf, static int usbtmc_probe(struct usb_interface *intf,
const struct usb_device_id *id) const struct usb_device_id *id)
...@@ -1114,6 +1261,8 @@ static int usbtmc_probe(struct usb_interface *intf, ...@@ -1114,6 +1261,8 @@ static int usbtmc_probe(struct usb_interface *intf,
usb_set_intfdata(intf, data); usb_set_intfdata(intf, data);
kref_init(&data->kref); kref_init(&data->kref);
mutex_init(&data->io_mutex); mutex_init(&data->io_mutex);
init_waitqueue_head(&data->waitq);
atomic_set(&data->iin_data_valid, 0);
data->zombie = 0; data->zombie = 0;
/* Determine if it is a Rigol or not */ /* Determine if it is a Rigol or not */
...@@ -1134,9 +1283,12 @@ static int usbtmc_probe(struct usb_interface *intf, ...@@ -1134,9 +1283,12 @@ static int usbtmc_probe(struct usb_interface *intf,
data->bTag = 1; data->bTag = 1;
data->TermCharEnabled = 0; data->TermCharEnabled = 0;
data->TermChar = '\n'; 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 */ /* USBTMC devices have only one setting, so use that */
iface_desc = data->intf->cur_altsetting; iface_desc = data->intf->cur_altsetting;
data->ifnum = iface_desc->desc.bInterfaceNumber;
/* Find bulk in endpoint */ /* Find bulk in endpoint */
for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) { for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
...@@ -1161,6 +1313,20 @@ static int usbtmc_probe(struct usb_interface *intf, ...@@ -1161,6 +1313,20 @@ static int usbtmc_probe(struct usb_interface *intf,
break; break;
} }
} }
/* Find int endpoint */
for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) {
endpoint = &iface_desc->endpoint[n].desc;
if (usb_endpoint_is_int_in(endpoint)) {
data->iin_ep_present = 1;
data->iin_ep = endpoint->bEndpointAddress;
data->iin_wMaxPacketSize = usb_endpoint_maxp(endpoint);
data->iin_interval = endpoint->bInterval;
dev_dbg(&intf->dev, "Found Int in endpoint at %u\n",
data->iin_ep);
break;
}
}
retcode = get_capabilities(data); retcode = get_capabilities(data);
if (retcode) if (retcode)
...@@ -1169,6 +1335,39 @@ static int usbtmc_probe(struct usb_interface *intf, ...@@ -1169,6 +1335,39 @@ static int usbtmc_probe(struct usb_interface *intf,
retcode = sysfs_create_group(&intf->dev.kobj, retcode = sysfs_create_group(&intf->dev.kobj,
&capability_attr_grp); &capability_attr_grp);
if (data->iin_ep_present) {
/* allocate int urb */
data->iin_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!data->iin_urb) {
dev_err(&intf->dev, "Failed to allocate int urb\n");
goto error_register;
}
/* will reference data in int urb */
kref_get(&data->kref);
/* allocate buffer for interrupt in */
data->iin_buffer = kmalloc(data->iin_wMaxPacketSize,
GFP_KERNEL);
if (!data->iin_buffer) {
dev_err(&intf->dev, "Failed to allocate int buf\n");
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 = sysfs_create_group(&intf->dev.kobj, &data_attr_grp); retcode = sysfs_create_group(&intf->dev.kobj, &data_attr_grp);
retcode = usb_register_dev(intf, &usbtmc_class); retcode = usb_register_dev(intf, &usbtmc_class);
...@@ -1185,6 +1384,7 @@ static int usbtmc_probe(struct usb_interface *intf, ...@@ -1185,6 +1384,7 @@ static int usbtmc_probe(struct usb_interface *intf,
error_register: error_register:
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
usbtmc_free_int(data);
kref_put(&data->kref, usbtmc_delete); kref_put(&data->kref, usbtmc_delete);
return retcode; return retcode;
} }
...@@ -1196,6 +1396,7 @@ static void usbtmc_disconnect(struct usb_interface *intf) ...@@ -1196,6 +1396,7 @@ static void usbtmc_disconnect(struct usb_interface *intf)
dev_dbg(&intf->dev, "usbtmc_disconnect called\n"); dev_dbg(&intf->dev, "usbtmc_disconnect called\n");
data = usb_get_intfdata(intf); data = usb_get_intfdata(intf);
usbtmc_free_int(data);
usb_deregister_dev(intf, &usbtmc_class); usb_deregister_dev(intf, &usbtmc_class);
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#define USBTMC_REQUEST_CHECK_CLEAR_STATUS 6 #define USBTMC_REQUEST_CHECK_CLEAR_STATUS 6
#define USBTMC_REQUEST_GET_CAPABILITIES 7 #define USBTMC_REQUEST_GET_CAPABILITIES 7
#define USBTMC_REQUEST_INDICATOR_PULSE 64 #define USBTMC_REQUEST_INDICATOR_PULSE 64
#define USBTMC488_REQUEST_READ_STATUS_BYTE 128
/* Request values for USBTMC driver's ioctl entry point */ /* Request values for USBTMC driver's ioctl entry point */
#define USBTMC_IOC_NR 91 #define USBTMC_IOC_NR 91
...@@ -39,5 +40,6 @@ ...@@ -39,5 +40,6 @@
#define USBTMC_IOCTL_ABORT_BULK_IN _IO(USBTMC_IOC_NR, 4) #define USBTMC_IOCTL_ABORT_BULK_IN _IO(USBTMC_IOC_NR, 4)
#define USBTMC_IOCTL_CLEAR_OUT_HALT _IO(USBTMC_IOC_NR, 6) #define USBTMC_IOCTL_CLEAR_OUT_HALT _IO(USBTMC_IOC_NR, 6)
#define USBTMC_IOCTL_CLEAR_IN_HALT _IO(USBTMC_IOC_NR, 7) #define USBTMC_IOCTL_CLEAR_IN_HALT _IO(USBTMC_IOC_NR, 7)
#define USBTMC488_IOCTL_READ_STB _IOR(USBTMC_IOC_NR, 18, unsigned char)
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment