784 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			784 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*****************************************************************************
 | 
						|
 *  File: drivers/usb/misc/vstusb.c
 | 
						|
 *
 | 
						|
 *  Purpose: Support for the bulk USB Vernier Spectrophotometers
 | 
						|
 *
 | 
						|
 *  Author:     Johnnie Peters
 | 
						|
 *              Axian Consulting
 | 
						|
 *              Beaverton, OR, USA 97005
 | 
						|
 *
 | 
						|
 *  Modified by:     EQware Engineering, Inc.
 | 
						|
 *                   Oregon City, OR, USA 97045
 | 
						|
 *
 | 
						|
 *  Copyright:  2007, 2008
 | 
						|
 *              Vernier Software & Technology
 | 
						|
 *              Beaverton, OR, USA 97005
 | 
						|
 *
 | 
						|
 *  Web:        www.vernier.com
 | 
						|
 *
 | 
						|
 *  This program is free software; you can redistribute it and/or modify
 | 
						|
 *  it under the terms of the GNU General Public License version 2 as
 | 
						|
 *  published by the Free Software Foundation.
 | 
						|
 *
 | 
						|
 *****************************************************************************/
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/errno.h>
 | 
						|
#include <linux/init.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/mutex.h>
 | 
						|
#include <linux/uaccess.h>
 | 
						|
#include <linux/usb.h>
 | 
						|
 | 
						|
#include <linux/usb/vstusb.h>
 | 
						|
 | 
						|
#define DRIVER_VERSION "VST USB Driver Version 1.5"
 | 
						|
#define DRIVER_DESC "Vernier Software Technology Bulk USB Driver"
 | 
						|
 | 
						|
#ifdef CONFIG_USB_DYNAMIC_MINORS
 | 
						|
	#define VSTUSB_MINOR_BASE	0
 | 
						|
#else
 | 
						|
	#define VSTUSB_MINOR_BASE	199
 | 
						|
#endif
 | 
						|
 | 
						|
#define USB_VENDOR_OCEANOPTICS	0x2457
 | 
						|
#define USB_VENDOR_VERNIER	0x08F7	/* Vernier Software & Technology */
 | 
						|
 | 
						|
#define USB_PRODUCT_USB2000	0x1002
 | 
						|
#define USB_PRODUCT_ADC1000_FW	0x1003	/* firmware download (renumerates) */
 | 
						|
#define USB_PRODUCT_ADC1000	0x1004
 | 
						|
#define USB_PRODUCT_HR2000_FW	0x1009	/* firmware download (renumerates) */
 | 
						|
#define USB_PRODUCT_HR2000	0x100A
 | 
						|
#define USB_PRODUCT_HR4000_FW	0x1011	/* firmware download (renumerates) */
 | 
						|
#define USB_PRODUCT_HR4000	0x1012
 | 
						|
#define USB_PRODUCT_USB650	0x1014	/* "Red Tide" */
 | 
						|
#define USB_PRODUCT_QE65000	0x1018
 | 
						|
#define USB_PRODUCT_USB4000	0x1022
 | 
						|
#define USB_PRODUCT_USB325	0x1024	/* "Vernier Spectrometer" */
 | 
						|
 | 
						|
#define USB_PRODUCT_LABPRO	0x0001
 | 
						|
#define USB_PRODUCT_LABQUEST	0x0005
 | 
						|
 | 
						|
#define VST_MAXBUFFER		(64*1024)
 | 
						|
 | 
						|
static struct usb_device_id id_table[] = {
 | 
						|
	{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB2000)},
 | 
						|
	{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_HR4000)},
 | 
						|
	{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB650)},
 | 
						|
	{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB4000)},
 | 
						|
	{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB325)},
 | 
						|
	{ USB_DEVICE(USB_VENDOR_VERNIER, USB_PRODUCT_LABQUEST)},
 | 
						|
	{ USB_DEVICE(USB_VENDOR_VERNIER, USB_PRODUCT_LABPRO)},
 | 
						|
	{},
 | 
						|
};
 | 
						|
 | 
						|
MODULE_DEVICE_TABLE(usb, id_table);
 | 
						|
 | 
						|
struct vstusb_device {
 | 
						|
	struct kref				kref;
 | 
						|
	struct mutex            lock;
 | 
						|
	struct usb_device       *usb_dev;
 | 
						|
	char                    present;
 | 
						|
	char                    isopen;
 | 
						|
	struct usb_anchor       submitted;
 | 
						|
	int                     rd_pipe;
 | 
						|
	int                     rd_timeout_ms;
 | 
						|
	int                     wr_pipe;
 | 
						|
	int                     wr_timeout_ms;
 | 
						|
};
 | 
						|
#define to_vst_dev(d) container_of(d, struct vstusb_device, kref)
 | 
						|
 | 
						|
static struct usb_driver vstusb_driver;
 | 
						|
 | 
						|
static void vstusb_delete(struct kref *kref)
 | 
						|
{
 | 
						|
	struct vstusb_device *vstdev = to_vst_dev(kref);
 | 
						|
 | 
						|
	usb_put_dev(vstdev->usb_dev);
 | 
						|
	kfree(vstdev);
 | 
						|
}
 | 
						|
 | 
						|
static int vstusb_open(struct inode *inode, struct file *file)
 | 
						|
{
 | 
						|
	struct vstusb_device *vstdev;
 | 
						|
	struct usb_interface *interface;
 | 
						|
 | 
						|
	interface = usb_find_interface(&vstusb_driver, iminor(inode));
 | 
						|
 | 
						|
	if (!interface) {
 | 
						|
		printk(KERN_ERR KBUILD_MODNAME
 | 
						|
		       ": %s - error, can't find device for minor %d\n",
 | 
						|
		       __func__, iminor(inode));
 | 
						|
		return -ENODEV;
 | 
						|
	}
 | 
						|
 | 
						|
	vstdev = usb_get_intfdata(interface);
 | 
						|
 | 
						|
	if (!vstdev)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	/* lock this device */
 | 
						|
	mutex_lock(&vstdev->lock);
 | 
						|
 | 
						|
	/* can only open one time */
 | 
						|
	if ((!vstdev->present) || (vstdev->isopen)) {
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		return -EBUSY;
 | 
						|
	}
 | 
						|
 | 
						|
	/* increment our usage count */
 | 
						|
	kref_get(&vstdev->kref);
 | 
						|
 | 
						|
	vstdev->isopen = 1;
 | 
						|
 | 
						|
	/* save device in the file's private structure */
 | 
						|
	file->private_data = vstdev;
 | 
						|
 | 
						|
	dev_dbg(&vstdev->usb_dev->dev, "%s: opened\n", __func__);
 | 
						|
 | 
						|
	mutex_unlock(&vstdev->lock);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int vstusb_release(struct inode *inode, struct file *file)
 | 
						|
{
 | 
						|
	struct vstusb_device *vstdev;
 | 
						|
 | 
						|
	vstdev = file->private_data;
 | 
						|
 | 
						|
	if (vstdev == NULL)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	mutex_lock(&vstdev->lock);
 | 
						|
 | 
						|
	vstdev->isopen = 0;
 | 
						|
 | 
						|
	dev_dbg(&vstdev->usb_dev->dev, "%s: released\n", __func__);
 | 
						|
 | 
						|
	mutex_unlock(&vstdev->lock);
 | 
						|
 | 
						|
	kref_put(&vstdev->kref, vstusb_delete);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void usb_api_blocking_completion(struct urb *urb)
 | 
						|
{
 | 
						|
	struct completion *completeit = urb->context;
 | 
						|
 | 
						|
	complete(completeit);
 | 
						|
}
 | 
						|
 | 
						|
static int vstusb_fill_and_send_urb(struct urb *urb,
 | 
						|
				    struct usb_device *usb_dev,
 | 
						|
				    unsigned int pipe, void *data,
 | 
						|
				    unsigned int len, struct completion *done)
 | 
						|
{
 | 
						|
	struct usb_host_endpoint *ep;
 | 
						|
	struct usb_host_endpoint **hostep;
 | 
						|
	unsigned int pipend;
 | 
						|
 | 
						|
	int status;
 | 
						|
 | 
						|
	hostep = usb_pipein(pipe) ? usb_dev->ep_in : usb_dev->ep_out;
 | 
						|
	pipend = usb_pipeendpoint(pipe);
 | 
						|
	ep = hostep[pipend];
 | 
						|
 | 
						|
	if (!ep || (len == 0))
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
 | 
						|
	    == USB_ENDPOINT_XFER_INT) {
 | 
						|
		pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
 | 
						|
		usb_fill_int_urb(urb, usb_dev, pipe, data, len,
 | 
						|
				 (usb_complete_t)usb_api_blocking_completion,
 | 
						|
				 NULL, ep->desc.bInterval);
 | 
						|
	} else
 | 
						|
		usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
 | 
						|
				  (usb_complete_t)usb_api_blocking_completion,
 | 
						|
				  NULL);
 | 
						|
 | 
						|
	init_completion(done);
 | 
						|
	urb->context = done;
 | 
						|
	urb->actual_length = 0;
 | 
						|
	status = usb_submit_urb(urb, GFP_KERNEL);
 | 
						|
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
static int vstusb_complete_urb(struct urb *urb, struct completion *done,
 | 
						|
			       int timeout, int *actual_length)
 | 
						|
{
 | 
						|
	unsigned long expire;
 | 
						|
	int status;
 | 
						|
 | 
						|
	expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
 | 
						|
	if (!wait_for_completion_interruptible_timeout(done, expire)) {
 | 
						|
		usb_kill_urb(urb);
 | 
						|
		status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status;
 | 
						|
 | 
						|
		dev_dbg(&urb->dev->dev,
 | 
						|
			"%s timed out on ep%d%s len=%d/%d, urb status = %d\n",
 | 
						|
			current->comm,
 | 
						|
			usb_pipeendpoint(urb->pipe),
 | 
						|
			usb_pipein(urb->pipe) ? "in" : "out",
 | 
						|
			urb->actual_length,
 | 
						|
			urb->transfer_buffer_length,
 | 
						|
			urb->status);
 | 
						|
 | 
						|
	} else {
 | 
						|
		if (signal_pending(current)) {
 | 
						|
			/* if really an error */
 | 
						|
			if (urb->status && !((urb->status == -ENOENT)     ||
 | 
						|
					     (urb->status == -ECONNRESET) ||
 | 
						|
					     (urb->status == -ESHUTDOWN))) {
 | 
						|
				status = -EINTR;
 | 
						|
				usb_kill_urb(urb);
 | 
						|
			} else {
 | 
						|
				status = 0;
 | 
						|
			}
 | 
						|
 | 
						|
			dev_dbg(&urb->dev->dev,
 | 
						|
				"%s: signal pending on ep%d%s len=%d/%d,"
 | 
						|
				"urb status = %d\n",
 | 
						|
				current->comm,
 | 
						|
				usb_pipeendpoint(urb->pipe),
 | 
						|
				usb_pipein(urb->pipe) ? "in" : "out",
 | 
						|
				urb->actual_length,
 | 
						|
				urb->transfer_buffer_length,
 | 
						|
				urb->status);
 | 
						|
 | 
						|
		} else {
 | 
						|
			status = urb->status;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (actual_length)
 | 
						|
		*actual_length = urb->actual_length;
 | 
						|
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
static ssize_t vstusb_read(struct file *file, char __user *buffer,
 | 
						|
			   size_t count, loff_t *ppos)
 | 
						|
{
 | 
						|
	struct vstusb_device *vstdev;
 | 
						|
	int cnt = -1;
 | 
						|
	void *buf;
 | 
						|
	int retval = 0;
 | 
						|
 | 
						|
	struct urb              *urb;
 | 
						|
	struct usb_device       *dev;
 | 
						|
	unsigned int            pipe;
 | 
						|
	int                     timeout;
 | 
						|
 | 
						|
	DECLARE_COMPLETION_ONSTACK(done);
 | 
						|
 | 
						|
	vstdev = file->private_data;
 | 
						|
 | 
						|
	if (vstdev == NULL)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	/* verify that we actually want to read some data */
 | 
						|
	if ((count == 0) || (count > VST_MAXBUFFER))
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	/* lock this object */
 | 
						|
	if (mutex_lock_interruptible(&vstdev->lock))
 | 
						|
		return -ERESTARTSYS;
 | 
						|
 | 
						|
	/* anyone home */
 | 
						|
	if (!vstdev->present) {
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		printk(KERN_ERR KBUILD_MODNAME
 | 
						|
		       ": %s: device not present\n", __func__);
 | 
						|
		return -ENODEV;
 | 
						|
	}
 | 
						|
 | 
						|
	/* pull out the necessary data */
 | 
						|
	dev =     vstdev->usb_dev;
 | 
						|
	pipe =    usb_rcvbulkpipe(dev, vstdev->rd_pipe);
 | 
						|
	timeout = vstdev->rd_timeout_ms;
 | 
						|
 | 
						|
	buf = kmalloc(count, GFP_KERNEL);
 | 
						|
	if (buf == NULL) {
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	urb = usb_alloc_urb(0, GFP_KERNEL);
 | 
						|
	if (!urb) {
 | 
						|
		kfree(buf);
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	usb_anchor_urb(urb, &vstdev->submitted);
 | 
						|
	retval = vstusb_fill_and_send_urb(urb, dev, pipe, buf, count, &done);
 | 
						|
	mutex_unlock(&vstdev->lock);
 | 
						|
	if (retval) {
 | 
						|
		usb_unanchor_urb(urb);
 | 
						|
		dev_err(&dev->dev, "%s: error %d filling and sending urb %d\n",
 | 
						|
			__func__, retval, pipe);
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	retval = vstusb_complete_urb(urb, &done, timeout, &cnt);
 | 
						|
	if (retval) {
 | 
						|
		dev_err(&dev->dev, "%s: error %d completing urb %d\n",
 | 
						|
			__func__, retval, pipe);
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	if (copy_to_user(buffer, buf, cnt)) {
 | 
						|
		dev_err(&dev->dev, "%s: can't copy_to_user\n", __func__);
 | 
						|
		retval = -EFAULT;
 | 
						|
	} else {
 | 
						|
		retval = cnt;
 | 
						|
		dev_dbg(&dev->dev, "%s: read %d bytes from pipe %d\n",
 | 
						|
			__func__, cnt, pipe);
 | 
						|
	}
 | 
						|
 | 
						|
exit:
 | 
						|
	usb_free_urb(urb);
 | 
						|
	kfree(buf);
 | 
						|
	return retval;
 | 
						|
}
 | 
						|
 | 
						|
static ssize_t vstusb_write(struct file *file, const char __user *buffer,
 | 
						|
			    size_t count, loff_t *ppos)
 | 
						|
{
 | 
						|
	struct vstusb_device *vstdev;
 | 
						|
	int cnt = -1;
 | 
						|
	void *buf;
 | 
						|
	int retval = 0;
 | 
						|
 | 
						|
	struct urb              *urb;
 | 
						|
	struct usb_device       *dev;
 | 
						|
	unsigned int            pipe;
 | 
						|
	int                     timeout;
 | 
						|
 | 
						|
	DECLARE_COMPLETION_ONSTACK(done);
 | 
						|
 | 
						|
	vstdev = file->private_data;
 | 
						|
 | 
						|
	if (vstdev == NULL)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	/* verify that we actually have some data to write */
 | 
						|
	if ((count == 0) || (count > VST_MAXBUFFER))
 | 
						|
		return retval;
 | 
						|
 | 
						|
	/* lock this object */
 | 
						|
	if (mutex_lock_interruptible(&vstdev->lock))
 | 
						|
		return -ERESTARTSYS;
 | 
						|
 | 
						|
	/* anyone home */
 | 
						|
	if (!vstdev->present) {
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		printk(KERN_ERR KBUILD_MODNAME
 | 
						|
		       ": %s: device not present\n", __func__);
 | 
						|
		return -ENODEV;
 | 
						|
	}
 | 
						|
 | 
						|
	/* pull out the necessary data */
 | 
						|
	dev =     vstdev->usb_dev;
 | 
						|
	pipe =    usb_sndbulkpipe(dev, vstdev->wr_pipe);
 | 
						|
	timeout = vstdev->wr_timeout_ms;
 | 
						|
 | 
						|
	buf = kmalloc(count, GFP_KERNEL);
 | 
						|
	if (buf == NULL) {
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	urb = usb_alloc_urb(0, GFP_KERNEL);
 | 
						|
	if (!urb) {
 | 
						|
		kfree(buf);
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	if (copy_from_user(buf, buffer, count)) {
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		dev_err(&dev->dev, "%s: can't copy_from_user\n", __func__);
 | 
						|
		retval = -EFAULT;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	usb_anchor_urb(urb, &vstdev->submitted);
 | 
						|
	retval = vstusb_fill_and_send_urb(urb, dev, pipe, buf, count, &done);
 | 
						|
	mutex_unlock(&vstdev->lock);
 | 
						|
	if (retval) {
 | 
						|
		usb_unanchor_urb(urb);
 | 
						|
		dev_err(&dev->dev, "%s: error %d filling and sending urb %d\n",
 | 
						|
			__func__, retval, pipe);
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	retval = vstusb_complete_urb(urb, &done, timeout, &cnt);
 | 
						|
	if (retval) {
 | 
						|
		dev_err(&dev->dev, "%s: error %d completing urb %d\n",
 | 
						|
			__func__, retval, pipe);
 | 
						|
		goto exit;
 | 
						|
	} else {
 | 
						|
		retval = cnt;
 | 
						|
		dev_dbg(&dev->dev, "%s: sent %d bytes to pipe %d\n",
 | 
						|
			__func__, cnt, pipe);
 | 
						|
	}
 | 
						|
 | 
						|
exit:
 | 
						|
	usb_free_urb(urb);
 | 
						|
	kfree(buf);
 | 
						|
	return retval;
 | 
						|
}
 | 
						|
 | 
						|
static long vstusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 | 
						|
{
 | 
						|
	int retval = 0;
 | 
						|
	int cnt = -1;
 | 
						|
	void __user *data = (void __user *)arg;
 | 
						|
	struct vstusb_args usb_data;
 | 
						|
 | 
						|
	struct vstusb_device *vstdev;
 | 
						|
	void *buffer = NULL; /* must be initialized. buffer is
 | 
						|
			      *	referenced on exit but not all
 | 
						|
			      * ioctls allocate it */
 | 
						|
 | 
						|
	struct urb              *urb = NULL; /* must be initialized. urb is
 | 
						|
					      *	referenced on exit but not all
 | 
						|
					      * ioctls allocate it */
 | 
						|
	struct usb_device       *dev;
 | 
						|
	unsigned int            pipe;
 | 
						|
	int                     timeout;
 | 
						|
 | 
						|
	DECLARE_COMPLETION_ONSTACK(done);
 | 
						|
 | 
						|
	vstdev = file->private_data;
 | 
						|
 | 
						|
	if (_IOC_TYPE(cmd) != VST_IOC_MAGIC) {
 | 
						|
		dev_warn(&vstdev->usb_dev->dev,
 | 
						|
			 "%s: ioctl command %x, bad ioctl magic %x, "
 | 
						|
			 "expected %x\n", __func__, cmd,
 | 
						|
			 _IOC_TYPE(cmd), VST_IOC_MAGIC);
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (vstdev == NULL)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	if (copy_from_user(&usb_data, data, sizeof(struct vstusb_args))) {
 | 
						|
		dev_err(&vstdev->usb_dev->dev, "%s: can't copy_from_user\n",
 | 
						|
			__func__);
 | 
						|
		return -EFAULT;
 | 
						|
	}
 | 
						|
 | 
						|
	/* lock this object */
 | 
						|
	if (mutex_lock_interruptible(&vstdev->lock)) {
 | 
						|
		retval = -ERESTARTSYS;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	/* anyone home */
 | 
						|
	if (!vstdev->present) {
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		dev_err(&vstdev->usb_dev->dev, "%s: device not present\n",
 | 
						|
			__func__);
 | 
						|
		retval = -ENODEV;
 | 
						|
		goto exit;
 | 
						|
	}
 | 
						|
 | 
						|
	/* pull out the necessary data */
 | 
						|
	dev = vstdev->usb_dev;
 | 
						|
 | 
						|
	switch (cmd) {
 | 
						|
 | 
						|
	case IOCTL_VSTUSB_CONFIG_RW:
 | 
						|
 | 
						|
		vstdev->rd_pipe = usb_data.rd_pipe;
 | 
						|
		vstdev->rd_timeout_ms = usb_data.rd_timeout_ms;
 | 
						|
		vstdev->wr_pipe = usb_data.wr_pipe;
 | 
						|
		vstdev->wr_timeout_ms = usb_data.wr_timeout_ms;
 | 
						|
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
 | 
						|
		dev_dbg(&dev->dev, "%s: setting pipes/timeouts, "
 | 
						|
			"rdpipe = %d, rdtimeout = %d, "
 | 
						|
			"wrpipe = %d, wrtimeout = %d\n", __func__,
 | 
						|
			vstdev->rd_pipe, vstdev->rd_timeout_ms,
 | 
						|
			vstdev->wr_pipe, vstdev->wr_timeout_ms);
 | 
						|
		break;
 | 
						|
 | 
						|
	case IOCTL_VSTUSB_SEND_PIPE:
 | 
						|
 | 
						|
		if ((usb_data.count == 0) || (usb_data.count > VST_MAXBUFFER)) {
 | 
						|
			mutex_unlock(&vstdev->lock);
 | 
						|
			retval = -EINVAL;
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		buffer = kmalloc(usb_data.count, GFP_KERNEL);
 | 
						|
		if (buffer == NULL) {
 | 
						|
			mutex_unlock(&vstdev->lock);
 | 
						|
			retval = -ENOMEM;
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		urb = usb_alloc_urb(0, GFP_KERNEL);
 | 
						|
		if (!urb) {
 | 
						|
			mutex_unlock(&vstdev->lock);
 | 
						|
			retval = -ENOMEM;
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		timeout = usb_data.timeout_ms;
 | 
						|
 | 
						|
		pipe = usb_sndbulkpipe(dev, usb_data.pipe);
 | 
						|
 | 
						|
		if (copy_from_user(buffer, usb_data.buffer, usb_data.count)) {
 | 
						|
			dev_err(&dev->dev, "%s: can't copy_from_user\n",
 | 
						|
				__func__);
 | 
						|
			mutex_unlock(&vstdev->lock);
 | 
						|
			retval = -EFAULT;
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		usb_anchor_urb(urb, &vstdev->submitted);
 | 
						|
		retval = vstusb_fill_and_send_urb(urb, dev, pipe, buffer,
 | 
						|
						  usb_data.count, &done);
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		if (retval) {
 | 
						|
			usb_unanchor_urb(urb);
 | 
						|
			dev_err(&dev->dev,
 | 
						|
				"%s: error %d filling and sending urb %d\n",
 | 
						|
				__func__, retval, pipe);
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		retval = vstusb_complete_urb(urb, &done, timeout, &cnt);
 | 
						|
		if (retval) {
 | 
						|
			dev_err(&dev->dev, "%s: error %d completing urb %d\n",
 | 
						|
				__func__, retval, pipe);
 | 
						|
		}
 | 
						|
 | 
						|
		break;
 | 
						|
	case IOCTL_VSTUSB_RECV_PIPE:
 | 
						|
 | 
						|
		if ((usb_data.count == 0) || (usb_data.count > VST_MAXBUFFER)) {
 | 
						|
			mutex_unlock(&vstdev->lock);
 | 
						|
			retval = -EINVAL;
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		buffer = kmalloc(usb_data.count, GFP_KERNEL);
 | 
						|
		if (buffer == NULL) {
 | 
						|
			mutex_unlock(&vstdev->lock);
 | 
						|
			retval = -ENOMEM;
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		urb = usb_alloc_urb(0, GFP_KERNEL);
 | 
						|
		if (!urb) {
 | 
						|
			mutex_unlock(&vstdev->lock);
 | 
						|
			retval = -ENOMEM;
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		timeout = usb_data.timeout_ms;
 | 
						|
 | 
						|
		pipe = usb_rcvbulkpipe(dev, usb_data.pipe);
 | 
						|
 | 
						|
		usb_anchor_urb(urb, &vstdev->submitted);
 | 
						|
		retval = vstusb_fill_and_send_urb(urb, dev, pipe, buffer,
 | 
						|
						  usb_data.count, &done);
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		if (retval) {
 | 
						|
			usb_unanchor_urb(urb);
 | 
						|
			dev_err(&dev->dev,
 | 
						|
				"%s: error %d filling and sending urb %d\n",
 | 
						|
				__func__, retval, pipe);
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		retval = vstusb_complete_urb(urb, &done, timeout, &cnt);
 | 
						|
		if (retval) {
 | 
						|
			dev_err(&dev->dev, "%s: error %d completing urb %d\n",
 | 
						|
				__func__, retval, pipe);
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		if (copy_to_user(usb_data.buffer, buffer, cnt)) {
 | 
						|
			dev_err(&dev->dev, "%s: can't copy_to_user\n",
 | 
						|
				__func__);
 | 
						|
			retval = -EFAULT;
 | 
						|
			goto exit;
 | 
						|
		}
 | 
						|
 | 
						|
		usb_data.count = cnt;
 | 
						|
		if (copy_to_user(data, &usb_data, sizeof(struct vstusb_args))) {
 | 
						|
			dev_err(&dev->dev, "%s: can't copy_to_user\n",
 | 
						|
				__func__);
 | 
						|
			retval = -EFAULT;
 | 
						|
		} else {
 | 
						|
			dev_dbg(&dev->dev, "%s: recv %zd bytes from pipe %d\n",
 | 
						|
				__func__, usb_data.count, usb_data.pipe);
 | 
						|
		}
 | 
						|
 | 
						|
		break;
 | 
						|
 | 
						|
	default:
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
		dev_warn(&dev->dev, "ioctl_vstusb: invalid ioctl cmd %x\n",
 | 
						|
			 cmd);
 | 
						|
		return -EINVAL;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
exit:
 | 
						|
	usb_free_urb(urb);
 | 
						|
	kfree(buffer);
 | 
						|
	return retval;
 | 
						|
}
 | 
						|
 | 
						|
static const struct file_operations vstusb_fops = {
 | 
						|
	.owner =                THIS_MODULE,
 | 
						|
	.read =                 vstusb_read,
 | 
						|
	.write =                vstusb_write,
 | 
						|
	.unlocked_ioctl =       vstusb_ioctl,
 | 
						|
	.compat_ioctl =         vstusb_ioctl,
 | 
						|
	.open =                 vstusb_open,
 | 
						|
	.release =              vstusb_release,
 | 
						|
};
 | 
						|
 | 
						|
static struct usb_class_driver usb_vstusb_class = {
 | 
						|
	.name =         "usb/vstusb%d",
 | 
						|
	.fops =         &vstusb_fops,
 | 
						|
	.minor_base =   VSTUSB_MINOR_BASE,
 | 
						|
};
 | 
						|
 | 
						|
static int vstusb_probe(struct usb_interface *intf,
 | 
						|
			const struct usb_device_id *id)
 | 
						|
{
 | 
						|
	struct usb_device *dev = interface_to_usbdev(intf);
 | 
						|
	struct vstusb_device *vstdev;
 | 
						|
	int i;
 | 
						|
	int retval = 0;
 | 
						|
 | 
						|
	/* allocate memory for our device state and intialize it */
 | 
						|
 | 
						|
	vstdev = kzalloc(sizeof(*vstdev), GFP_KERNEL);
 | 
						|
	if (vstdev == NULL)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	/* must do usb_get_dev() prior to kref_init() since the kref_put()
 | 
						|
	 * release function will do a usb_put_dev() */
 | 
						|
	usb_get_dev(dev);
 | 
						|
	kref_init(&vstdev->kref);
 | 
						|
	mutex_init(&vstdev->lock);
 | 
						|
 | 
						|
	i = dev->descriptor.bcdDevice;
 | 
						|
 | 
						|
	dev_dbg(&intf->dev, "Version %1d%1d.%1d%1d found at address %d\n",
 | 
						|
		(i & 0xF000) >> 12, (i & 0xF00) >> 8,
 | 
						|
		(i & 0xF0) >> 4, (i & 0xF), dev->devnum);
 | 
						|
 | 
						|
	vstdev->present = 1;
 | 
						|
	vstdev->isopen = 0;
 | 
						|
	vstdev->usb_dev = dev;
 | 
						|
	init_usb_anchor(&vstdev->submitted);
 | 
						|
 | 
						|
	usb_set_intfdata(intf, vstdev);
 | 
						|
	retval = usb_register_dev(intf, &usb_vstusb_class);
 | 
						|
	if (retval) {
 | 
						|
		dev_err(&intf->dev,
 | 
						|
			"%s: Not able to get a minor for this device.\n",
 | 
						|
			__func__);
 | 
						|
		usb_set_intfdata(intf, NULL);
 | 
						|
		kref_put(&vstdev->kref, vstusb_delete);
 | 
						|
		return retval;
 | 
						|
	}
 | 
						|
 | 
						|
	/* let the user know what node this device is now attached to */
 | 
						|
	dev_info(&intf->dev,
 | 
						|
		 "VST USB Device #%d now attached to major %d minor %d\n",
 | 
						|
		 (intf->minor - VSTUSB_MINOR_BASE), USB_MAJOR, intf->minor);
 | 
						|
 | 
						|
	dev_info(&intf->dev, "%s, %s\n", DRIVER_DESC, DRIVER_VERSION);
 | 
						|
 | 
						|
	return retval;
 | 
						|
}
 | 
						|
 | 
						|
static void vstusb_disconnect(struct usb_interface *intf)
 | 
						|
{
 | 
						|
	struct vstusb_device *vstdev = usb_get_intfdata(intf);
 | 
						|
 | 
						|
	usb_deregister_dev(intf, &usb_vstusb_class);
 | 
						|
	usb_set_intfdata(intf, NULL);
 | 
						|
 | 
						|
	if (vstdev) {
 | 
						|
 | 
						|
		mutex_lock(&vstdev->lock);
 | 
						|
		vstdev->present = 0;
 | 
						|
 | 
						|
		usb_kill_anchored_urbs(&vstdev->submitted);
 | 
						|
 | 
						|
		mutex_unlock(&vstdev->lock);
 | 
						|
 | 
						|
		kref_put(&vstdev->kref, vstusb_delete);
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static int vstusb_suspend(struct usb_interface *intf, pm_message_t message)
 | 
						|
{
 | 
						|
	struct vstusb_device *vstdev = usb_get_intfdata(intf);
 | 
						|
	int time;
 | 
						|
	if (!vstdev)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	mutex_lock(&vstdev->lock);
 | 
						|
	time = usb_wait_anchor_empty_timeout(&vstdev->submitted, 1000);
 | 
						|
	if (!time)
 | 
						|
		usb_kill_anchored_urbs(&vstdev->submitted);
 | 
						|
	mutex_unlock(&vstdev->lock);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int vstusb_resume(struct usb_interface *intf)
 | 
						|
{
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static struct usb_driver vstusb_driver = {
 | 
						|
	.name =         "vstusb",
 | 
						|
	.probe =        vstusb_probe,
 | 
						|
	.disconnect =   vstusb_disconnect,
 | 
						|
	.suspend =      vstusb_suspend,
 | 
						|
	.resume =       vstusb_resume,
 | 
						|
	.id_table = id_table,
 | 
						|
};
 | 
						|
 | 
						|
static int __init vstusb_init(void)
 | 
						|
{
 | 
						|
	int rc;
 | 
						|
 | 
						|
	rc = usb_register(&vstusb_driver);
 | 
						|
	if (rc)
 | 
						|
		printk(KERN_ERR "%s: failed to register (%d)", __func__, rc);
 | 
						|
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
 | 
						|
static void __exit vstusb_exit(void)
 | 
						|
{
 | 
						|
	usb_deregister(&vstusb_driver);
 | 
						|
}
 | 
						|
 | 
						|
module_init(vstusb_init);
 | 
						|
module_exit(vstusb_exit);
 | 
						|
 | 
						|
MODULE_AUTHOR("Dennis O'Brien/Stephen Ware");
 | 
						|
MODULE_DESCRIPTION(DRIVER_VERSION);
 | 
						|
MODULE_LICENSE("GPL");
 |