/**
* drivers/usb/class/usbtmc.c - USB Test & Measurment class driver
*
* Copyright (C) 2007 Stefan Kopp, Gechingen, Germany
* Copyright (C) 2008 Novell, Inc.
* Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* The GNU General Public License is available at
* http://www.gnu.org/copyleft/gpl.html.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/usb.h>
#include <linux/usb/tmc.h>
#define USBTMC_MINOR_BASE 176
/*
* Size of driver internal IO buffer. Must be multiple of 4 and at least as
* large as wMaxPacketSize (which is usually 512 bytes).
*/
#define USBTMC_SIZE_IOBUFFER 2048
/* Default USB timeout (in milliseconds) */
#define USBTMC_TIMEOUT 10
/*
* 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
* packet is never read.
*/
#define USBTMC_MAX_READS_TO_CLEAR_BULK_IN 100
static struct usb_device_id usbtmc_devices[] = {
{ USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, 3, 0), },
{ 0, } /* terminating entry */
};
MODULE_DEVICE_TABLE(usb, usbtmc_devices);
/*
* This structure is the capabilities for the device
* See section 4.2.1.8 of the USBTMC specification for details.
*/
struct usbtmc_dev_capabilities {
__u8 interface_capabilities;
__u8 device_capabilities;
__u8 usb488_interface_capabilities;
__u8 usb488_device_capabilities;
};
/* This structure holds private data for each USBTMC device. One copy is
* allocated for each USBTMC device in the driver's probe function.
*/
struct usbtmc_device_data {
const struct usb_device_id *id;
struct usb_device *usb_dev;
struct usb_interface *intf;
unsigned int bulk_in;
unsigned int bulk_out;
u8 bTag;
u8 bTag_last_write; /* needed for abort */
u8 bTag_last_read; /* needed for abort */
/* attributes from the USB TMC spec for this device */
u8 TermChar;
bool TermCharEnabled;
bool auto_abort;
struct usbtmc_dev_capabilities capabilities;
struct kref kref;
struct mutex io_mutex; /* only one i/o function running at a time */
};
#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref)
/* Forward declarations */
static struct usb_driver usbtmc_driver;
static void usbtmc_delete(struct kref *kref)
{
struct usbtmc_device_data *data = to_usbtmc_data(kref);
usb_put_dev(data->usb_dev);
kfree(data);
}
static int usbtmc_open(struct inode *inode, struct file *filp)
{
struct usb_interface *intf;
struct usbtmc_device_data *data;
int retval = -ENODEV;
intf = usb_find_interface(&usbtmc_driver, iminor(inode));
if (!intf) {
printk(KERN_ERR KBUILD_MODNAME
": can not find device for minor %d", iminor(inode));
goto exit;
}
data = usb_get_intfdata(intf);
kref_get(&data->kref);
/* Store pointer in file structure's private data field */
filp->private_data = data;
exit:
return retval;
}
static int usbtmc_release(struct inode *inode, struct file *file)
{
struct usbtmc_device_data *data = file->private_data;
kref_put(&data->kref, usbtmc_delete);
return 0;
}
static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
{
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);
if (!buffer)
return -ENOMEM;
rv = usb_control_msg(data->usb_dev,
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);
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",