|
@@ -87,6 +87,19 @@ struct usbtmc_device_data {
|
|
|
u8 bTag_last_write; /* 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;
|
|
|
|
|
|
/* attributes from the USB TMC spec for this device */
|
|
@@ -99,6 +112,7 @@ struct usbtmc_device_data {
|
|
|
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;
|
|
|
};
|
|
|
#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref)
|
|
|
|
|
@@ -373,6 +387,84 @@ exit:
|
|
|
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.
|
|
|
* @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)
|
|
|
case USBTMC_IOCTL_ABORT_BULK_IN:
|
|
|
retval = usbtmc_ioctl_abort_bulk_in(data);
|
|
|
break;
|
|
|
+
|
|
|
+ case USBTMC488_IOCTL_READ_STB:
|
|
|
+ retval = usbtmc488_ioctl_read_stb(data, (void __user *)arg);
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
skip_io_on_zombie:
|
|
@@ -1092,6 +1188,57 @@ 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;
|
|
|
+ }
|
|
|
+ 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,
|
|
|
const struct usb_device_id *id)
|
|
@@ -1114,6 +1261,8 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|
|
usb_set_intfdata(intf, data);
|
|
|
kref_init(&data->kref);
|
|
|
mutex_init(&data->io_mutex);
|
|
|
+ init_waitqueue_head(&data->waitq);
|
|
|
+ atomic_set(&data->iin_data_valid, 0);
|
|
|
data->zombie = 0;
|
|
|
|
|
|
/* Determine if it is a Rigol or not */
|
|
@@ -1134,9 +1283,12 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|
|
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++) {
|
|
@@ -1161,6 +1313,20 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|
|
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);
|
|
|
if (retcode)
|
|
@@ -1169,6 +1335,39 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|
|
retcode = sysfs_create_group(&intf->dev.kobj,
|
|
|
&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 = usb_register_dev(intf, &usbtmc_class);
|
|
@@ -1185,6 +1384,7 @@ static int usbtmc_probe(struct usb_interface *intf,
|
|
|
error_register:
|
|
|
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
|
|
|
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
|
|
|
+ usbtmc_free_int(data);
|
|
|
kref_put(&data->kref, usbtmc_delete);
|
|
|
return retcode;
|
|
|
}
|
|
@@ -1196,6 +1396,7 @@ static void usbtmc_disconnect(struct usb_interface *intf)
|
|
|
dev_dbg(&intf->dev, "usbtmc_disconnect called\n");
|
|
|
|
|
|
data = usb_get_intfdata(intf);
|
|
|
+ usbtmc_free_int(data);
|
|
|
usb_deregister_dev(intf, &usbtmc_class);
|
|
|
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
|
|
|
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
|