- fix ioctls USBTMC_IOCTL_ABORT_BULK_OUT/IN

- add ioctls USBTMC_IOCTL_ABORT_BULK_OUT_TAG and
  USBTMC_IOCTL_ABORT_BULK_IN_TAG for test purpose.

  Useful for testing devices and client applications: The ioctls
  allow to test the abort mechanism and the response of the
  device when using different or wrong tag ids.

Signed-off-by: Guido Kiener <guido.kie...@rohde-schwarz.com>
Reviewed-by: Steve Bayless <steve_bayl...@keysight.com>
---
 drivers/usb/class/usbtmc.c   | 151 ++++++++++++++++++-----------------
 include/uapi/linux/usb/tmc.h |   2 +
 2 files changed, 81 insertions(+), 72 deletions(-)

diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 8b464598bee5..c24efe513556 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -277,18 +277,17 @@ static int usbtmc_release(struct inode *inode, struct 
file *file)
        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 bTag)
 {
        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;
 
@@ -296,86 +295,87 @@ 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);
+                            bTag, 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(&current_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 */
+       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 == max_size) {
+       if (actual == USBTMC_BUFSIZE)
+               goto usbtmc_abort_bulk_in_status;
+
+       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;
@@ -383,46 +383,30 @@ static int usbtmc_ioctl_abort_bulk_in(struct 
usbtmc_device_data *data)
        }
 
        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 bTag)
 {
        struct device *dev;
        u8 *buffer;
@@ -439,8 +423,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);
+                            bTag, data->bulk_out,
+                            buffer, 2, USB_CTRL_GET_TIMEOUT);
 
        if (rv < 0) {
                dev_err(dev, "usb_control_msg returned %d\n", rv);
@@ -459,12 +443,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);
@@ -498,6 +484,11 @@ static int usbtmc_ioctl_abort_bulk_out(struct 
usbtmc_device_data *data)
        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 usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
                                void __user *arg)
 {
@@ -2199,10 +2190,26 @@ static long usbtmc_ioctl(struct file *file, unsigned 
int cmd, unsigned long arg)
                retval = usbtmc_ioctl_abort_bulk_out(data);
                break;
 
+       case USBTMC_IOCTL_ABORT_BULK_OUT_TAG:
+               if (get_user(tmp_byte, (__u8 __user *)arg)) {
+                       retval = -EFAULT;
+                       break;
+               }
+               retval = usbtmc_ioctl_abort_bulk_out_tag(data, tmp_byte);
+               break;
+
        case USBTMC_IOCTL_ABORT_BULK_IN:
                retval = usbtmc_ioctl_abort_bulk_in(data);
                break;
 
+       case USBTMC_IOCTL_ABORT_BULK_IN_TAG:
+               if (get_user(tmp_byte, (__u8 __user *)arg)) {
+                       retval = -EFAULT;
+                       break;
+               }
+               retval = usbtmc_ioctl_abort_bulk_in_tag(data, tmp_byte);
+               break;
+
        case USBTMC_IOCTL_CTRL_REQUEST:
                retval = usbtmc_ioctl_request(data, (void __user *)arg);
                break;
diff --git a/include/uapi/linux/usb/tmc.h b/include/uapi/linux/usb/tmc.h
index 886fabf5dfea..98d756489302 100644
--- a/include/uapi/linux/usb/tmc.h
+++ b/include/uapi/linux/usb/tmc.h
@@ -104,6 +104,8 @@ struct usbtmc_message {
 /* For test purpose only */
 #define USBTMC_IOCTL_SET_OUT_HALT      _IO(USBTMC_IOC_NR, 30)
 #define USBTMC_IOCTL_SET_IN_HALT       _IO(USBTMC_IOC_NR, 31)
+#define USBTMC_IOCTL_ABORT_BULK_OUT_TAG        _IOW(USBTMC_IOC_NR, 32, __u8)
+#define USBTMC_IOCTL_ABORT_BULK_IN_TAG _IOW(USBTMC_IOC_NR, 33, __u8)
 
 /* Cancel and cleanup asynchronous calls */
 #define USBTMC_IOCTL_CANCEL_IO         _IO(USBTMC_IOC_NR, 35)
-- 
2.17.0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to