2017-11-03 18:28:30 +08:00
|
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2007-02-15 05:40:14 +08:00
|
|
|
|
/*
|
|
|
|
|
* Native support for the I/O-Warrior USB devices
|
|
|
|
|
*
|
|
|
|
|
* Copyright (c) 2003-2005 Code Mercenaries GmbH
|
|
|
|
|
* written by Christian Lucht <lucht@codemercs.com>
|
|
|
|
|
*
|
|
|
|
|
* based on
|
|
|
|
|
|
|
|
|
|
* usb-skeleton.c by Greg Kroah-Hartman <greg@kroah.com>
|
|
|
|
|
* brlvger.c by Stephane Dalton <sdalton@videotron.ca>
|
|
|
|
|
* and St<EFBFBD>hane Doyon <s.doyon@videotron.ca>
|
|
|
|
|
*
|
|
|
|
|
* Released under the GPLv2.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
#include <linux/usb.h>
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
|
#include <linux/sched.h>
|
2010-07-12 05:18:56 +08:00
|
|
|
|
#include <linux/mutex.h>
|
2007-02-15 05:40:14 +08:00
|
|
|
|
#include <linux/poll.h>
|
|
|
|
|
#include <linux/usb/iowarrior.h>
|
|
|
|
|
|
|
|
|
|
#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>"
|
2017-07-19 20:17:41 +08:00
|
|
|
|
#define DRIVER_DESC "USB IO-Warrior driver"
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
#define USB_VENDOR_ID_CODEMERCS 1984
|
|
|
|
|
/* low speed iowarrior */
|
|
|
|
|
#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500
|
|
|
|
|
#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501
|
|
|
|
|
#define USB_DEVICE_ID_CODEMERCS_IOWPV1 0x1511
|
|
|
|
|
#define USB_DEVICE_ID_CODEMERCS_IOWPV2 0x1512
|
|
|
|
|
/* full speed iowarrior */
|
|
|
|
|
#define USB_DEVICE_ID_CODEMERCS_IOW56 0x1503
|
|
|
|
|
|
|
|
|
|
/* Get a minor range for your devices from the usb maintainer */
|
|
|
|
|
#ifdef CONFIG_USB_DYNAMIC_MINORS
|
|
|
|
|
#define IOWARRIOR_MINOR_BASE 0
|
|
|
|
|
#else
|
2011-03-31 09:57:33 +08:00
|
|
|
|
#define IOWARRIOR_MINOR_BASE 208 // SKELETON_MINOR_BASE 192 + 16, not official yet
|
2007-02-15 05:40:14 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* interrupt input queue size */
|
|
|
|
|
#define MAX_INTERRUPT_BUFFER 16
|
|
|
|
|
/*
|
|
|
|
|
maximum number of urbs that are submitted for writes at the same time,
|
|
|
|
|
this applies to the IOWarrior56 only!
|
|
|
|
|
IOWarrior24 and IOWarrior40 use synchronous usb_control_msg calls.
|
|
|
|
|
*/
|
|
|
|
|
#define MAX_WRITES_IN_FLIGHT 4
|
|
|
|
|
|
|
|
|
|
MODULE_AUTHOR(DRIVER_AUTHOR);
|
|
|
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
|
|
/* Module parameters */
|
2010-07-12 05:18:56 +08:00
|
|
|
|
static DEFINE_MUTEX(iowarrior_mutex);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
static struct usb_driver iowarrior_driver;
|
2007-10-25 21:46:30 +08:00
|
|
|
|
static DEFINE_MUTEX(iowarrior_open_disc_lock);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
/*--------------*/
|
|
|
|
|
/* data */
|
|
|
|
|
/*--------------*/
|
|
|
|
|
|
|
|
|
|
/* Structure to hold all of our device specific stuff */
|
|
|
|
|
struct iowarrior {
|
|
|
|
|
struct mutex mutex; /* locks this structure */
|
|
|
|
|
struct usb_device *udev; /* save off the usb device pointer */
|
|
|
|
|
struct usb_interface *interface; /* the interface for this device */
|
|
|
|
|
unsigned char minor; /* the starting minor number for this device */
|
|
|
|
|
struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */
|
|
|
|
|
struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */
|
|
|
|
|
struct urb *int_in_urb; /* the urb for reading data */
|
|
|
|
|
unsigned char *int_in_buffer; /* buffer for data to be read */
|
|
|
|
|
unsigned char serial_number; /* to detect lost packages */
|
|
|
|
|
unsigned char *read_queue; /* size is MAX_INTERRUPT_BUFFER * packet size */
|
|
|
|
|
wait_queue_head_t read_wait;
|
|
|
|
|
wait_queue_head_t write_wait; /* wait-queue for writing to the device */
|
|
|
|
|
atomic_t write_busy; /* number of write-urbs submitted */
|
|
|
|
|
atomic_t read_idx;
|
|
|
|
|
atomic_t intr_idx;
|
|
|
|
|
atomic_t overflow_flag; /* signals an index 'rollover' */
|
|
|
|
|
int present; /* this is 1 as long as the device is connected */
|
|
|
|
|
int opened; /* this is 1 if the device is currently open */
|
|
|
|
|
char chip_serial[9]; /* the serial number string of the chip connected */
|
|
|
|
|
int report_size; /* number of bytes in a report */
|
|
|
|
|
u16 product_id;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*--------------*/
|
|
|
|
|
/* globals */
|
|
|
|
|
/*--------------*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* USB spec identifies 5 second timeouts.
|
|
|
|
|
*/
|
|
|
|
|
#define GET_TIMEOUT 5
|
|
|
|
|
#define USB_REQ_GET_REPORT 0x01
|
|
|
|
|
//#if 0
|
|
|
|
|
static int usb_get_report(struct usb_device *dev,
|
|
|
|
|
struct usb_host_interface *inter, unsigned char type,
|
|
|
|
|
unsigned char id, void *buf, int size)
|
|
|
|
|
{
|
|
|
|
|
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
|
|
|
|
|
USB_REQ_GET_REPORT,
|
|
|
|
|
USB_DIR_IN | USB_TYPE_CLASS |
|
|
|
|
|
USB_RECIP_INTERFACE, (type << 8) + id,
|
|
|
|
|
inter->desc.bInterfaceNumber, buf, size,
|
2007-04-05 17:13:21 +08:00
|
|
|
|
GET_TIMEOUT*HZ);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
}
|
|
|
|
|
//#endif
|
|
|
|
|
|
|
|
|
|
#define USB_REQ_SET_REPORT 0x09
|
|
|
|
|
|
|
|
|
|
static int usb_set_report(struct usb_interface *intf, unsigned char type,
|
|
|
|
|
unsigned char id, void *buf, int size)
|
|
|
|
|
{
|
|
|
|
|
return usb_control_msg(interface_to_usbdev(intf),
|
|
|
|
|
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
|
|
|
|
|
USB_REQ_SET_REPORT,
|
|
|
|
|
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
|
|
|
|
(type << 8) + id,
|
|
|
|
|
intf->cur_altsetting->desc.bInterfaceNumber, buf,
|
2007-04-05 17:13:21 +08:00
|
|
|
|
size, HZ);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*---------------------*/
|
|
|
|
|
/* driver registration */
|
|
|
|
|
/*---------------------*/
|
|
|
|
|
/* table of devices that work with this driver */
|
2010-01-10 22:34:45 +08:00
|
|
|
|
static const struct usb_device_id iowarrior_ids[] = {
|
2007-02-15 05:40:14 +08:00
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40)},
|
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24)},
|
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV1)},
|
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV2)},
|
|
|
|
|
{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56)},
|
|
|
|
|
{} /* Terminating entry */
|
|
|
|
|
};
|
|
|
|
|
MODULE_DEVICE_TABLE(usb, iowarrior_ids);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* USB callback handler for reading data
|
|
|
|
|
*/
|
|
|
|
|
static void iowarrior_callback(struct urb *urb)
|
|
|
|
|
{
|
2008-02-24 18:41:47 +08:00
|
|
|
|
struct iowarrior *dev = urb->context;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
int intr_idx;
|
|
|
|
|
int read_idx;
|
|
|
|
|
int aux_idx;
|
|
|
|
|
int offset;
|
2007-07-19 01:58:02 +08:00
|
|
|
|
int status = urb->status;
|
|
|
|
|
int retval;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
2007-07-19 01:58:02 +08:00
|
|
|
|
switch (status) {
|
2007-02-15 05:40:14 +08:00
|
|
|
|
case 0:
|
|
|
|
|
/* success */
|
|
|
|
|
break;
|
|
|
|
|
case -ECONNRESET:
|
|
|
|
|
case -ENOENT:
|
|
|
|
|
case -ESHUTDOWN:
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
intr_idx = atomic_read(&dev->intr_idx);
|
|
|
|
|
/* aux_idx become previous intr_idx */
|
|
|
|
|
aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1);
|
|
|
|
|
read_idx = atomic_read(&dev->read_idx);
|
|
|
|
|
|
|
|
|
|
/* queue is not empty and it's interface 0 */
|
|
|
|
|
if ((intr_idx != read_idx)
|
|
|
|
|
&& (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0)) {
|
|
|
|
|
/* + 1 for serial number */
|
|
|
|
|
offset = aux_idx * (dev->report_size + 1);
|
|
|
|
|
if (!memcmp
|
|
|
|
|
(dev->read_queue + offset, urb->transfer_buffer,
|
|
|
|
|
dev->report_size)) {
|
|
|
|
|
/* equal values on interface 0 will be ignored */
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* aux_idx become next intr_idx */
|
|
|
|
|
aux_idx = (intr_idx == (MAX_INTERRUPT_BUFFER - 1)) ? 0 : (intr_idx + 1);
|
|
|
|
|
if (read_idx == aux_idx) {
|
|
|
|
|
/* queue full, dropping oldest input */
|
|
|
|
|
read_idx = (++read_idx == MAX_INTERRUPT_BUFFER) ? 0 : read_idx;
|
|
|
|
|
atomic_set(&dev->read_idx, read_idx);
|
|
|
|
|
atomic_set(&dev->overflow_flag, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* +1 for serial number */
|
|
|
|
|
offset = intr_idx * (dev->report_size + 1);
|
|
|
|
|
memcpy(dev->read_queue + offset, urb->transfer_buffer,
|
|
|
|
|
dev->report_size);
|
|
|
|
|
*(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++;
|
|
|
|
|
|
|
|
|
|
atomic_set(&dev->intr_idx, aux_idx);
|
|
|
|
|
/* tell the blocking read about the new data */
|
|
|
|
|
wake_up_interruptible(&dev->read_wait);
|
|
|
|
|
|
|
|
|
|
exit:
|
2007-07-19 01:58:02 +08:00
|
|
|
|
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
|
|
if (retval)
|
2007-10-18 18:06:30 +08:00
|
|
|
|
dev_err(&dev->interface->dev, "%s - usb_submit_urb failed with result %d\n",
|
2008-03-04 08:08:34 +08:00
|
|
|
|
__func__, retval);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* USB Callback handler for write-ops
|
|
|
|
|
*/
|
|
|
|
|
static void iowarrior_write_callback(struct urb *urb)
|
|
|
|
|
{
|
|
|
|
|
struct iowarrior *dev;
|
2007-07-19 01:58:02 +08:00
|
|
|
|
int status = urb->status;
|
|
|
|
|
|
2008-02-24 18:41:47 +08:00
|
|
|
|
dev = urb->context;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
/* sync/async unlink faults aren't errors */
|
2007-07-19 01:58:02 +08:00
|
|
|
|
if (status &&
|
|
|
|
|
!(status == -ENOENT ||
|
|
|
|
|
status == -ECONNRESET || status == -ESHUTDOWN)) {
|
2014-05-21 04:09:33 +08:00
|
|
|
|
dev_dbg(&dev->interface->dev,
|
|
|
|
|
"nonzero write bulk status received: %d\n", status);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
}
|
|
|
|
|
/* free up our allocated buffer */
|
2010-04-12 19:17:25 +08:00
|
|
|
|
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
|
|
|
|
|
urb->transfer_buffer, urb->transfer_dma);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
/* tell a waiting writer the interrupt-out-pipe is available again */
|
|
|
|
|
atomic_dec(&dev->write_busy);
|
|
|
|
|
wake_up_interruptible(&dev->write_wait);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* iowarrior_delete
|
|
|
|
|
*/
|
|
|
|
|
static inline void iowarrior_delete(struct iowarrior *dev)
|
|
|
|
|
{
|
2014-05-21 04:09:33 +08:00
|
|
|
|
dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
kfree(dev->int_in_buffer);
|
|
|
|
|
usb_free_urb(dev->int_in_urb);
|
|
|
|
|
kfree(dev->read_queue);
|
2019-10-09 18:48:42 +08:00
|
|
|
|
usb_put_intf(dev->interface);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
kfree(dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*---------------------*/
|
|
|
|
|
/* fops implementation */
|
|
|
|
|
/*---------------------*/
|
|
|
|
|
|
|
|
|
|
static int read_index(struct iowarrior *dev)
|
|
|
|
|
{
|
|
|
|
|
int intr_idx, read_idx;
|
|
|
|
|
|
|
|
|
|
read_idx = atomic_read(&dev->read_idx);
|
|
|
|
|
intr_idx = atomic_read(&dev->intr_idx);
|
|
|
|
|
|
|
|
|
|
return (read_idx == intr_idx ? -1 : read_idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* iowarrior_read
|
|
|
|
|
*/
|
|
|
|
|
static ssize_t iowarrior_read(struct file *file, char __user *buffer,
|
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
|
{
|
|
|
|
|
struct iowarrior *dev;
|
|
|
|
|
int read_idx;
|
|
|
|
|
int offset;
|
|
|
|
|
|
2010-07-13 04:50:12 +08:00
|
|
|
|
dev = file->private_data;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
/* verify that the device wasn't unplugged */
|
2016-08-26 01:39:17 +08:00
|
|
|
|
if (!dev || !dev->present)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
2014-05-21 04:09:33 +08:00
|
|
|
|
dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
|
|
|
|
|
dev->minor, count);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
/* read count must be packet size (+ time stamp) */
|
|
|
|
|
if ((count != dev->report_size)
|
|
|
|
|
&& (count != (dev->report_size + 1)))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
/* repeat until no buffer overrun in callback handler occur */
|
|
|
|
|
do {
|
|
|
|
|
atomic_set(&dev->overflow_flag, 0);
|
|
|
|
|
if ((read_idx = read_index(dev)) == -1) {
|
2013-12-30 22:30:54 +08:00
|
|
|
|
/* queue empty */
|
2007-02-15 05:40:14 +08:00
|
|
|
|
if (file->f_flags & O_NONBLOCK)
|
|
|
|
|
return -EAGAIN;
|
|
|
|
|
else {
|
|
|
|
|
//next line will return when there is either new data, or the device is unplugged
|
|
|
|
|
int r = wait_event_interruptible(dev->read_wait,
|
|
|
|
|
(!dev->present
|
|
|
|
|
|| (read_idx =
|
|
|
|
|
read_index
|
|
|
|
|
(dev)) !=
|
|
|
|
|
-1));
|
|
|
|
|
if (r) {
|
|
|
|
|
//we were interrupted by a signal
|
|
|
|
|
return -ERESTART;
|
|
|
|
|
}
|
|
|
|
|
if (!dev->present) {
|
|
|
|
|
//The device was unplugged
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
|
|
|
|
if (read_idx == -1) {
|
|
|
|
|
// Can this happen ???
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
offset = read_idx * (dev->report_size + 1);
|
|
|
|
|
if (copy_to_user(buffer, dev->read_queue + offset, count)) {
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
}
|
|
|
|
|
} while (atomic_read(&dev->overflow_flag));
|
|
|
|
|
|
|
|
|
|
read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx;
|
|
|
|
|
atomic_set(&dev->read_idx, read_idx);
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* iowarrior_write
|
|
|
|
|
*/
|
|
|
|
|
static ssize_t iowarrior_write(struct file *file,
|
|
|
|
|
const char __user *user_buffer,
|
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
|
{
|
|
|
|
|
struct iowarrior *dev;
|
|
|
|
|
int retval = 0;
|
|
|
|
|
char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */
|
|
|
|
|
struct urb *int_out_urb = NULL;
|
|
|
|
|
|
2010-07-13 04:50:12 +08:00
|
|
|
|
dev = file->private_data;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
mutex_lock(&dev->mutex);
|
|
|
|
|
/* verify that the device wasn't unplugged */
|
2007-10-18 18:52:50 +08:00
|
|
|
|
if (!dev->present) {
|
2007-02-15 05:40:14 +08:00
|
|
|
|
retval = -ENODEV;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
2014-05-21 04:09:33 +08:00
|
|
|
|
dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
|
|
|
|
|
dev->minor, count);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
/* if count is 0 we're already done */
|
|
|
|
|
if (count == 0) {
|
|
|
|
|
retval = 0;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
/* We only accept full reports */
|
|
|
|
|
if (count != dev->report_size) {
|
|
|
|
|
retval = -EINVAL;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
switch (dev->product_id) {
|
|
|
|
|
case USB_DEVICE_ID_CODEMERCS_IOW24:
|
|
|
|
|
case USB_DEVICE_ID_CODEMERCS_IOWPV1:
|
|
|
|
|
case USB_DEVICE_ID_CODEMERCS_IOWPV2:
|
|
|
|
|
case USB_DEVICE_ID_CODEMERCS_IOW40:
|
|
|
|
|
/* IOW24 and IOW40 use a synchronous call */
|
2017-05-13 11:16:00 +08:00
|
|
|
|
buf = memdup_user(user_buffer, count);
|
|
|
|
|
if (IS_ERR(buf)) {
|
|
|
|
|
retval = PTR_ERR(buf);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
retval = usb_set_report(dev->interface, 2, 0, buf, count);
|
|
|
|
|
kfree(buf);
|
|
|
|
|
goto exit;
|
|
|
|
|
break;
|
|
|
|
|
case USB_DEVICE_ID_CODEMERCS_IOW56:
|
|
|
|
|
/* The IOW56 uses asynchronous IO and more urbs */
|
|
|
|
|
if (atomic_read(&dev->write_busy) == MAX_WRITES_IN_FLIGHT) {
|
|
|
|
|
/* Wait until we are below the limit for submitted urbs */
|
|
|
|
|
if (file->f_flags & O_NONBLOCK) {
|
|
|
|
|
retval = -EAGAIN;
|
|
|
|
|
goto exit;
|
|
|
|
|
} else {
|
|
|
|
|
retval = wait_event_interruptible(dev->write_wait,
|
|
|
|
|
(!dev->present || (atomic_read (&dev-> write_busy) < MAX_WRITES_IN_FLIGHT)));
|
|
|
|
|
if (retval) {
|
|
|
|
|
/* we were interrupted by a signal */
|
|
|
|
|
retval = -ERESTART;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
if (!dev->present) {
|
|
|
|
|
/* The device was unplugged */
|
|
|
|
|
retval = -ENODEV;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
if (!dev->opened) {
|
|
|
|
|
/* We were closed while waiting for an URB */
|
|
|
|
|
retval = -ENODEV;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
atomic_inc(&dev->write_busy);
|
|
|
|
|
int_out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
|
|
|
if (!int_out_urb) {
|
|
|
|
|
retval = -ENOMEM;
|
2007-03-19 18:39:13 +08:00
|
|
|
|
goto error_no_urb;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
}
|
2010-04-12 19:17:25 +08:00
|
|
|
|
buf = usb_alloc_coherent(dev->udev, dev->report_size,
|
|
|
|
|
GFP_KERNEL, &int_out_urb->transfer_dma);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
if (!buf) {
|
|
|
|
|
retval = -ENOMEM;
|
2014-05-21 04:09:33 +08:00
|
|
|
|
dev_dbg(&dev->interface->dev,
|
|
|
|
|
"Unable to allocate buffer\n");
|
2007-03-19 18:39:13 +08:00
|
|
|
|
goto error_no_buffer;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
}
|
|
|
|
|
usb_fill_int_urb(int_out_urb, dev->udev,
|
|
|
|
|
usb_sndintpipe(dev->udev,
|
|
|
|
|
dev->int_out_endpoint->bEndpointAddress),
|
|
|
|
|
buf, dev->report_size,
|
|
|
|
|
iowarrior_write_callback, dev,
|
|
|
|
|
dev->int_out_endpoint->bInterval);
|
|
|
|
|
int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
|
|
|
|
if (copy_from_user(buf, user_buffer, count)) {
|
|
|
|
|
retval = -EFAULT;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
retval = usb_submit_urb(int_out_urb, GFP_KERNEL);
|
|
|
|
|
if (retval) {
|
2014-05-21 04:09:33 +08:00
|
|
|
|
dev_dbg(&dev->interface->dev,
|
|
|
|
|
"submit error %d for urb nr.%d\n",
|
|
|
|
|
retval, atomic_read(&dev->write_busy));
|
2007-02-15 05:40:14 +08:00
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
/* submit was ok */
|
|
|
|
|
retval = count;
|
|
|
|
|
usb_free_urb(int_out_urb);
|
|
|
|
|
goto exit;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* what do we have here ? An unsupported Product-ID ? */
|
2007-10-18 18:06:30 +08:00
|
|
|
|
dev_err(&dev->interface->dev, "%s - not supported for product=0x%x\n",
|
2008-03-04 08:08:34 +08:00
|
|
|
|
__func__, dev->product_id);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
retval = -EFAULT;
|
|
|
|
|
goto exit;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
error:
|
2010-04-12 19:17:25 +08:00
|
|
|
|
usb_free_coherent(dev->udev, dev->report_size, buf,
|
|
|
|
|
int_out_urb->transfer_dma);
|
2007-03-19 18:39:13 +08:00
|
|
|
|
error_no_buffer:
|
2007-02-15 05:40:14 +08:00
|
|
|
|
usb_free_urb(int_out_urb);
|
2007-03-19 18:39:13 +08:00
|
|
|
|
error_no_urb:
|
2007-02-15 05:40:14 +08:00
|
|
|
|
atomic_dec(&dev->write_busy);
|
|
|
|
|
wake_up_interruptible(&dev->write_wait);
|
|
|
|
|
exit:
|
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* iowarrior_ioctl
|
|
|
|
|
*/
|
2008-05-23 05:06:02 +08:00
|
|
|
|
static long iowarrior_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
|
unsigned long arg)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
{
|
|
|
|
|
struct iowarrior *dev = NULL;
|
|
|
|
|
__u8 *buffer;
|
|
|
|
|
__u8 __user *user_buffer;
|
|
|
|
|
int retval;
|
|
|
|
|
int io_res; /* checks for bytes read/written and copy_to/from_user results */
|
|
|
|
|
|
2010-07-13 04:50:12 +08:00
|
|
|
|
dev = file->private_data;
|
2016-08-26 01:39:17 +08:00
|
|
|
|
if (!dev)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
buffer = kzalloc(dev->report_size, GFP_KERNEL);
|
|
|
|
|
if (!buffer)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
/* lock this object */
|
2010-07-12 05:18:56 +08:00
|
|
|
|
mutex_lock(&iowarrior_mutex);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
mutex_lock(&dev->mutex);
|
|
|
|
|
|
|
|
|
|
/* verify that the device wasn't unplugged */
|
|
|
|
|
if (!dev->present) {
|
2007-06-12 21:36:07 +08:00
|
|
|
|
retval = -ENODEV;
|
|
|
|
|
goto error_out;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2014-05-21 04:09:33 +08:00
|
|
|
|
dev_dbg(&dev->interface->dev, "minor %d, cmd 0x%.4x, arg %ld\n",
|
|
|
|
|
dev->minor, cmd, arg);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
retval = 0;
|
|
|
|
|
io_res = 0;
|
|
|
|
|
switch (cmd) {
|
|
|
|
|
case IOW_WRITE:
|
|
|
|
|
if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 ||
|
|
|
|
|
dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV1 ||
|
|
|
|
|
dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV2 ||
|
|
|
|
|
dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW40) {
|
|
|
|
|
user_buffer = (__u8 __user *)arg;
|
|
|
|
|
io_res = copy_from_user(buffer, user_buffer,
|
|
|
|
|
dev->report_size);
|
|
|
|
|
if (io_res) {
|
|
|
|
|
retval = -EFAULT;
|
|
|
|
|
} else {
|
|
|
|
|
io_res = usb_set_report(dev->interface, 2, 0,
|
|
|
|
|
buffer,
|
|
|
|
|
dev->report_size);
|
|
|
|
|
if (io_res < 0)
|
|
|
|
|
retval = io_res;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
retval = -EINVAL;
|
|
|
|
|
dev_err(&dev->interface->dev,
|
2007-10-18 18:06:30 +08:00
|
|
|
|
"ioctl 'IOW_WRITE' is not supported for product=0x%x.\n",
|
2007-02-15 05:40:14 +08:00
|
|
|
|
dev->product_id);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case IOW_READ:
|
|
|
|
|
user_buffer = (__u8 __user *)arg;
|
|
|
|
|
io_res = usb_get_report(dev->udev,
|
|
|
|
|
dev->interface->cur_altsetting, 1, 0,
|
|
|
|
|
buffer, dev->report_size);
|
|
|
|
|
if (io_res < 0)
|
|
|
|
|
retval = io_res;
|
|
|
|
|
else {
|
|
|
|
|
io_res = copy_to_user(user_buffer, buffer, dev->report_size);
|
2010-08-01 01:39:46 +08:00
|
|
|
|
if (io_res)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
retval = -EFAULT;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case IOW_GETINFO:
|
|
|
|
|
{
|
|
|
|
|
/* Report available information for the device */
|
|
|
|
|
struct iowarrior_info info;
|
|
|
|
|
/* needed for power consumption */
|
|
|
|
|
struct usb_config_descriptor *cfg_descriptor = &dev->udev->actconfig->desc;
|
|
|
|
|
|
2010-11-06 22:41:31 +08:00
|
|
|
|
memset(&info, 0, sizeof(info));
|
2007-02-15 05:40:14 +08:00
|
|
|
|
/* directly from the descriptor */
|
|
|
|
|
info.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
|
|
|
|
|
info.product = dev->product_id;
|
|
|
|
|
info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice);
|
|
|
|
|
|
|
|
|
|
/* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */
|
2017-05-11 17:36:02 +08:00
|
|
|
|
info.speed = dev->udev->speed;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber;
|
|
|
|
|
info.report_size = dev->report_size;
|
|
|
|
|
|
|
|
|
|
/* serial number string has been read earlier 8 chars or empty string */
|
|
|
|
|
memcpy(info.serial, dev->chip_serial,
|
|
|
|
|
sizeof(dev->chip_serial));
|
|
|
|
|
if (cfg_descriptor == NULL) {
|
|
|
|
|
info.power = -1; /* no information available */
|
|
|
|
|
} else {
|
|
|
|
|
/* the MaxPower is stored in units of 2mA to make it fit into a byte-value */
|
|
|
|
|
info.power = cfg_descriptor->bMaxPower * 2;
|
|
|
|
|
}
|
|
|
|
|
io_res = copy_to_user((struct iowarrior_info __user *)arg, &info,
|
|
|
|
|
sizeof(struct iowarrior_info));
|
2010-08-01 01:39:46 +08:00
|
|
|
|
if (io_res)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
retval = -EFAULT;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
/* return that we did not understand this ioctl call */
|
|
|
|
|
retval = -ENOTTY;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2007-06-12 21:36:07 +08:00
|
|
|
|
error_out:
|
2007-02-15 05:40:14 +08:00
|
|
|
|
/* unlock the device */
|
|
|
|
|
mutex_unlock(&dev->mutex);
|
2010-07-12 05:18:56 +08:00
|
|
|
|
mutex_unlock(&iowarrior_mutex);
|
2007-06-12 21:36:07 +08:00
|
|
|
|
kfree(buffer);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* iowarrior_open
|
|
|
|
|
*/
|
|
|
|
|
static int iowarrior_open(struct inode *inode, struct file *file)
|
|
|
|
|
{
|
|
|
|
|
struct iowarrior *dev = NULL;
|
|
|
|
|
struct usb_interface *interface;
|
|
|
|
|
int subminor;
|
|
|
|
|
int retval = 0;
|
|
|
|
|
|
2010-07-12 05:18:56 +08:00
|
|
|
|
mutex_lock(&iowarrior_mutex);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
subminor = iminor(inode);
|
|
|
|
|
|
|
|
|
|
interface = usb_find_interface(&iowarrior_driver, subminor);
|
|
|
|
|
if (!interface) {
|
2010-07-12 05:18:56 +08:00
|
|
|
|
mutex_unlock(&iowarrior_mutex);
|
2012-04-21 07:53:45 +08:00
|
|
|
|
printk(KERN_ERR "%s - error, can't find device for minor %d\n",
|
|
|
|
|
__func__, subminor);
|
USB: prevent char device open/deregister race
This patch (as908) adds central protection in usbcore for the
prototypical race between opening and unregistering a char device.
The spinlock used to protect the minor-numbers array is replaced with
an rwsem, which can remain locked across a call to a driver's open()
method. This guarantees that open() and deregister() will be mutually
exclusive.
The private locks currently used in several individual drivers for
this purpose are no longer necessary, and the patch removes them. The
following USB drivers are affected: usblcd, idmouse, auerswald,
legousbtower, sisusbvga/sisusb, ldusb, adutux, iowarrior, and
usb-skeleton.
As a side effect of this change, usb_deregister_dev() must not be
called while holding a lock that is acquired by open(). Unfortunately
a number of drivers do this, but luckily the solution is simple: call
usb_deregister_dev() before acquiring the lock.
In addition to these changes (and their consequent code
simplifications), the patch fixes a use-after-free bug in adutux and a
race between open() and release() in iowarrior.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-05-22 23:46:41 +08:00
|
|
|
|
return -ENODEV;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2007-10-25 21:46:30 +08:00
|
|
|
|
mutex_lock(&iowarrior_open_disc_lock);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
dev = usb_get_intfdata(interface);
|
2007-10-25 21:46:30 +08:00
|
|
|
|
if (!dev) {
|
|
|
|
|
mutex_unlock(&iowarrior_open_disc_lock);
|
2010-07-12 05:18:56 +08:00
|
|
|
|
mutex_unlock(&iowarrior_mutex);
|
USB: prevent char device open/deregister race
This patch (as908) adds central protection in usbcore for the
prototypical race between opening and unregistering a char device.
The spinlock used to protect the minor-numbers array is replaced with
an rwsem, which can remain locked across a call to a driver's open()
method. This guarantees that open() and deregister() will be mutually
exclusive.
The private locks currently used in several individual drivers for
this purpose are no longer necessary, and the patch removes them. The
following USB drivers are affected: usblcd, idmouse, auerswald,
legousbtower, sisusbvga/sisusb, ldusb, adutux, iowarrior, and
usb-skeleton.
As a side effect of this change, usb_deregister_dev() must not be
called while holding a lock that is acquired by open(). Unfortunately
a number of drivers do this, but luckily the solution is simple: call
usb_deregister_dev() before acquiring the lock.
In addition to these changes (and their consequent code
simplifications), the patch fixes a use-after-free bug in adutux and a
race between open() and release() in iowarrior.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-05-22 23:46:41 +08:00
|
|
|
|
return -ENODEV;
|
2007-10-25 21:46:30 +08:00
|
|
|
|
}
|
USB: prevent char device open/deregister race
This patch (as908) adds central protection in usbcore for the
prototypical race between opening and unregistering a char device.
The spinlock used to protect the minor-numbers array is replaced with
an rwsem, which can remain locked across a call to a driver's open()
method. This guarantees that open() and deregister() will be mutually
exclusive.
The private locks currently used in several individual drivers for
this purpose are no longer necessary, and the patch removes them. The
following USB drivers are affected: usblcd, idmouse, auerswald,
legousbtower, sisusbvga/sisusb, ldusb, adutux, iowarrior, and
usb-skeleton.
As a side effect of this change, usb_deregister_dev() must not be
called while holding a lock that is acquired by open(). Unfortunately
a number of drivers do this, but luckily the solution is simple: call
usb_deregister_dev() before acquiring the lock.
In addition to these changes (and their consequent code
simplifications), the patch fixes a use-after-free bug in adutux and a
race between open() and release() in iowarrior.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-05-22 23:46:41 +08:00
|
|
|
|
|
|
|
|
|
mutex_lock(&dev->mutex);
|
2007-10-25 21:46:30 +08:00
|
|
|
|
mutex_unlock(&iowarrior_open_disc_lock);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
/* Only one process can open each device, no sharing. */
|
|
|
|
|
if (dev->opened) {
|
|
|
|
|
retval = -EBUSY;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* setup interrupt handler for receiving values */
|
|
|
|
|
if ((retval = usb_submit_urb(dev->int_in_urb, GFP_KERNEL)) < 0) {
|
|
|
|
|
dev_err(&interface->dev, "Error %d while submitting URB\n", retval);
|
|
|
|
|
retval = -EFAULT;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
/* increment our usage count for the driver */
|
|
|
|
|
++dev->opened;
|
|
|
|
|
/* save our object in the file's private structure */
|
|
|
|
|
file->private_data = dev;
|
|
|
|
|
retval = 0;
|
|
|
|
|
|
|
|
|
|
out:
|
USB: prevent char device open/deregister race
This patch (as908) adds central protection in usbcore for the
prototypical race between opening and unregistering a char device.
The spinlock used to protect the minor-numbers array is replaced with
an rwsem, which can remain locked across a call to a driver's open()
method. This guarantees that open() and deregister() will be mutually
exclusive.
The private locks currently used in several individual drivers for
this purpose are no longer necessary, and the patch removes them. The
following USB drivers are affected: usblcd, idmouse, auerswald,
legousbtower, sisusbvga/sisusb, ldusb, adutux, iowarrior, and
usb-skeleton.
As a side effect of this change, usb_deregister_dev() must not be
called while holding a lock that is acquired by open(). Unfortunately
a number of drivers do this, but luckily the solution is simple: call
usb_deregister_dev() before acquiring the lock.
In addition to these changes (and their consequent code
simplifications), the patch fixes a use-after-free bug in adutux and a
race between open() and release() in iowarrior.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-05-22 23:46:41 +08:00
|
|
|
|
mutex_unlock(&dev->mutex);
|
2010-07-12 05:18:56 +08:00
|
|
|
|
mutex_unlock(&iowarrior_mutex);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* iowarrior_release
|
|
|
|
|
*/
|
|
|
|
|
static int iowarrior_release(struct inode *inode, struct file *file)
|
|
|
|
|
{
|
|
|
|
|
struct iowarrior *dev;
|
|
|
|
|
int retval = 0;
|
|
|
|
|
|
2010-07-13 04:50:12 +08:00
|
|
|
|
dev = file->private_data;
|
2016-08-26 01:39:17 +08:00
|
|
|
|
if (!dev)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
2014-05-21 04:09:33 +08:00
|
|
|
|
dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
/* lock our device */
|
|
|
|
|
mutex_lock(&dev->mutex);
|
|
|
|
|
|
|
|
|
|
if (dev->opened <= 0) {
|
|
|
|
|
retval = -ENODEV; /* close called more than once */
|
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
|
} else {
|
2013-04-09 01:06:50 +08:00
|
|
|
|
dev->opened = 0; /* we're closing now */
|
2007-02-15 05:40:14 +08:00
|
|
|
|
retval = 0;
|
|
|
|
|
if (dev->present) {
|
|
|
|
|
/*
|
|
|
|
|
The device is still connected so we only shutdown
|
|
|
|
|
pending read-/write-ops.
|
|
|
|
|
*/
|
|
|
|
|
usb_kill_urb(dev->int_in_urb);
|
|
|
|
|
wake_up_interruptible(&dev->read_wait);
|
|
|
|
|
wake_up_interruptible(&dev->write_wait);
|
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
|
} else {
|
|
|
|
|
/* The device was unplugged, cleanup resources */
|
|
|
|
|
mutex_unlock(&dev->mutex);
|
|
|
|
|
iowarrior_delete(dev);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-03 18:39:46 +08:00
|
|
|
|
static __poll_t iowarrior_poll(struct file *file, poll_table * wait)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
{
|
|
|
|
|
struct iowarrior *dev = file->private_data;
|
2017-07-03 18:39:46 +08:00
|
|
|
|
__poll_t mask = 0;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
if (!dev->present)
|
2018-02-12 06:34:03 +08:00
|
|
|
|
return EPOLLERR | EPOLLHUP;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
poll_wait(file, &dev->read_wait, wait);
|
|
|
|
|
poll_wait(file, &dev->write_wait, wait);
|
|
|
|
|
|
|
|
|
|
if (!dev->present)
|
2018-02-12 06:34:03 +08:00
|
|
|
|
return EPOLLERR | EPOLLHUP;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
if (read_index(dev) != -1)
|
2018-02-12 06:34:03 +08:00
|
|
|
|
mask |= EPOLLIN | EPOLLRDNORM;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
if (atomic_read(&dev->write_busy) < MAX_WRITES_IN_FLIGHT)
|
2018-02-12 06:34:03 +08:00
|
|
|
|
mask |= EPOLLOUT | EPOLLWRNORM;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
return mask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* File operations needed when we register this driver.
|
|
|
|
|
* This assumes that this driver NEEDS file operations,
|
|
|
|
|
* of course, which means that the driver is expected
|
|
|
|
|
* to have a node in the /dev directory. If the USB
|
|
|
|
|
* device were for a network interface then the driver
|
|
|
|
|
* would use "struct net_driver" instead, and a serial
|
|
|
|
|
* device would use "struct tty_driver".
|
|
|
|
|
*/
|
2008-01-23 03:51:11 +08:00
|
|
|
|
static const struct file_operations iowarrior_fops = {
|
2007-02-15 05:40:14 +08:00
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
|
.write = iowarrior_write,
|
|
|
|
|
.read = iowarrior_read,
|
2008-05-23 05:06:02 +08:00
|
|
|
|
.unlocked_ioctl = iowarrior_ioctl,
|
2007-02-15 05:40:14 +08:00
|
|
|
|
.open = iowarrior_open,
|
|
|
|
|
.release = iowarrior_release,
|
|
|
|
|
.poll = iowarrior_poll,
|
llseek: automatically add .llseek fop
All file_operations should get a .llseek operation so we can make
nonseekable_open the default for future file operations without a
.llseek pointer.
The three cases that we can automatically detect are no_llseek, seq_lseek
and default_llseek. For cases where we can we can automatically prove that
the file offset is always ignored, we use noop_llseek, which maintains
the current behavior of not returning an error from a seek.
New drivers should normally not use noop_llseek but instead use no_llseek
and call nonseekable_open at open time. Existing drivers can be converted
to do the same when the maintainer knows for certain that no user code
relies on calling seek on the device file.
The generated code is often incorrectly indented and right now contains
comments that clarify for each added line why a specific variant was
chosen. In the version that gets submitted upstream, the comments will
be gone and I will manually fix the indentation, because there does not
seem to be a way to do that using coccinelle.
Some amount of new code is currently sitting in linux-next that should get
the same modifications, which I will do at the end of the merge window.
Many thanks to Julia Lawall for helping me learn to write a semantic
patch that does all this.
===== begin semantic patch =====
// This adds an llseek= method to all file operations,
// as a preparation for making no_llseek the default.
//
// The rules are
// - use no_llseek explicitly if we do nonseekable_open
// - use seq_lseek for sequential files
// - use default_llseek if we know we access f_pos
// - use noop_llseek if we know we don't access f_pos,
// but we still want to allow users to call lseek
//
@ open1 exists @
identifier nested_open;
@@
nested_open(...)
{
<+...
nonseekable_open(...)
...+>
}
@ open exists@
identifier open_f;
identifier i, f;
identifier open1.nested_open;
@@
int open_f(struct inode *i, struct file *f)
{
<+...
(
nonseekable_open(...)
|
nested_open(...)
)
...+>
}
@ read disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ read_no_fpos disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
... when != off
}
@ write @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ write_no_fpos @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
... when != off
}
@ fops0 @
identifier fops;
@@
struct file_operations fops = {
...
};
@ has_llseek depends on fops0 @
identifier fops0.fops;
identifier llseek_f;
@@
struct file_operations fops = {
...
.llseek = llseek_f,
...
};
@ has_read depends on fops0 @
identifier fops0.fops;
identifier read_f;
@@
struct file_operations fops = {
...
.read = read_f,
...
};
@ has_write depends on fops0 @
identifier fops0.fops;
identifier write_f;
@@
struct file_operations fops = {
...
.write = write_f,
...
};
@ has_open depends on fops0 @
identifier fops0.fops;
identifier open_f;
@@
struct file_operations fops = {
...
.open = open_f,
...
};
// use no_llseek if we call nonseekable_open
////////////////////////////////////////////
@ nonseekable1 depends on !has_llseek && has_open @
identifier fops0.fops;
identifier nso ~= "nonseekable_open";
@@
struct file_operations fops = {
... .open = nso, ...
+.llseek = no_llseek, /* nonseekable */
};
@ nonseekable2 depends on !has_llseek @
identifier fops0.fops;
identifier open.open_f;
@@
struct file_operations fops = {
... .open = open_f, ...
+.llseek = no_llseek, /* open uses nonseekable */
};
// use seq_lseek for sequential files
/////////////////////////////////////
@ seq depends on !has_llseek @
identifier fops0.fops;
identifier sr ~= "seq_read";
@@
struct file_operations fops = {
... .read = sr, ...
+.llseek = seq_lseek, /* we have seq_read */
};
// use default_llseek if there is a readdir
///////////////////////////////////////////
@ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier readdir_e;
@@
// any other fop is used that changes pos
struct file_operations fops = {
... .readdir = readdir_e, ...
+.llseek = default_llseek, /* readdir is present */
};
// use default_llseek if at least one of read/write touches f_pos
/////////////////////////////////////////////////////////////////
@ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read.read_f;
@@
// read fops use offset
struct file_operations fops = {
... .read = read_f, ...
+.llseek = default_llseek, /* read accesses f_pos */
};
@ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write.write_f;
@@
// write fops use offset
struct file_operations fops = {
... .write = write_f, ...
+ .llseek = default_llseek, /* write accesses f_pos */
};
// Use noop_llseek if neither read nor write accesses f_pos
///////////////////////////////////////////////////////////
@ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
identifier write_no_fpos.write_f;
@@
// write fops use offset
struct file_operations fops = {
...
.write = write_f,
.read = read_f,
...
+.llseek = noop_llseek, /* read and write both use no f_pos */
};
@ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write_no_fpos.write_f;
@@
struct file_operations fops = {
... .write = write_f, ...
+.llseek = noop_llseek, /* write uses no f_pos */
};
@ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
@@
struct file_operations fops = {
... .read = read_f, ...
+.llseek = noop_llseek, /* read uses no f_pos */
};
@ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
@@
struct file_operations fops = {
...
+.llseek = noop_llseek, /* no read or write fn */
};
===== End semantic patch =====
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Julia Lawall <julia@diku.dk>
Cc: Christoph Hellwig <hch@infradead.org>
2010-08-16 00:52:59 +08:00
|
|
|
|
.llseek = noop_llseek,
|
2007-02-15 05:40:14 +08:00
|
|
|
|
};
|
|
|
|
|
|
2011-07-24 08:24:48 +08:00
|
|
|
|
static char *iowarrior_devnode(struct device *dev, umode_t *mode)
|
2009-04-30 21:23:42 +08:00
|
|
|
|
{
|
|
|
|
|
return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
|
|
|
|
|
}
|
|
|
|
|
|
2007-02-15 05:40:14 +08:00
|
|
|
|
/*
|
|
|
|
|
* usb class driver info in order to get a minor number from the usb core,
|
|
|
|
|
* and to have the device registered with devfs and the driver core
|
|
|
|
|
*/
|
|
|
|
|
static struct usb_class_driver iowarrior_class = {
|
|
|
|
|
.name = "iowarrior%d",
|
2009-09-19 05:01:12 +08:00
|
|
|
|
.devnode = iowarrior_devnode,
|
2007-02-15 05:40:14 +08:00
|
|
|
|
.fops = &iowarrior_fops,
|
|
|
|
|
.minor_base = IOWARRIOR_MINOR_BASE,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*---------------------------------*/
|
|
|
|
|
/* probe and disconnect functions */
|
|
|
|
|
/*---------------------------------*/
|
|
|
|
|
/**
|
|
|
|
|
* iowarrior_probe
|
|
|
|
|
*
|
|
|
|
|
* Called by the usb core when a new device is connected that it thinks
|
|
|
|
|
* this driver might be interested in.
|
|
|
|
|
*/
|
|
|
|
|
static int iowarrior_probe(struct usb_interface *interface,
|
|
|
|
|
const struct usb_device_id *id)
|
|
|
|
|
{
|
|
|
|
|
struct usb_device *udev = interface_to_usbdev(interface);
|
|
|
|
|
struct iowarrior *dev = NULL;
|
|
|
|
|
struct usb_host_interface *iface_desc;
|
|
|
|
|
int retval = -ENOMEM;
|
2017-03-17 18:35:39 +08:00
|
|
|
|
int res;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-02 03:38:34 +08:00
|
|
|
|
/* allocate memory for our device state and initialize it */
|
2007-02-15 05:40:14 +08:00
|
|
|
|
dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
|
2016-08-26 01:39:17 +08:00
|
|
|
|
if (!dev)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
|
|
mutex_init(&dev->mutex);
|
|
|
|
|
|
|
|
|
|
atomic_set(&dev->intr_idx, 0);
|
|
|
|
|
atomic_set(&dev->read_idx, 0);
|
|
|
|
|
atomic_set(&dev->overflow_flag, 0);
|
|
|
|
|
init_waitqueue_head(&dev->read_wait);
|
|
|
|
|
atomic_set(&dev->write_busy, 0);
|
|
|
|
|
init_waitqueue_head(&dev->write_wait);
|
|
|
|
|
|
|
|
|
|
dev->udev = udev;
|
2019-10-09 18:48:42 +08:00
|
|
|
|
dev->interface = usb_get_intf(interface);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
iface_desc = interface->cur_altsetting;
|
|
|
|
|
dev->product_id = le16_to_cpu(udev->descriptor.idProduct);
|
|
|
|
|
|
2017-03-17 18:35:39 +08:00
|
|
|
|
res = usb_find_last_int_in_endpoint(iface_desc, &dev->int_in_endpoint);
|
|
|
|
|
if (res) {
|
2017-03-07 23:11:03 +08:00
|
|
|
|
dev_err(&interface->dev, "no interrupt-in endpoint found\n");
|
2017-03-17 18:35:39 +08:00
|
|
|
|
retval = res;
|
2017-03-07 23:11:03 +08:00
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-07 23:11:04 +08:00
|
|
|
|
if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) {
|
2017-03-17 18:35:39 +08:00
|
|
|
|
res = usb_find_last_int_out_endpoint(iface_desc,
|
|
|
|
|
&dev->int_out_endpoint);
|
|
|
|
|
if (res) {
|
2017-03-07 23:11:04 +08:00
|
|
|
|
dev_err(&interface->dev, "no interrupt-out endpoint found\n");
|
2017-03-17 18:35:39 +08:00
|
|
|
|
retval = res;
|
2017-03-07 23:11:04 +08:00
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 01:06:50 +08:00
|
|
|
|
/* we have to check the report_size often, so remember it in the endianness suitable for our machine */
|
2011-08-23 18:12:03 +08:00
|
|
|
|
dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
|
|
|
|
|
(dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56))
|
|
|
|
|
/* IOWarrior56 has wMaxPacketSize different from report size */
|
|
|
|
|
dev->report_size = 7;
|
|
|
|
|
|
|
|
|
|
/* create the urb and buffer for reading */
|
|
|
|
|
dev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
|
2016-08-12 05:14:39 +08:00
|
|
|
|
if (!dev->int_in_urb)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
goto error;
|
|
|
|
|
dev->int_in_buffer = kmalloc(dev->report_size, GFP_KERNEL);
|
2016-08-26 01:39:17 +08:00
|
|
|
|
if (!dev->int_in_buffer)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
goto error;
|
|
|
|
|
usb_fill_int_urb(dev->int_in_urb, dev->udev,
|
|
|
|
|
usb_rcvintpipe(dev->udev,
|
|
|
|
|
dev->int_in_endpoint->bEndpointAddress),
|
|
|
|
|
dev->int_in_buffer, dev->report_size,
|
|
|
|
|
iowarrior_callback, dev,
|
|
|
|
|
dev->int_in_endpoint->bInterval);
|
|
|
|
|
/* create an internal buffer for interrupt data from the device */
|
|
|
|
|
dev->read_queue =
|
2018-08-24 01:55:27 +08:00
|
|
|
|
kmalloc_array(dev->report_size + 1, MAX_INTERRUPT_BUFFER,
|
|
|
|
|
GFP_KERNEL);
|
2016-08-26 01:39:17 +08:00
|
|
|
|
if (!dev->read_queue)
|
2007-02-15 05:40:14 +08:00
|
|
|
|
goto error;
|
|
|
|
|
/* Get the serial-number of the chip */
|
|
|
|
|
memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
|
|
|
|
|
usb_string(udev, udev->descriptor.iSerialNumber, dev->chip_serial,
|
|
|
|
|
sizeof(dev->chip_serial));
|
|
|
|
|
if (strlen(dev->chip_serial) != 8)
|
|
|
|
|
memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
|
|
|
|
|
|
|
|
|
|
/* Set the idle timeout to 0, if this is interface 0 */
|
|
|
|
|
if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) {
|
2007-04-05 17:13:21 +08:00
|
|
|
|
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
|
|
|
|
0x0A,
|
|
|
|
|
USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
|
|
|
|
|
0, NULL, 0, USB_CTRL_SET_TIMEOUT);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
}
|
|
|
|
|
/* allow device read and ioctl */
|
|
|
|
|
dev->present = 1;
|
|
|
|
|
|
|
|
|
|
/* we can register the device now, as it is ready */
|
|
|
|
|
usb_set_intfdata(interface, dev);
|
|
|
|
|
|
|
|
|
|
retval = usb_register_dev(interface, &iowarrior_class);
|
|
|
|
|
if (retval) {
|
|
|
|
|
/* something prevented us from registering this driver */
|
|
|
|
|
dev_err(&interface->dev, "Not able to get a minor for this device.\n");
|
|
|
|
|
usb_set_intfdata(interface, NULL);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dev->minor = interface->minor;
|
|
|
|
|
|
|
|
|
|
/* let the user know what node this device is now attached to */
|
|
|
|
|
dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d "
|
|
|
|
|
"now attached to iowarrior%d\n", dev->product_id, dev->chip_serial,
|
|
|
|
|
iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE);
|
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
iowarrior_delete(dev);
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* iowarrior_disconnect
|
|
|
|
|
*
|
|
|
|
|
* Called by the usb core when the device is removed from the system.
|
|
|
|
|
*/
|
|
|
|
|
static void iowarrior_disconnect(struct usb_interface *interface)
|
|
|
|
|
{
|
|
|
|
|
struct iowarrior *dev;
|
|
|
|
|
int minor;
|
|
|
|
|
|
|
|
|
|
dev = usb_get_intfdata(interface);
|
2007-10-25 21:46:30 +08:00
|
|
|
|
mutex_lock(&iowarrior_open_disc_lock);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
usb_set_intfdata(interface, NULL);
|
|
|
|
|
|
|
|
|
|
minor = dev->minor;
|
2019-08-08 17:27:28 +08:00
|
|
|
|
mutex_unlock(&iowarrior_open_disc_lock);
|
|
|
|
|
/* give back our minor - this will call close() locks need to be dropped at this point*/
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
usb_deregister_dev(interface, &iowarrior_class);
|
|
|
|
|
|
USB: prevent char device open/deregister race
This patch (as908) adds central protection in usbcore for the
prototypical race between opening and unregistering a char device.
The spinlock used to protect the minor-numbers array is replaced with
an rwsem, which can remain locked across a call to a driver's open()
method. This guarantees that open() and deregister() will be mutually
exclusive.
The private locks currently used in several individual drivers for
this purpose are no longer necessary, and the patch removes them. The
following USB drivers are affected: usblcd, idmouse, auerswald,
legousbtower, sisusbvga/sisusb, ldusb, adutux, iowarrior, and
usb-skeleton.
As a side effect of this change, usb_deregister_dev() must not be
called while holding a lock that is acquired by open(). Unfortunately
a number of drivers do this, but luckily the solution is simple: call
usb_deregister_dev() before acquiring the lock.
In addition to these changes (and their consequent code
simplifications), the patch fixes a use-after-free bug in adutux and a
race between open() and release() in iowarrior.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-05-22 23:46:41 +08:00
|
|
|
|
mutex_lock(&dev->mutex);
|
|
|
|
|
|
2007-02-15 05:40:14 +08:00
|
|
|
|
/* prevent device read, write and ioctl */
|
2019-10-09 18:48:41 +08:00
|
|
|
|
dev->present = 0;
|
2007-02-15 05:40:14 +08:00
|
|
|
|
|
|
|
|
|
if (dev->opened) {
|
|
|
|
|
/* There is a process that holds a filedescriptor to the device ,
|
|
|
|
|
so we only shutdown read-/write-ops going on.
|
|
|
|
|
Deleting the device is postponed until close() was called.
|
|
|
|
|
*/
|
|
|
|
|
usb_kill_urb(dev->int_in_urb);
|
|
|
|
|
wake_up_interruptible(&dev->read_wait);
|
|
|
|
|
wake_up_interruptible(&dev->write_wait);
|
2019-10-09 18:48:41 +08:00
|
|
|
|
mutex_unlock(&dev->mutex);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
} else {
|
|
|
|
|
/* no process is using the device, cleanup now */
|
2019-10-09 18:48:41 +08:00
|
|
|
|
mutex_unlock(&dev->mutex);
|
2007-02-15 05:40:14 +08:00
|
|
|
|
iowarrior_delete(dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n",
|
|
|
|
|
minor - IOWARRIOR_MINOR_BASE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* usb specific object needed to register this driver with the usb subsystem */
|
|
|
|
|
static struct usb_driver iowarrior_driver = {
|
|
|
|
|
.name = "iowarrior",
|
|
|
|
|
.probe = iowarrior_probe,
|
|
|
|
|
.disconnect = iowarrior_disconnect,
|
|
|
|
|
.id_table = iowarrior_ids,
|
|
|
|
|
};
|
|
|
|
|
|
2011-11-19 01:34:02 +08:00
|
|
|
|
module_usb_driver(iowarrior_driver);
|