1946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*
2946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *  Native support for the I/O-Warrior USB devices
3946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *
4946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *  Copyright (c) 2003-2005  Code Mercenaries GmbH
5946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *  written by Christian Lucht <lucht@codemercs.com>
6946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *
7946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *  based on
8946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
9946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *  usb-skeleton.c by Greg Kroah-Hartman  <greg@kroah.com>
10946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *  brlvger.c by Stephane Dalton  <sdalton@videotron.ca>
11946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *           and St�hane Doyon   <s.doyon@videotron.ca>
12946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *
13946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *  Released under the GPLv2.
14946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
15946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
16946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#include <linux/module.h>
17946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#include <linux/usb.h>
18946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#include <linux/init.h>
19946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#include <linux/slab.h>
20946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#include <linux/sched.h>
21925ce689bb31960c839804c19ef38d676f1939b9Arnd Bergmann#include <linux/mutex.h>
22946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#include <linux/poll.h>
23946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#include <linux/usb/iowarrior.h>
24946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
25946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* Version Information */
26946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define DRIVER_VERSION "v0.4.0"
27946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>"
28946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define DRIVER_DESC "USB IO-Warrior driver (Linux 2.6.x)"
29946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
30946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define USB_VENDOR_ID_CODEMERCS		1984
31946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* low speed iowarrior */
32946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define USB_DEVICE_ID_CODEMERCS_IOW40	0x1500
33946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define USB_DEVICE_ID_CODEMERCS_IOW24	0x1501
34946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define USB_DEVICE_ID_CODEMERCS_IOWPV1	0x1511
35946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define USB_DEVICE_ID_CODEMERCS_IOWPV2	0x1512
36946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* full speed iowarrior */
37946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define USB_DEVICE_ID_CODEMERCS_IOW56	0x1503
38946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
39946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* Get a minor range for your devices from the usb maintainer */
40946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#ifdef CONFIG_USB_DYNAMIC_MINORS
41946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define IOWARRIOR_MINOR_BASE	0
42946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#else
4325985edcedea6396277003854657b5f3cb31a628Lucas De Marchi#define IOWARRIOR_MINOR_BASE	208	// SKELETON_MINOR_BASE 192 + 16, not official yet
44946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#endif
45946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
46946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* interrupt input queue size */
47946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define MAX_INTERRUPT_BUFFER 16
48946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*
49946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman   maximum number of urbs that are submitted for writes at the same time,
50946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman   this applies to the IOWarrior56 only!
51946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman   IOWarrior24 and IOWarrior40 use synchronous usb_control_msg calls.
52946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman*/
53946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define MAX_WRITES_IN_FLIGHT 4
54946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
55946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* Use our own dbg macro */
56946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#undef dbg
57946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define dbg( format, arg... ) do { if( debug ) printk( KERN_DEBUG __FILE__ ": " format "\n" , ## arg ); } while ( 0 )
58946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
59946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-HartmanMODULE_AUTHOR(DRIVER_AUTHOR);
60946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-HartmanMODULE_DESCRIPTION(DRIVER_DESC);
61946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-HartmanMODULE_LICENSE("GPL");
62946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
63946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* Module parameters */
64925ce689bb31960c839804c19ef38d676f1939b9Arnd Bergmannstatic DEFINE_MUTEX(iowarrior_mutex);
6590ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool debug = 0;
66946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanmodule_param(debug, bool, 0644);
67946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-HartmanMODULE_PARM_DESC(debug, "debug=1 enables debugging messages");
68946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
69946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic struct usb_driver iowarrior_driver;
7003f36e885fc26cb0ea299fb6df5171a51e814548Oliver Neukumstatic DEFINE_MUTEX(iowarrior_open_disc_lock);
71946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
72946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*--------------*/
73946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*     data     */
74946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*--------------*/
75946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
76946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* Structure to hold all of our device specific stuff */
77946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstruct iowarrior {
78946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct mutex mutex;			/* locks this structure */
79946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct usb_device *udev;		/* save off the usb device pointer */
80946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct usb_interface *interface;	/* the interface for this device */
81946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	unsigned char minor;			/* the starting minor number for this device */
82946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct usb_endpoint_descriptor *int_out_endpoint;	/* endpoint for reading (needed for IOW56 only) */
83946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct usb_endpoint_descriptor *int_in_endpoint;	/* endpoint for reading */
84946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct urb *int_in_urb;		/* the urb for reading data */
85946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	unsigned char *int_in_buffer;	/* buffer for data to be read */
86946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	unsigned char serial_number;	/* to detect lost packages */
87946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	unsigned char *read_queue;	/* size is MAX_INTERRUPT_BUFFER * packet size */
88946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	wait_queue_head_t read_wait;
89946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	wait_queue_head_t write_wait;	/* wait-queue for writing to the device */
90946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_t write_busy;		/* number of write-urbs submitted */
91946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_t read_idx;
92946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_t intr_idx;
93946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	spinlock_t intr_idx_lock;	/* protects intr_idx */
94946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_t overflow_flag;		/* signals an index 'rollover' */
95946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int present;			/* this is 1 as long as the device is connected */
96946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int opened;			/* this is 1 if the device is currently open */
97946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	char chip_serial[9];		/* the serial number string of the chip connected */
98946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int report_size;		/* number of bytes in a report */
99946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	u16 product_id;
100946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman};
101946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
102946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*--------------*/
103946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*    globals   */
104946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*--------------*/
105946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
106946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*
107946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *  USB spec identifies 5 second timeouts.
108946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
109946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define GET_TIMEOUT 5
110946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define USB_REQ_GET_REPORT  0x01
111946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman//#if 0
112946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic int usb_get_report(struct usb_device *dev,
113946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			  struct usb_host_interface *inter, unsigned char type,
114946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			  unsigned char id, void *buf, int size)
115946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
116946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
117946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       USB_REQ_GET_REPORT,
118946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       USB_DIR_IN | USB_TYPE_CLASS |
119946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       USB_RECIP_INTERFACE, (type << 8) + id,
120946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       inter->desc.bInterfaceNumber, buf, size,
121147c5a17338fc72a89452f0a6c14ae6fcf853919Eberhard Fahle			       GET_TIMEOUT*HZ);
122946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
123946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman//#endif
124946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
125946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman#define USB_REQ_SET_REPORT 0x09
126946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
127946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic int usb_set_report(struct usb_interface *intf, unsigned char type,
128946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			  unsigned char id, void *buf, int size)
129946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
130946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return usb_control_msg(interface_to_usbdev(intf),
131946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       usb_sndctrlpipe(interface_to_usbdev(intf), 0),
132946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       USB_REQ_SET_REPORT,
133946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       USB_TYPE_CLASS | USB_RECIP_INTERFACE,
134946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       (type << 8) + id,
135946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       intf->cur_altsetting->desc.bInterfaceNumber, buf,
136147c5a17338fc72a89452f0a6c14ae6fcf853919Eberhard Fahle			       size, HZ);
137946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
138946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
139946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*---------------------*/
140946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* driver registration */
141946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*---------------------*/
142946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* table of devices that work with this driver */
14333b9e16243fd69493be3ddda7be73226c8be586aNémeth Mártonstatic const struct usb_device_id iowarrior_ids[] = {
144946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40)},
145946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24)},
146946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV1)},
147946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV2)},
148946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56)},
149946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	{}			/* Terminating entry */
150946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman};
151946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-HartmanMODULE_DEVICE_TABLE(usb, iowarrior_ids);
152946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
153946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*
154946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * USB callback handler for reading data
155946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
156946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic void iowarrior_callback(struct urb *urb)
157946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
158cdc97792289179974af6dda781c855696358d307Ming Lei	struct iowarrior *dev = urb->context;
159946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int intr_idx;
160946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int read_idx;
161946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int aux_idx;
162946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int offset;
163fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman	int status = urb->status;
164fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman	int retval;
165946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
166fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman	switch (status) {
167946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case 0:
168946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* success */
169946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		break;
170946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case -ECONNRESET:
171946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case -ENOENT:
172946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case -ESHUTDOWN:
173946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		return;
174946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	default:
175946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto exit;
176946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
177946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
178946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	spin_lock(&dev->intr_idx_lock);
179946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	intr_idx = atomic_read(&dev->intr_idx);
180946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* aux_idx become previous intr_idx */
181946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1);
182946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	read_idx = atomic_read(&dev->read_idx);
183946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
184946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* queue is not empty and it's interface 0 */
185946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if ((intr_idx != read_idx)
186946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	    && (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0)) {
187946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* + 1 for serial number */
188946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		offset = aux_idx * (dev->report_size + 1);
189946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (!memcmp
190946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		    (dev->read_queue + offset, urb->transfer_buffer,
191946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		     dev->report_size)) {
192946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/* equal values on interface 0 will be ignored */
193946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			spin_unlock(&dev->intr_idx_lock);
194946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			goto exit;
195946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
196946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
197946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
198946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* aux_idx become next intr_idx */
199946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	aux_idx = (intr_idx == (MAX_INTERRUPT_BUFFER - 1)) ? 0 : (intr_idx + 1);
200946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (read_idx == aux_idx) {
201946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* queue full, dropping oldest input */
202946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		read_idx = (++read_idx == MAX_INTERRUPT_BUFFER) ? 0 : read_idx;
203946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		atomic_set(&dev->read_idx, read_idx);
204946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		atomic_set(&dev->overflow_flag, 1);
205946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
206946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
207946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* +1 for serial number */
208946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	offset = intr_idx * (dev->report_size + 1);
209946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	memcpy(dev->read_queue + offset, urb->transfer_buffer,
210946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	       dev->report_size);
211946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	*(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++;
212946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
213946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_set(&dev->intr_idx, aux_idx);
214946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	spin_unlock(&dev->intr_idx_lock);
215946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* tell the blocking read about the new data */
216946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	wake_up_interruptible(&dev->read_wait);
217946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
218946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanexit:
219fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman	retval = usb_submit_urb(urb, GFP_ATOMIC);
220fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman	if (retval)
221898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_err(&dev->interface->dev, "%s - usb_submit_urb failed with result %d\n",
222441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison			__func__, retval);
223946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
224946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
225946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
226946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*
227946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * USB Callback handler for write-ops
228946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
229946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic void iowarrior_write_callback(struct urb *urb)
230946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
231946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct iowarrior *dev;
232fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman	int status = urb->status;
233fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman
234cdc97792289179974af6dda781c855696358d307Ming Lei	dev = urb->context;
235946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* sync/async unlink faults aren't errors */
236fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman	if (status &&
237fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman	    !(status == -ENOENT ||
238fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman	      status == -ECONNRESET || status == -ESHUTDOWN)) {
239946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		dbg("%s - nonzero write bulk status received: %d",
240fb3abee69de583180348b9029378e31574c31cfdGreg Kroah-Hartman		    __func__, status);
241946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
242946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* free up our allocated buffer */
243997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
244997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack			  urb->transfer_buffer, urb->transfer_dma);
245946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* tell a waiting writer the interrupt-out-pipe is available again */
246946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_dec(&dev->write_busy);
247946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	wake_up_interruptible(&dev->write_wait);
248946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
249946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
250946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/**
251946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *	iowarrior_delete
252946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
253946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic inline void iowarrior_delete(struct iowarrior *dev)
254946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
255946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dbg("%s - minor %d", __func__, dev->minor);
256946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	kfree(dev->int_in_buffer);
257946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	usb_free_urb(dev->int_in_urb);
258946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	kfree(dev->read_queue);
259946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	kfree(dev);
260946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
261946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
262946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*---------------------*/
263946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* fops implementation */
264946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*---------------------*/
265946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
266946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic int read_index(struct iowarrior *dev)
267946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
268946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int intr_idx, read_idx;
269946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
270946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	read_idx = atomic_read(&dev->read_idx);
271946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	intr_idx = atomic_read(&dev->intr_idx);
272946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
273946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return (read_idx == intr_idx ? -1 : read_idx);
274946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
275946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
276946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/**
277946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *  iowarrior_read
278946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
279946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic ssize_t iowarrior_read(struct file *file, char __user *buffer,
280946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			      size_t count, loff_t *ppos)
281946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
282946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct iowarrior *dev;
283946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int read_idx;
284946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int offset;
285946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
2865bd6e8b3fb787b7337b681aaa601e5c7bdc67c55Joe Perches	dev = file->private_data;
287946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
288946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* verify that the device wasn't unplugged */
289946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (dev == NULL || !dev->present)
290946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		return -ENODEV;
291946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
292946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dbg("%s - minor %d, count = %zd", __func__, dev->minor, count);
293946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
294946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* read count must be packet size (+ time stamp) */
295946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if ((count != dev->report_size)
296946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	    && (count != (dev->report_size + 1)))
297946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		return -EINVAL;
298946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
299946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* repeat until no buffer overrun in callback handler occur */
300946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	do {
301946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		atomic_set(&dev->overflow_flag, 0);
302946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if ((read_idx = read_index(dev)) == -1) {
303946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/* queue emty */
304946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			if (file->f_flags & O_NONBLOCK)
305946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				return -EAGAIN;
306946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			else {
307946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				//next line will return when there is either new data, or the device is unplugged
308946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				int r = wait_event_interruptible(dev->read_wait,
309946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman								 (!dev->present
310946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman								  || (read_idx =
311946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman								      read_index
312946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman								      (dev)) !=
313946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman								  -1));
314946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				if (r) {
315946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					//we were interrupted by a signal
316946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					return -ERESTART;
317946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				}
318946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				if (!dev->present) {
319946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					//The device was unplugged
320946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					return -ENODEV;
321946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				}
322946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				if (read_idx == -1) {
323946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					// Can this happen ???
324946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					return 0;
325946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				}
326946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			}
327946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
328946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
329946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		offset = read_idx * (dev->report_size + 1);
330946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (copy_to_user(buffer, dev->read_queue + offset, count)) {
331946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			return -EFAULT;
332946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
333946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	} while (atomic_read(&dev->overflow_flag));
334946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
335946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx;
336946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_set(&dev->read_idx, read_idx);
337946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return count;
338946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
339946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
340946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*
341946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * iowarrior_write
342946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
343946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic ssize_t iowarrior_write(struct file *file,
344946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       const char __user *user_buffer,
345946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       size_t count, loff_t *ppos)
346946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
347946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct iowarrior *dev;
348946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int retval = 0;
349946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	char *buf = NULL;	/* for IOW24 and IOW56 we need a buffer */
350946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct urb *int_out_urb = NULL;
351946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
3525bd6e8b3fb787b7337b681aaa601e5c7bdc67c55Joe Perches	dev = file->private_data;
353946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
354946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	mutex_lock(&dev->mutex);
355946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* verify that the device wasn't unplugged */
356e28c6a77061ab28bd2f0b57e400e3e58cd3474caAdrian Bunk	if (!dev->present) {
357946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = -ENODEV;
358946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto exit;
359946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
360946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dbg("%s - minor %d, count = %zd", __func__, dev->minor, count);
361946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* if count is 0 we're already done */
362946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (count == 0) {
363946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = 0;
364946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto exit;
365946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
366946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* We only accept full reports */
367946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (count != dev->report_size) {
368946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = -EINVAL;
369946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto exit;
370946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
371946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	switch (dev->product_id) {
372946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case USB_DEVICE_ID_CODEMERCS_IOW24:
373946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case USB_DEVICE_ID_CODEMERCS_IOWPV1:
374946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case USB_DEVICE_ID_CODEMERCS_IOWPV2:
375946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case USB_DEVICE_ID_CODEMERCS_IOW40:
376946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* IOW24 and IOW40 use a synchronous call */
3773ed780117dbe5acb64280d218f0347f238dafed0Kees Cook		buf = kmalloc(count, GFP_KERNEL);
378946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (!buf) {
379946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			retval = -ENOMEM;
380946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			goto exit;
381946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
382946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (copy_from_user(buf, user_buffer, count)) {
383946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			retval = -EFAULT;
384946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			kfree(buf);
385946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			goto exit;
386946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
387946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = usb_set_report(dev->interface, 2, 0, buf, count);
388946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		kfree(buf);
389946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto exit;
390946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		break;
391946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case USB_DEVICE_ID_CODEMERCS_IOW56:
392946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* The IOW56 uses asynchronous IO and more urbs */
393946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (atomic_read(&dev->write_busy) == MAX_WRITES_IN_FLIGHT) {
394946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/* Wait until we are below the limit for submitted urbs */
395946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			if (file->f_flags & O_NONBLOCK) {
396946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				retval = -EAGAIN;
397946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				goto exit;
398946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			} else {
399946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				retval = wait_event_interruptible(dev->write_wait,
400946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman								  (!dev->present || (atomic_read (&dev-> write_busy) < MAX_WRITES_IN_FLIGHT)));
401946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				if (retval) {
402946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					/* we were interrupted by a signal */
403946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					retval = -ERESTART;
404946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					goto exit;
405946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				}
406946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				if (!dev->present) {
407946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					/* The device was unplugged */
408946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					retval = -ENODEV;
409946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					goto exit;
410946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				}
411946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				if (!dev->opened) {
412946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					/* We were closed while waiting for an URB */
413946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					retval = -ENODEV;
414946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					goto exit;
415946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				}
416946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			}
417946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
418946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		atomic_inc(&dev->write_busy);
419946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		int_out_urb = usb_alloc_urb(0, GFP_KERNEL);
420946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (!int_out_urb) {
421946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			retval = -ENOMEM;
422946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			dbg("%s Unable to allocate urb ", __func__);
423f81ee4d52880b08c213982df5041217212689960Oliver Neukum			goto error_no_urb;
424946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
425997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack		buf = usb_alloc_coherent(dev->udev, dev->report_size,
426997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack					 GFP_KERNEL, &int_out_urb->transfer_dma);
427946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (!buf) {
428946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			retval = -ENOMEM;
429946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			dbg("%s Unable to allocate buffer ", __func__);
430f81ee4d52880b08c213982df5041217212689960Oliver Neukum			goto error_no_buffer;
431946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
432946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		usb_fill_int_urb(int_out_urb, dev->udev,
433946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				 usb_sndintpipe(dev->udev,
434946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman						dev->int_out_endpoint->bEndpointAddress),
435946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				 buf, dev->report_size,
436946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				 iowarrior_write_callback, dev,
437946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				 dev->int_out_endpoint->bInterval);
438946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
439946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (copy_from_user(buf, user_buffer, count)) {
440946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			retval = -EFAULT;
441946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			goto error;
442946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
443946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = usb_submit_urb(int_out_urb, GFP_KERNEL);
444946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (retval) {
445946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			dbg("%s submit error %d for urb nr.%d", __func__,
446946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			    retval, atomic_read(&dev->write_busy));
447946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			goto error;
448946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
449946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* submit was ok */
450946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = count;
451946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		usb_free_urb(int_out_urb);
452946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto exit;
453946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		break;
454946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	default:
455946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* what do we have here ? An unsupported Product-ID ? */
456898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_err(&dev->interface->dev, "%s - not supported for product=0x%x\n",
457441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison			__func__, dev->product_id);
458946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = -EFAULT;
459946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto exit;
460946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		break;
461946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
462946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanerror:
463997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack	usb_free_coherent(dev->udev, dev->report_size, buf,
464997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack			  int_out_urb->transfer_dma);
465f81ee4d52880b08c213982df5041217212689960Oliver Neukumerror_no_buffer:
466946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	usb_free_urb(int_out_urb);
467f81ee4d52880b08c213982df5041217212689960Oliver Neukumerror_no_urb:
468946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_dec(&dev->write_busy);
469946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	wake_up_interruptible(&dev->write_wait);
470946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanexit:
471946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	mutex_unlock(&dev->mutex);
472946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return retval;
473946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
474946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
475946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/**
476946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *	iowarrior_ioctl
477946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
478824f16fda56a88267aba9b2580d7566cf56a0860Alan Coxstatic long iowarrior_ioctl(struct file *file, unsigned int cmd,
479824f16fda56a88267aba9b2580d7566cf56a0860Alan Cox							unsigned long arg)
480946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
481946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct iowarrior *dev = NULL;
482946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	__u8 *buffer;
483946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	__u8 __user *user_buffer;
484946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int retval;
485946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int io_res;		/* checks for bytes read/written and copy_to/from_user results */
486946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
4875bd6e8b3fb787b7337b681aaa601e5c7bdc67c55Joe Perches	dev = file->private_data;
488946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (dev == NULL) {
489946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		return -ENODEV;
490946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
491946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
492946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	buffer = kzalloc(dev->report_size, GFP_KERNEL);
493946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (!buffer)
494946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		return -ENOMEM;
495946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
496946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* lock this object */
497925ce689bb31960c839804c19ef38d676f1939b9Arnd Bergmann	mutex_lock(&iowarrior_mutex);
498946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	mutex_lock(&dev->mutex);
499946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
500946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* verify that the device wasn't unplugged */
501946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (!dev->present) {
502fc0f8fc9be654bbff08ede04a49bd8f9805b9e13Oliver Neukum		retval = -ENODEV;
503fc0f8fc9be654bbff08ede04a49bd8f9805b9e13Oliver Neukum		goto error_out;
504946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
505946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
506946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dbg("%s - minor %d, cmd 0x%.4x, arg %ld", __func__, dev->minor, cmd,
507946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	    arg);
508946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
509946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	retval = 0;
510946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	io_res = 0;
511946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	switch (cmd) {
512946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case IOW_WRITE:
513946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 ||
514946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		    dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV1 ||
515946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		    dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV2 ||
516946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		    dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW40) {
517946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			user_buffer = (__u8 __user *)arg;
518946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			io_res = copy_from_user(buffer, user_buffer,
519946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman						dev->report_size);
520946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			if (io_res) {
521946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				retval = -EFAULT;
522946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			} else {
523946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				io_res = usb_set_report(dev->interface, 2, 0,
524946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman							buffer,
525946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman							dev->report_size);
526946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				if (io_res < 0)
527946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					retval = io_res;
528946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			}
529946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		} else {
530946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			retval = -EINVAL;
531946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			dev_err(&dev->interface->dev,
532898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches				"ioctl 'IOW_WRITE' is not supported for product=0x%x.\n",
533946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				dev->product_id);
534946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
535946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		break;
536946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case IOW_READ:
537946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		user_buffer = (__u8 __user *)arg;
538946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		io_res = usb_get_report(dev->udev,
539946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					dev->interface->cur_altsetting, 1, 0,
540946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					buffer, dev->report_size);
541946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (io_res < 0)
542946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			retval = io_res;
543946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		else {
544946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			io_res = copy_to_user(user_buffer, buffer, dev->report_size);
5456d4d4554863b7897f2bc9cd9085f54c819152825Kulikov Vasiliy			if (io_res)
546946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				retval = -EFAULT;
547946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
548946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		break;
549946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	case IOW_GETINFO:
550946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		{
551946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/* Report available information for the device */
552946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			struct iowarrior_info info;
553946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/* needed for power consumption */
554946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			struct usb_config_descriptor *cfg_descriptor = &dev->udev->actconfig->desc;
555946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
556eca67aaeebd6e5d22b0d991af1dd0424dc703bfbVasiliy Kulikov			memset(&info, 0, sizeof(info));
557946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/* directly from the descriptor */
558946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			info.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
559946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			info.product = dev->product_id;
560946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice);
561946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
562946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */
563946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			info.speed = le16_to_cpu(dev->udev->speed);
564946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber;
565946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			info.report_size = dev->report_size;
566946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
567946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/* serial number string has been read earlier 8 chars or empty string */
568946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			memcpy(info.serial, dev->chip_serial,
569946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			       sizeof(dev->chip_serial));
570946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			if (cfg_descriptor == NULL) {
571946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				info.power = -1;	/* no information available */
572946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			} else {
573946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				/* the MaxPower is stored in units of 2mA to make it fit into a byte-value */
574946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				info.power = cfg_descriptor->bMaxPower * 2;
575946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			}
576946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			io_res = copy_to_user((struct iowarrior_info __user *)arg, &info,
577946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					 sizeof(struct iowarrior_info));
5786d4d4554863b7897f2bc9cd9085f54c819152825Kulikov Vasiliy			if (io_res)
579946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman				retval = -EFAULT;
580946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			break;
581946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
582946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	default:
583946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* return that we did not understand this ioctl call */
584946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = -ENOTTY;
585946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		break;
586946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
587fc0f8fc9be654bbff08ede04a49bd8f9805b9e13Oliver Neukumerror_out:
588946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* unlock the device */
589946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	mutex_unlock(&dev->mutex);
590925ce689bb31960c839804c19ef38d676f1939b9Arnd Bergmann	mutex_unlock(&iowarrior_mutex);
591fc0f8fc9be654bbff08ede04a49bd8f9805b9e13Oliver Neukum	kfree(buffer);
592946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return retval;
593946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
594946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
595946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/**
596946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *	iowarrior_open
597946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
598946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic int iowarrior_open(struct inode *inode, struct file *file)
599946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
600946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct iowarrior *dev = NULL;
601946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct usb_interface *interface;
602946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int subminor;
603946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int retval = 0;
604946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
605946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dbg("%s", __func__);
606946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
607925ce689bb31960c839804c19ef38d676f1939b9Arnd Bergmann	mutex_lock(&iowarrior_mutex);
608946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	subminor = iminor(inode);
609946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
610946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	interface = usb_find_interface(&iowarrior_driver, subminor);
611946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (!interface) {
612925ce689bb31960c839804c19ef38d676f1939b9Arnd Bergmann		mutex_unlock(&iowarrior_mutex);
613441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		err("%s - error, can't find device for minor %d", __func__,
614946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		    subminor);
615d4ead16f50f9ad30bdc7276ec8fee7a24c72f294Alan Stern		return -ENODEV;
616946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
617946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
61803f36e885fc26cb0ea299fb6df5171a51e814548Oliver Neukum	mutex_lock(&iowarrior_open_disc_lock);
619946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev = usb_get_intfdata(interface);
62003f36e885fc26cb0ea299fb6df5171a51e814548Oliver Neukum	if (!dev) {
62103f36e885fc26cb0ea299fb6df5171a51e814548Oliver Neukum		mutex_unlock(&iowarrior_open_disc_lock);
622925ce689bb31960c839804c19ef38d676f1939b9Arnd Bergmann		mutex_unlock(&iowarrior_mutex);
623d4ead16f50f9ad30bdc7276ec8fee7a24c72f294Alan Stern		return -ENODEV;
62403f36e885fc26cb0ea299fb6df5171a51e814548Oliver Neukum	}
625d4ead16f50f9ad30bdc7276ec8fee7a24c72f294Alan Stern
626d4ead16f50f9ad30bdc7276ec8fee7a24c72f294Alan Stern	mutex_lock(&dev->mutex);
62703f36e885fc26cb0ea299fb6df5171a51e814548Oliver Neukum	mutex_unlock(&iowarrior_open_disc_lock);
628946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
629946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* Only one process can open each device, no sharing. */
630946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (dev->opened) {
631946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = -EBUSY;
632946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto out;
633946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
634946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
635946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* setup interrupt handler for receiving values */
636946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if ((retval = usb_submit_urb(dev->int_in_urb, GFP_KERNEL)) < 0) {
637946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		dev_err(&interface->dev, "Error %d while submitting URB\n", retval);
638946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = -EFAULT;
639946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto out;
640946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
641946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* increment our usage count for the driver */
642946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	++dev->opened;
643946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* save our object in the file's private structure */
644946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	file->private_data = dev;
645946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	retval = 0;
646946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
647946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanout:
648d4ead16f50f9ad30bdc7276ec8fee7a24c72f294Alan Stern	mutex_unlock(&dev->mutex);
649925ce689bb31960c839804c19ef38d676f1939b9Arnd Bergmann	mutex_unlock(&iowarrior_mutex);
650946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return retval;
651946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
652946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
653946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/**
654946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *	iowarrior_release
655946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
656946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic int iowarrior_release(struct inode *inode, struct file *file)
657946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
658946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct iowarrior *dev;
659946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int retval = 0;
660946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
6615bd6e8b3fb787b7337b681aaa601e5c7bdc67c55Joe Perches	dev = file->private_data;
662946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (dev == NULL) {
663946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		return -ENODEV;
664946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
665946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
666946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dbg("%s - minor %d", __func__, dev->minor);
667946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
668946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* lock our device */
669946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	mutex_lock(&dev->mutex);
670946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
671946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (dev->opened <= 0) {
672946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = -ENODEV;	/* close called more than once */
673946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		mutex_unlock(&dev->mutex);
674946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	} else {
675946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		dev->opened = 0;	/* we're closeing now */
676946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		retval = 0;
677946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (dev->present) {
678946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/*
679946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			   The device is still connected so we only shutdown
680946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			   pending read-/write-ops.
681946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			 */
682946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			usb_kill_urb(dev->int_in_urb);
683946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			wake_up_interruptible(&dev->read_wait);
684946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			wake_up_interruptible(&dev->write_wait);
685946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			mutex_unlock(&dev->mutex);
686946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		} else {
687946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/* The device was unplugged, cleanup resources */
688946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			mutex_unlock(&dev->mutex);
689946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			iowarrior_delete(dev);
690946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		}
691946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
692946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return retval;
693946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
694946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
695946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic unsigned iowarrior_poll(struct file *file, poll_table * wait)
696946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
697946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct iowarrior *dev = file->private_data;
698946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	unsigned int mask = 0;
699946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
700946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (!dev->present)
701946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		return POLLERR | POLLHUP;
702946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
703946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	poll_wait(file, &dev->read_wait, wait);
704946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	poll_wait(file, &dev->write_wait, wait);
705946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
706946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (!dev->present)
707946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		return POLLERR | POLLHUP;
708946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
709946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (read_index(dev) != -1)
710946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		mask |= POLLIN | POLLRDNORM;
711946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
712946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (atomic_read(&dev->write_busy) < MAX_WRITES_IN_FLIGHT)
713946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		mask |= POLLOUT | POLLWRNORM;
714946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return mask;
715946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
716946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
717946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*
718946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * File operations needed when we register this driver.
719946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * This assumes that this driver NEEDS file operations,
720946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * of course, which means that the driver is expected
721946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * to have a node in the /dev directory. If the USB
722946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * device were for a network interface then the driver
723946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * would use "struct net_driver" instead, and a serial
724946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * device would use "struct tty_driver".
725946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
7260b3f5fe673d4626ea91816ca9c486b43b39a2fd1Jan Engelhardtstatic const struct file_operations iowarrior_fops = {
727946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.owner = THIS_MODULE,
728946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.write = iowarrior_write,
729946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.read = iowarrior_read,
730824f16fda56a88267aba9b2580d7566cf56a0860Alan Cox	.unlocked_ioctl = iowarrior_ioctl,
731946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.open = iowarrior_open,
732946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.release = iowarrior_release,
733946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.poll = iowarrior_poll,
7346038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann	.llseek = noop_llseek,
735946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman};
736946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
7372c9ede55ecec58099b72e4bb8eab719f32f72c31Al Virostatic char *iowarrior_devnode(struct device *dev, umode_t *mode)
738f7a386c5b8ff34cd84ae922603d1c6f9d234edeeKay Sievers{
739f7a386c5b8ff34cd84ae922603d1c6f9d234edeeKay Sievers	return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
740f7a386c5b8ff34cd84ae922603d1c6f9d234edeeKay Sievers}
741f7a386c5b8ff34cd84ae922603d1c6f9d234edeeKay Sievers
742946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*
743946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * usb class driver info in order to get a minor number from the usb core,
744946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman * and to have the device registered with devfs and the driver core
745946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
746946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic struct usb_class_driver iowarrior_class = {
747946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.name = "iowarrior%d",
748e454cea20bdcff10ee698d11b8882662a0153a47Kay Sievers	.devnode = iowarrior_devnode,
749946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.fops = &iowarrior_fops,
750946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.minor_base = IOWARRIOR_MINOR_BASE,
751946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman};
752946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
753946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*---------------------------------*/
754946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*  probe and disconnect functions */
755946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/*---------------------------------*/
756946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/**
757946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *	iowarrior_probe
758946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *
759946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *	Called by the usb core when a new device is connected that it thinks
760946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *	this driver might be interested in.
761946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
762946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic int iowarrior_probe(struct usb_interface *interface,
763946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			   const struct usb_device_id *id)
764946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
765946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct usb_device *udev = interface_to_usbdev(interface);
766946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct iowarrior *dev = NULL;
767946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct usb_host_interface *iface_desc;
768946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct usb_endpoint_descriptor *endpoint;
769946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int i;
770946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int retval = -ENOMEM;
771946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
772b595076a180a56d1bb170e6eceda6eb9d76f4cd3Uwe Kleine-König	/* allocate memory for our device state and initialize it */
773946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
774946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (dev == NULL) {
775898eb71cb17644964c5895fb190e79e3d0c49679Joe Perches		dev_err(&interface->dev, "Out of memory\n");
776946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		return retval;
777946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
778946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
779946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	mutex_init(&dev->mutex);
780946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
781946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_set(&dev->intr_idx, 0);
782946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_set(&dev->read_idx, 0);
783946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	spin_lock_init(&dev->intr_idx_lock);
784946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_set(&dev->overflow_flag, 0);
785946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	init_waitqueue_head(&dev->read_wait);
786946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	atomic_set(&dev->write_busy, 0);
787946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	init_waitqueue_head(&dev->write_wait);
788946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
789946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev->udev = udev;
790946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev->interface = interface;
791946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
792946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	iface_desc = interface->cur_altsetting;
793946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev->product_id = le16_to_cpu(udev->descriptor.idProduct);
794946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
795946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* set up the endpoint information */
796946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
797946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		endpoint = &iface_desc->endpoint[i].desc;
798946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
799946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (usb_endpoint_is_int_in(endpoint))
800946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			dev->int_in_endpoint = endpoint;
801946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		if (usb_endpoint_is_int_out(endpoint))
802946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			/* this one will match for the IOWarrior56 only */
803946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			dev->int_out_endpoint = endpoint;
804946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
805946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* we have to check the report_size often, so remember it in the endianess suitable for our machine */
80629cc88979a8818cd8c5019426e945aed118b400eKuninori Morimoto	dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint);
807946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
808946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	    (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56))
809946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* IOWarrior56 has wMaxPacketSize different from report size */
810946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		dev->report_size = 7;
811946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
812946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* create the urb and buffer for reading */
813946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
814946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (!dev->int_in_urb) {
815946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
816946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto error;
817946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
818946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev->int_in_buffer = kmalloc(dev->report_size, GFP_KERNEL);
819946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (!dev->int_in_buffer) {
820946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		dev_err(&interface->dev, "Couldn't allocate int_in_buffer\n");
821946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto error;
822946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
823946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	usb_fill_int_urb(dev->int_in_urb, dev->udev,
824946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			 usb_rcvintpipe(dev->udev,
825946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman					dev->int_in_endpoint->bEndpointAddress),
826946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			 dev->int_in_buffer, dev->report_size,
827946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			 iowarrior_callback, dev,
828946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman			 dev->int_in_endpoint->bInterval);
829946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* create an internal buffer for interrupt data from the device */
830946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev->read_queue =
831946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	    kmalloc(((dev->report_size + 1) * MAX_INTERRUPT_BUFFER),
832946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		    GFP_KERNEL);
833946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (!dev->read_queue) {
834946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		dev_err(&interface->dev, "Couldn't allocate read_queue\n");
835946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto error;
836946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
837946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* Get the serial-number of the chip */
838946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
839946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	usb_string(udev, udev->descriptor.iSerialNumber, dev->chip_serial,
840946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		   sizeof(dev->chip_serial));
841946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (strlen(dev->chip_serial) != 8)
842946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
843946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
844946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* Set the idle timeout to 0, if this is interface 0 */
845946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) {
846147c5a17338fc72a89452f0a6c14ae6fcf853919Eberhard Fahle	    usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
847147c5a17338fc72a89452f0a6c14ae6fcf853919Eberhard Fahle			    0x0A,
848147c5a17338fc72a89452f0a6c14ae6fcf853919Eberhard Fahle			    USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
849147c5a17338fc72a89452f0a6c14ae6fcf853919Eberhard Fahle			    0, NULL, 0, USB_CTRL_SET_TIMEOUT);
850946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
851946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* allow device read and ioctl */
852946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev->present = 1;
853946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
854946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* we can register the device now, as it is ready */
855946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	usb_set_intfdata(interface, dev);
856946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
857946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	retval = usb_register_dev(interface, &iowarrior_class);
858946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (retval) {
859946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* something prevented us from registering this driver */
860946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		dev_err(&interface->dev, "Not able to get a minor for this device.\n");
861946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		usb_set_intfdata(interface, NULL);
862946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		goto error;
863946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
864946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
865946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev->minor = interface->minor;
866946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
867946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* let the user know what node this device is now attached to */
868946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d "
869946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		 "now attached to iowarrior%d\n", dev->product_id, dev->chip_serial,
870946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		 iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE);
871946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return retval;
872946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
873946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanerror:
874946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	iowarrior_delete(dev);
875946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	return retval;
876946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
877946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
878946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/**
879946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *	iowarrior_disconnect
880946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *
881946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman *	Called by the usb core when the device is removed from the system.
882946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman */
883946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic void iowarrior_disconnect(struct usb_interface *interface)
884946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman{
885946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	struct iowarrior *dev;
886946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	int minor;
887946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
888946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev = usb_get_intfdata(interface);
88903f36e885fc26cb0ea299fb6df5171a51e814548Oliver Neukum	mutex_lock(&iowarrior_open_disc_lock);
890946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	usb_set_intfdata(interface, NULL);
891946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
892946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	minor = dev->minor;
893946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
894946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* give back our minor */
895946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	usb_deregister_dev(interface, &iowarrior_class);
896946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
897d4ead16f50f9ad30bdc7276ec8fee7a24c72f294Alan Stern	mutex_lock(&dev->mutex);
898d4ead16f50f9ad30bdc7276ec8fee7a24c72f294Alan Stern
899946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	/* prevent device read, write and ioctl */
900946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev->present = 0;
901946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
902946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	mutex_unlock(&dev->mutex);
90303f36e885fc26cb0ea299fb6df5171a51e814548Oliver Neukum	mutex_unlock(&iowarrior_open_disc_lock);
904946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
905946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	if (dev->opened) {
906946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* There is a process that holds a filedescriptor to the device ,
907946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		   so we only shutdown read-/write-ops going on.
908946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		   Deleting the device is postponed until close() was called.
909946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		 */
910946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		usb_kill_urb(dev->int_in_urb);
911946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		wake_up_interruptible(&dev->read_wait);
912946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		wake_up_interruptible(&dev->write_wait);
913946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	} else {
914946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		/* no process is using the device, cleanup now */
915946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		iowarrior_delete(dev);
916946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	}
917946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
918946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n",
919946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman		 minor - IOWARRIOR_MINOR_BASE);
920946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman}
921946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
922946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman/* usb specific object needed to register this driver with the usb subsystem */
923946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartmanstatic struct usb_driver iowarrior_driver = {
924946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.name = "iowarrior",
925946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.probe = iowarrior_probe,
926946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.disconnect = iowarrior_disconnect,
927946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman	.id_table = iowarrior_ids,
928946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman};
929946b960d13c15f050a3b848987aaca79f6a459b7Greg Kroah-Hartman
93065db43054065790a75291b0834657445fea2cf56Greg Kroah-Hartmanmodule_usb_driver(iowarrior_driver);
931