diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/class/usbtmc.c | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 83ffa5a14c3d..7e69bd05c631 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 <gregkh@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 @@ -1250,6 +1254,67 @@ exit: 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, 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(request.data, buffer, rv); + if (res) + rv = -EFAULT; + } + + exit: + kfree(buffer); + return rv; +} + /* * Get the usb timeout value */ @@ -1366,6 +1431,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); |