Add USBTMC_IOCTL_CTRL_REQUEST to send arbitrary requests on the
control pipe.  Used by specific applications of IVI Foundation,
Inc. to implement VISA API functions: viUsbControlIn/Out.

The maximum length of control request is set to 4k.

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

diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 83ffa5a14c3d..3b3b4284d04e 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2007 Stefan Kopp, Gechingen, Germany
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 Greg Kroah-Hartman <gre...@suse.de>
+ * Copyright (C) 2018 IVI Foundation, Inc.
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -36,6 +37,9 @@
 /* Default USB timeout (in milliseconds) */
 #define USBTMC_TIMEOUT         5000
 
+/* 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
@@ -129,6 +133,21 @@ struct usbtmc_file_data {
 /* Forward declarations */
 static struct usb_driver usbtmc_driver;
 
+#ifdef CONFIG_COMPAT
+static void __user *u64_to_uptr(u64 value)
+{
+       if (in_compat_syscall())
+               return compat_ptr(value);
+       else
+               return (void __user *)(unsigned long)value;
+}
+#else
+static inline void __user *u64_to_uptr(u64 value)
+{
+       return (void __user *)(unsigned long)value;
+}
+#endif /* CONFIG_COMPAT */
+
 static void usbtmc_delete(struct kref *kref)
 {
        struct usbtmc_device_data *data = to_usbtmc_data(kref);
@@ -1250,6 +1269,67 @@ static int usbtmc_ioctl_indicator_pulse(struct 
usbtmc_device_data *data)
        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 long res;
+
+       res = copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest));
+       if (res)
+               return -EFAULT;
+
+       buffer = kmalloc(request.req.wLength, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       if (request.req.wLength > USBTMC_BUFSIZE)
+               return -EMSGSIZE;
+
+       if (request.req.wLength) {
+               buffer = kmalloc(request.req.wLength, GFP_KERNEL);
+               if (!buffer)
+                       return -ENOMEM;
+
+               if ((request.req.bRequestType & USB_DIR_IN) == 0) {
+                       /* Send control data to device */
+                       res = copy_from_user(buffer, u64_to_uptr(request.data),
+                                            request.req.wLength);
+                       if (res) {
+                               rv = -EFAULT;
+                               goto exit;
+                       }
+               }
+       }
+
+       rv = usb_control_msg(data->usb_dev,
+                       usb_rcvctrlpipe(data->usb_dev, 0),
+                       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 && (request.req.bRequestType & USB_DIR_IN)) {
+               /* Read control data from device */
+               res = copy_to_user(u64_to_uptr(request.data), buffer, rv);
+               if (res)
+                       rv = -EFAULT;
+       }
+
+ exit:
+       kfree(buffer);
+       return rv;
+}
+
 /*
  * Get the usb timeout value
  */
@@ -1366,6 +1446,10 @@ static long usbtmc_ioctl(struct file *file, unsigned int 
cmd, unsigned long arg)
                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);
diff --git a/include/uapi/linux/usb/tmc.h b/include/uapi/linux/usb/tmc.h
index 729af2f861a4..95533118ac34 100644
--- a/include/uapi/linux/usb/tmc.h
+++ b/include/uapi/linux/usb/tmc.h
@@ -4,6 +4,7 @@
  * Copyright (C) 2008 Novell, Inc.
  * Copyright (C) 2008 Greg Kroah-Hartman <gre...@suse.de>
  * Copyright (C) 2015 Dave Penkler <dpenk...@gmail.com>
+ * Copyright (C) 2018 IVI Foundation, Inc.
  *
  * This file holds USB constants defined by the USB Device Class
  * and USB488 Subclass Definitions for Test and Measurement devices
@@ -40,6 +41,19 @@
 #define USBTMC488_REQUEST_GOTO_LOCAL                   161
 #define USBTMC488_REQUEST_LOCAL_LOCKOUT                        162
 
+struct usbtmc_request {
+       __u8 bRequestType;
+       __u8 bRequest;
+       __u16 wValue;
+       __u16 wIndex;
+       __u16 wLength;
+} __attribute__ ((packed));
+
+struct usbtmc_ctrlrequest {
+       struct usbtmc_request req;
+       __u64 data; /* pointer to user space */
+} __attribute__ ((packed));
+
 struct usbtmc_termchar {
        __u8 term_char;
        __u8 term_char_enabled;
@@ -53,6 +67,7 @@ struct usbtmc_termchar {
 #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_IN_HALT     _IO(USBTMC_IOC_NR, 7)
+#define USBTMC_IOCTL_CTRL_REQUEST      _IOWR(USBTMC_IOC_NR, 8, struct 
usbtmc_ctrlrequest)
 #define USBTMC_IOCTL_GET_TIMEOUT       _IOR(USBTMC_IOC_NR, 9, __u32)
 #define USBTMC_IOCTL_SET_TIMEOUT       _IOW(USBTMC_IOC_NR, 10, __u32)
 #define USBTMC_IOCTL_EOM_ENABLE                _IOW(USBTMC_IOC_NR, 11, __u8)
-- 
2.17.1

--
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