diff options
author | David Taht <d@teklibre.com> | 2008-12-17 20:13:45 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-01-06 16:52:36 -0500 |
commit | 8da3dc28753ece6b7ddae9d5897a0ad0797e21e6 (patch) | |
tree | df9f694ef05bfad2cc9d05f55fb46693e713b36e /drivers/staging | |
parent | 1242c70df56978e8abbf715a02fb1c55313f8471 (diff) |
Staging: add frontier tranzport and alphatrack drivers
Adds the tranzport and alphatrack drivers to the staging tree.
Cc: David Taht <d@teklibre.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging')
-rw-r--r-- | drivers/staging/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/frontier/Kconfig | 6 | ||||
-rw-r--r-- | drivers/staging/frontier/Makefile | 2 | ||||
-rw-r--r-- | drivers/staging/frontier/README | 28 | ||||
-rw-r--r-- | drivers/staging/frontier/TODO | 9 | ||||
-rw-r--r-- | drivers/staging/frontier/alphatrack.c | 901 | ||||
-rw-r--r-- | drivers/staging/frontier/alphatrack.h | 117 | ||||
-rw-r--r-- | drivers/staging/frontier/alphatrack_sysfs.c | 279 | ||||
-rw-r--r-- | drivers/staging/frontier/frontier_compat.h | 63 | ||||
-rw-r--r-- | drivers/staging/frontier/surface_sysfs.h | 100 | ||||
-rw-r--r-- | drivers/staging/frontier/tranzport.c | 1017 |
12 files changed, 2525 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index e2f57e9b694c..15f2b1148df3 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig | |||
@@ -89,5 +89,7 @@ source "drivers/staging/rspiusb/Kconfig" | |||
89 | 89 | ||
90 | source "drivers/staging/mimio/Kconfig" | 90 | source "drivers/staging/mimio/Kconfig" |
91 | 91 | ||
92 | source "drivers/staging/frontier/Kconfig" | ||
93 | |||
92 | endif # !STAGING_EXCLUDE_BUILD | 94 | endif # !STAGING_EXCLUDE_BUILD |
93 | endif # STAGING | 95 | endif # STAGING |
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 94efc9df5a85..d33803092b62 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile | |||
@@ -27,3 +27,4 @@ obj-$(CONFIG_ALTERA_PCIE_CHDMA) += altpciechdma/ | |||
27 | obj-$(CONFIG_RTL8187SE) += rtl8187se/ | 27 | obj-$(CONFIG_RTL8187SE) += rtl8187se/ |
28 | obj-$(CONFIG_USB_RSPI) += rspiusb/ | 28 | obj-$(CONFIG_USB_RSPI) += rspiusb/ |
29 | obj-$(CONFIG_INPUT_MIMIO) += mimio/ | 29 | obj-$(CONFIG_INPUT_MIMIO) += mimio/ |
30 | obj-$(CONFIG_TRANZPORT) += frontier/ | ||
diff --git a/drivers/staging/frontier/Kconfig b/drivers/staging/frontier/Kconfig new file mode 100644 index 000000000000..7121853bd397 --- /dev/null +++ b/drivers/staging/frontier/Kconfig | |||
@@ -0,0 +1,6 @@ | |||
1 | config TRANZPORT | ||
2 | tristate "Frontier Tranzport and Alphatrack support" | ||
3 | depends on USB | ||
4 | default N | ||
5 | ---help--- | ||
6 | Enable support for the Frontier Tranzport and Alphatrack devices. | ||
diff --git a/drivers/staging/frontier/Makefile b/drivers/staging/frontier/Makefile new file mode 100644 index 000000000000..2d2ac97492dd --- /dev/null +++ b/drivers/staging/frontier/Makefile | |||
@@ -0,0 +1,2 @@ | |||
1 | obj-$(CONFIG_TRANZPORT) += tranzport.o | ||
2 | obj-$(CONFIG_TRANZPORT) += alphatrack.o | ||
diff --git a/drivers/staging/frontier/README b/drivers/staging/frontier/README new file mode 100644 index 000000000000..07c9ef9b8fc4 --- /dev/null +++ b/drivers/staging/frontier/README | |||
@@ -0,0 +1,28 @@ | |||
1 | This directory contains the USB Tranzport and Alphatrack Kernel drivers for Linux. | ||
2 | |||
3 | At present the tranzport does reads/writes of 8 byte cmds to /dev/tranzport0 to control | ||
4 | the lights and screen and wheel | ||
5 | |||
6 | At present the alphatrack accepts reads/writes of 12 byte cmds to /dev/tranzport0 to control | ||
7 | the lights and screen and fader. | ||
8 | |||
9 | Both drivers also have some sysfs hooks that are non-functional at the moment. | ||
10 | |||
11 | The API is currently closely tied to the ardour revision and WILL change. | ||
12 | |||
13 | A sysfs interface is PERFECT for simple userspace apps to do fun things with the | ||
14 | lights and screen. It's fairly lousy for handling input events and very lousy | ||
15 | for watching the state of the shuttle wheel. | ||
16 | |||
17 | A linux input events interface is great for the input events and shuttle wheel. It's | ||
18 | theoretically OK on LEDs. A Fader can be mapped to an absolute mouse device. | ||
19 | But there is no LCD support at all. | ||
20 | |||
21 | In the end this is going to be driven by a midi layer, which handles all those | ||
22 | cases via a defined API, but - among other things - is slow, doesn't do | ||
23 | flow control, and is a LOT of extra work. Frankly, I'd like to keep the | ||
24 | core driver simple because the only realtime work really required is | ||
25 | the bottom half interrupt handler and the output overlapping. | ||
26 | |||
27 | Exposing some sort of clean aio api to userspace would be perfect. What that | ||
28 | API looks like? Gah. beats me. | ||
diff --git a/drivers/staging/frontier/TODO b/drivers/staging/frontier/TODO new file mode 100644 index 000000000000..3620ad2df3ee --- /dev/null +++ b/drivers/staging/frontier/TODO | |||
@@ -0,0 +1,9 @@ | |||
1 | TODO: | ||
2 | - checkpatch.pl clean | ||
3 | - sparse clean | ||
4 | - fix userspace interface to be sane | ||
5 | - possibly just port to userspace with libusb | ||
6 | - review by the USB developer community | ||
7 | |||
8 | Please send any patches for this driver to Greg Kroah-Hartman <greg@kroah.com> | ||
9 | and David Taht <d@teklibre.com>. | ||
diff --git a/drivers/staging/frontier/alphatrack.c b/drivers/staging/frontier/alphatrack.c new file mode 100644 index 000000000000..efc2345a676b --- /dev/null +++ b/drivers/staging/frontier/alphatrack.c | |||
@@ -0,0 +1,901 @@ | |||
1 | /* | ||
2 | * Frontier Designs Alphatrack driver | ||
3 | * | ||
4 | * Copyright (C) 2007 Michael Taht (m@taht.net) | ||
5 | * | ||
6 | * Based on the usbled driver and ldusb drivers by | ||
7 | * | ||
8 | * Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com) | ||
9 | * Copyright (C) 2005 Michael Hund <mhund@ld-didactic.de> | ||
10 | * | ||
11 | * The ldusb driver was, in turn, derived from Lego USB Tower driver | ||
12 | * Copyright (C) 2003 David Glance <advidgsf@sourceforge.net> | ||
13 | * 2001-2004 Juergen Stuber <starblue@users.sourceforge.net> | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License as | ||
17 | * published by the Free Software Foundation, version 2. | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * This driver uses a ring buffer for time critical reading of | ||
23 | * interrupt in reports and provides read and write methods for | ||
24 | * raw interrupt reports. | ||
25 | */ | ||
26 | |||
27 | /* Note: this currently uses a dumb ringbuffer for reads and writes. | ||
28 | * A more optimal driver would cache and kill off outstanding urbs that are | ||
29 | * now invalid, and ignore ones that already were in the queue but valid | ||
30 | * as we only have 30 commands for the alphatrack. In particular this is | ||
31 | * key for getting lights to flash in time as otherwise many commands | ||
32 | * can be buffered up before the light change makes it to the interface. | ||
33 | */ | ||
34 | |||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/slab.h> | ||
39 | #include <linux/module.h> | ||
40 | #include <linux/kobject.h> | ||
41 | #include <linux/mutex.h> | ||
42 | #include <linux/version.h> | ||
43 | |||
44 | #include <asm/uaccess.h> | ||
45 | #include <linux/input.h> | ||
46 | #include <linux/usb.h> | ||
47 | #include <linux/poll.h> | ||
48 | |||
49 | #include "surface_sysfs.h" | ||
50 | |||
51 | /* make this work on older kernel versions */ | ||
52 | |||
53 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | ||
54 | #include "frontier_compat.h" | ||
55 | #endif /* older kernel versions */ | ||
56 | |||
57 | #include "alphatrack.h" | ||
58 | |||
59 | #define VENDOR_ID 0x165b | ||
60 | #define PRODUCT_ID 0xfad1 | ||
61 | |||
62 | #ifdef CONFIG_USB_DYNAMIC_MINORS | ||
63 | #define USB_ALPHATRACK_MINOR_BASE 0 | ||
64 | #else | ||
65 | // FIXME 176 - is another driver's minor - apply for that | ||
66 | // #define USB_ALPHATRACK_MINOR_BASE 177 | ||
67 | #define USB_ALPHATRACK_MINOR_BASE 176 | ||
68 | #endif | ||
69 | |||
70 | /* table of devices that work with this driver */ | ||
71 | static struct usb_device_id usb_alphatrack_table [] = { | ||
72 | { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, | ||
73 | { } /* Terminating entry */ | ||
74 | }; | ||
75 | |||
76 | MODULE_DEVICE_TABLE(usb, usb_alphatrack_table); | ||
77 | MODULE_VERSION("0.40"); | ||
78 | MODULE_AUTHOR("Mike Taht <m@taht.net>"); | ||
79 | MODULE_DESCRIPTION("Alphatrack USB Driver"); | ||
80 | MODULE_LICENSE("GPL"); | ||
81 | MODULE_SUPPORTED_DEVICE("Frontier Designs Alphatrack Control Surface"); | ||
82 | |||
83 | /* These aren't done yet */ | ||
84 | |||
85 | #define ALPHATRACK_HAVE_SYSFS 0 | ||
86 | #define SUPPRESS_EXTRA_ONLINE_EVENTS 0 | ||
87 | #define BUFFERED_WRITES 0 | ||
88 | #define SUPPRESS_EXTRA_OFFLINE_EVENTS 0 | ||
89 | #define COMPRESS_FADER_EVENTS 0 | ||
90 | |||
91 | #define BUFFERED_READS 1 | ||
92 | #define RING_BUFFER_SIZE 512 | ||
93 | #define WRITE_BUFFER_SIZE 34 | ||
94 | #define ALPHATRACK_USB_TIMEOUT 10 | ||
95 | #define OUTPUT_CMD_SIZE 8 | ||
96 | #define INPUT_CMD_SIZE 12 | ||
97 | |||
98 | |||
99 | static int debug = 0; | ||
100 | |||
101 | /* Use our own dbg macro */ | ||
102 | #define dbg_info(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) | ||
103 | |||
104 | #if 0 | ||
105 | #define alphatrack_ocmd_info(dev, cmd, format, arg...) do { if (debug) ocmd_info(dev , cmd , format, ## arg); } while (0) | ||
106 | |||
107 | #define alphatrack_icmd_info(dev, cmd, format, arg...) do { if (debug) icmd_info(dev , cmd, format, ## arg); } while (0) | ||
108 | #else | ||
109 | #define alphatrack_ocmd_info(dev, cmd, format, arg...) | ||
110 | |||
111 | #define alphatrack_icmd_info(dev, cmd, format, arg...) | ||
112 | |||
113 | #endif | ||
114 | |||
115 | /* Module parameters */ | ||
116 | |||
117 | module_param(debug, int, S_IRUGO | S_IWUSR); | ||
118 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | ||
119 | |||
120 | /* All interrupt in transfers are collected in a ring buffer to | ||
121 | * avoid racing conditions and get better performance of the driver. | ||
122 | */ | ||
123 | |||
124 | static int ring_buffer_size = RING_BUFFER_SIZE; | ||
125 | |||
126 | module_param(ring_buffer_size, int, S_IRUGO); | ||
127 | MODULE_PARM_DESC(ring_buffer_size, "Read ring buffer size"); | ||
128 | |||
129 | /* The write_buffer can one day contain more than one interrupt out transfer. | ||
130 | */ | ||
131 | |||
132 | static int write_buffer_size = WRITE_BUFFER_SIZE; | ||
133 | module_param(write_buffer_size, int, S_IRUGO); | ||
134 | MODULE_PARM_DESC(write_buffer_size, "Write buffer size"); | ||
135 | |||
136 | /* | ||
137 | * Increase the interval for debugging purposes. | ||
138 | * or set to 1 to use the standard interval from the endpoint descriptors. | ||
139 | */ | ||
140 | |||
141 | static int min_interrupt_in_interval = ALPHATRACK_USB_TIMEOUT; | ||
142 | module_param(min_interrupt_in_interval, int, 0); | ||
143 | MODULE_PARM_DESC(min_interrupt_in_interval, "Minimum interrupt in interval in ms"); | ||
144 | |||
145 | static int min_interrupt_out_interval = ALPHATRACK_USB_TIMEOUT; | ||
146 | module_param(min_interrupt_out_interval, int, 0); | ||
147 | MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in ms"); | ||
148 | |||
149 | |||
150 | |||
151 | /* Structure to hold all of our device specific stuff */ | ||
152 | |||
153 | struct usb_alphatrack { | ||
154 | struct semaphore sem; /* locks this structure */ | ||
155 | struct usb_interface* intf; /* save off the usb interface pointer */ | ||
156 | int open_count; /* number of times this port has been opened */ | ||
157 | |||
158 | struct alphatrack_icmd (*ring_buffer)[RING_BUFFER_SIZE]; /* just make c happy */ | ||
159 | struct alphatrack_ocmd (*write_buffer)[WRITE_BUFFER_SIZE]; /* just make c happy */ | ||
160 | unsigned int ring_head; | ||
161 | unsigned int ring_tail; | ||
162 | |||
163 | wait_queue_head_t read_wait; | ||
164 | wait_queue_head_t write_wait; | ||
165 | |||
166 | unsigned char* interrupt_in_buffer; | ||
167 | unsigned char* oldi_buffer; | ||
168 | struct usb_endpoint_descriptor* interrupt_in_endpoint; | ||
169 | struct urb* interrupt_in_urb; | ||
170 | int interrupt_in_interval; | ||
171 | size_t interrupt_in_endpoint_size; | ||
172 | int interrupt_in_running; | ||
173 | int interrupt_in_done; | ||
174 | |||
175 | char* interrupt_out_buffer; | ||
176 | struct usb_endpoint_descriptor* interrupt_out_endpoint; | ||
177 | struct urb* interrupt_out_urb; | ||
178 | int interrupt_out_interval; | ||
179 | size_t interrupt_out_endpoint_size; | ||
180 | int interrupt_out_busy; | ||
181 | |||
182 | atomic_t writes_pending; | ||
183 | int event; /* alternate interface to events */ | ||
184 | int fader; /* 10 bits */ | ||
185 | int lights; /* 23 bits */ | ||
186 | unsigned char dump_state; /* 0 if disabled 1 if enabled */ | ||
187 | unsigned char enable; /* 0 if disabled 1 if enabled */ | ||
188 | unsigned char offline; /* if the device is out of range or asleep */ | ||
189 | unsigned char verbose; /* be verbose in error reporting */ | ||
190 | unsigned char last_cmd[OUTPUT_CMD_SIZE]; | ||
191 | unsigned char screen[32]; | ||
192 | }; | ||
193 | |||
194 | /* prevent races between open() and disconnect() */ | ||
195 | static DEFINE_MUTEX(disconnect_mutex); | ||
196 | |||
197 | /* forward declaration */ | ||
198 | |||
199 | static struct usb_driver usb_alphatrack_driver; | ||
200 | |||
201 | static void icmd_info(struct usb_alphatrack *dev, char *cmd, char *str, char *a) { | ||
202 | /* | ||
203 | if (dev->verbose) { | ||
204 | } else { | ||
205 | } | ||
206 | */ | ||
207 | } | ||
208 | |||
209 | static void ocmd_info(struct usb_alphatrack *dev, char *cmd, char *str, char* a) { | ||
210 | /* | ||
211 | if (dev->verbose) { | ||
212 | } else { | ||
213 | } | ||
214 | */ | ||
215 | } | ||
216 | |||
217 | |||
218 | /** | ||
219 | * usb_alphatrack_abort_transfers | ||
220 | * aborts transfers and frees associated data structures | ||
221 | */ | ||
222 | static void usb_alphatrack_abort_transfers(struct usb_alphatrack *dev) | ||
223 | { | ||
224 | /* shutdown transfer */ | ||
225 | if (dev->interrupt_in_running) { | ||
226 | dev->interrupt_in_running = 0; | ||
227 | if (dev->intf) | ||
228 | usb_kill_urb(dev->interrupt_in_urb); | ||
229 | } | ||
230 | if (dev->interrupt_out_busy) | ||
231 | if (dev->intf) | ||
232 | usb_kill_urb(dev->interrupt_out_urb); | ||
233 | } | ||
234 | |||
235 | #if ALPHATRACK_HAVE_SYSFS | ||
236 | /* lots and lots and lots of sysfs stuff */ | ||
237 | /* Currently borked, probably useless */ | ||
238 | #include "alphatrack_sysfs.c" | ||
239 | #endif | ||
240 | |||
241 | /** | ||
242 | * usb_alphatrack_delete | ||
243 | */ | ||
244 | static void usb_alphatrack_delete(struct usb_alphatrack *dev) | ||
245 | { | ||
246 | usb_alphatrack_abort_transfers(dev); | ||
247 | usb_free_urb(dev->interrupt_in_urb); | ||
248 | usb_free_urb(dev->interrupt_out_urb); | ||
249 | kfree(dev->ring_buffer); | ||
250 | kfree(dev->interrupt_in_buffer); | ||
251 | kfree(dev->interrupt_out_buffer); | ||
252 | kfree(dev); // fixme oldi_buffer | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * usb_alphatrack_interrupt_in_callback | ||
257 | */ | ||
258 | |||
259 | static void usb_alphatrack_interrupt_in_callback(struct urb *urb) | ||
260 | { | ||
261 | struct usb_alphatrack *dev = urb->context; | ||
262 | unsigned int next_ring_head; | ||
263 | int retval = -1; | ||
264 | int *iptr; | ||
265 | |||
266 | if (urb->status) { | ||
267 | if (urb->status == -ENOENT || | ||
268 | urb->status == -ECONNRESET || | ||
269 | urb->status == -ESHUTDOWN) { | ||
270 | goto exit; | ||
271 | } else { | ||
272 | dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n", | ||
273 | __FUNCTION__, urb->status); | ||
274 | goto resubmit; /* maybe we can recover */ | ||
275 | } | ||
276 | } | ||
277 | |||
278 | if (urb->actual_length != INPUT_CMD_SIZE) { | ||
279 | dev_warn(&dev->intf->dev, | ||
280 | "Urb length was %d bytes!! Do something intelligent \n", urb->actual_length); | ||
281 | } else { | ||
282 | alphatrack_ocmd_info(&dev->intf->dev,&(*dev->ring_buffer)[dev->ring_tail].cmd,"%s", "bla"); | ||
283 | if(memcmp(dev->interrupt_in_buffer,dev->oldi_buffer,INPUT_CMD_SIZE)==0) { | ||
284 | goto resubmit; | ||
285 | } | ||
286 | memcpy(dev->oldi_buffer,dev->interrupt_in_buffer,INPUT_CMD_SIZE); | ||
287 | |||
288 | #if SUPPRESS_EXTRA_OFFLINE_EVENTS | ||
289 | if(dev->offline == 2 && dev->interrupt_in_buffer[1] == 0xff) { goto resubmit; } | ||
290 | if(dev->offline == 1 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 2; goto resubmit; } | ||
291 | /* Always pass one offline event up the stack */ | ||
292 | if(dev->offline > 0 && dev->interrupt_in_buffer[1] != 0xff) { dev->offline = 0; } | ||
293 | if(dev->offline == 0 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 1; } | ||
294 | #endif | ||
295 | dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); | ||
296 | next_ring_head = (dev->ring_head+1) % ring_buffer_size; | ||
297 | |||
298 | if (next_ring_head != dev->ring_tail) { | ||
299 | memcpy(&((*dev->ring_buffer)[dev->ring_head]), | ||
300 | dev->interrupt_in_buffer, urb->actual_length); | ||
301 | dev->ring_head = next_ring_head; | ||
302 | retval = 0; | ||
303 | memset(dev->interrupt_in_buffer, 0, urb->actual_length); | ||
304 | } else { | ||
305 | dev_warn(&dev->intf->dev, | ||
306 | "Ring buffer overflow, %d bytes dropped\n", | ||
307 | urb->actual_length); | ||
308 | memset(dev->interrupt_in_buffer, 0, urb->actual_length); | ||
309 | } | ||
310 | } | ||
311 | |||
312 | resubmit: | ||
313 | /* resubmit if we're still running */ | ||
314 | if (dev->interrupt_in_running && dev->intf) { | ||
315 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); | ||
316 | if (retval) | ||
317 | dev_err(&dev->intf->dev, | ||
318 | "usb_submit_urb failed (%d)\n", retval); | ||
319 | } | ||
320 | |||
321 | exit: | ||
322 | dev->interrupt_in_done = 1; | ||
323 | wake_up_interruptible(&dev->read_wait); | ||
324 | } | ||
325 | |||
326 | /** | ||
327 | * usb_alphatrack_interrupt_out_callback | ||
328 | */ | ||
329 | static void usb_alphatrack_interrupt_out_callback(struct urb *urb) | ||
330 | { | ||
331 | struct usb_alphatrack *dev = urb->context; | ||
332 | |||
333 | /* sync/async unlink faults aren't errors */ | ||
334 | if (urb->status && !(urb->status == -ENOENT || | ||
335 | urb->status == -ECONNRESET || | ||
336 | urb->status == -ESHUTDOWN)) | ||
337 | dbg_info(&dev->intf->dev, | ||
338 | "%s - nonzero write interrupt status received: %d\n", | ||
339 | __FUNCTION__, urb->status); | ||
340 | atomic_dec(&dev->writes_pending); | ||
341 | dev->interrupt_out_busy = 0; | ||
342 | wake_up_interruptible(&dev->write_wait); | ||
343 | } | ||
344 | |||
345 | /** | ||
346 | * usb_alphatrack_open | ||
347 | */ | ||
348 | static int usb_alphatrack_open(struct inode *inode, struct file *file) | ||
349 | { | ||
350 | struct usb_alphatrack *dev; | ||
351 | int subminor; | ||
352 | int retval = 0; | ||
353 | struct usb_interface *interface; | ||
354 | |||
355 | nonseekable_open(inode, file); | ||
356 | subminor = iminor(inode); | ||
357 | |||
358 | mutex_lock(&disconnect_mutex); | ||
359 | |||
360 | interface = usb_find_interface(&usb_alphatrack_driver, subminor); | ||
361 | |||
362 | if (!interface) { | ||
363 | err("%s - error, can't find device for minor %d\n", | ||
364 | __FUNCTION__, subminor); | ||
365 | retval = -ENODEV; | ||
366 | goto unlock_disconnect_exit; | ||
367 | } | ||
368 | |||
369 | dev = usb_get_intfdata(interface); | ||
370 | |||
371 | if (!dev) { | ||
372 | retval = -ENODEV; | ||
373 | goto unlock_disconnect_exit; | ||
374 | } | ||
375 | |||
376 | /* lock this device */ | ||
377 | if (down_interruptible(&dev->sem)) { | ||
378 | retval = -ERESTARTSYS; | ||
379 | goto unlock_disconnect_exit; | ||
380 | } | ||
381 | |||
382 | /* allow opening only once */ | ||
383 | if (dev->open_count) { | ||
384 | retval = -EBUSY; | ||
385 | goto unlock_exit; | ||
386 | } | ||
387 | dev->open_count = 1; | ||
388 | |||
389 | /* initialize in direction */ | ||
390 | dev->ring_head = 0; | ||
391 | dev->ring_tail = 0; | ||
392 | usb_fill_int_urb(dev->interrupt_in_urb, | ||
393 | interface_to_usbdev(interface), | ||
394 | usb_rcvintpipe(interface_to_usbdev(interface), | ||
395 | dev->interrupt_in_endpoint->bEndpointAddress), | ||
396 | dev->interrupt_in_buffer, | ||
397 | dev->interrupt_in_endpoint_size, | ||
398 | usb_alphatrack_interrupt_in_callback, | ||
399 | dev, | ||
400 | dev->interrupt_in_interval); | ||
401 | |||
402 | dev->interrupt_in_running = 1; | ||
403 | dev->interrupt_in_done = 0; | ||
404 | dev->enable = 1; | ||
405 | dev->offline = 0; | ||
406 | |||
407 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); | ||
408 | if (retval) { | ||
409 | dev_err(&interface->dev, "Couldn't submit interrupt_in_urb %d\n", retval); | ||
410 | dev->interrupt_in_running = 0; | ||
411 | dev->open_count = 0; | ||
412 | goto unlock_exit; | ||
413 | } | ||
414 | |||
415 | /* save device in the file's private structure */ | ||
416 | file->private_data = dev; | ||
417 | |||
418 | |||
419 | unlock_exit: | ||
420 | up(&dev->sem); | ||
421 | |||
422 | unlock_disconnect_exit: | ||
423 | mutex_unlock(&disconnect_mutex); | ||
424 | |||
425 | return retval; | ||
426 | } | ||
427 | |||
428 | /** | ||
429 | * usb_alphatrack_release | ||
430 | */ | ||
431 | static int usb_alphatrack_release(struct inode *inode, struct file *file) | ||
432 | { | ||
433 | struct usb_alphatrack *dev; | ||
434 | int retval = 0; | ||
435 | |||
436 | dev = file->private_data; | ||
437 | |||
438 | if (dev == NULL) { | ||
439 | retval = -ENODEV; | ||
440 | goto exit; | ||
441 | } | ||
442 | |||
443 | if (down_interruptible(&dev->sem)) { | ||
444 | retval = -ERESTARTSYS; | ||
445 | goto exit; | ||
446 | } | ||
447 | |||
448 | if (dev->open_count != 1) { | ||
449 | retval = -ENODEV; | ||
450 | goto unlock_exit; | ||
451 | } | ||
452 | |||
453 | if (dev->intf == NULL) { | ||
454 | /* the device was unplugged before the file was released */ | ||
455 | up(&dev->sem); | ||
456 | /* unlock here as usb_alphatrack_delete frees dev */ | ||
457 | usb_alphatrack_delete(dev); | ||
458 | retval = -ENODEV; | ||
459 | goto exit; | ||
460 | } | ||
461 | |||
462 | /* wait until write transfer is finished */ | ||
463 | if (dev->interrupt_out_busy) | ||
464 | wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy, 2 * HZ); | ||
465 | usb_alphatrack_abort_transfers(dev); | ||
466 | dev->open_count = 0; | ||
467 | |||
468 | unlock_exit: | ||
469 | up(&dev->sem); | ||
470 | |||
471 | exit: | ||
472 | return retval; | ||
473 | } | ||
474 | |||
475 | /** | ||
476 | * usb_alphatrack_poll | ||
477 | */ | ||
478 | static unsigned int usb_alphatrack_poll(struct file *file, poll_table *wait) | ||
479 | { | ||
480 | struct usb_alphatrack *dev; | ||
481 | unsigned int mask = 0; | ||
482 | |||
483 | dev = file->private_data; | ||
484 | |||
485 | poll_wait(file, &dev->read_wait, wait); | ||
486 | poll_wait(file, &dev->write_wait, wait); | ||
487 | |||
488 | if (dev->ring_head != dev->ring_tail) | ||
489 | mask |= POLLIN | POLLRDNORM; | ||
490 | if (!dev->interrupt_out_busy) | ||
491 | mask |= POLLOUT | POLLWRNORM; | ||
492 | |||
493 | return mask; | ||
494 | } | ||
495 | |||
496 | /** | ||
497 | * usb_alphatrack_read | ||
498 | */ | ||
499 | static ssize_t usb_alphatrack_read(struct file *file, char __user *buffer, size_t count, | ||
500 | loff_t *ppos) | ||
501 | { | ||
502 | struct usb_alphatrack *dev; | ||
503 | int retval = 0; | ||
504 | |||
505 | int c = 0; | ||
506 | |||
507 | dev = file->private_data; | ||
508 | |||
509 | /* verify that we actually have some data to read */ | ||
510 | if (count == 0) | ||
511 | goto exit; | ||
512 | |||
513 | /* lock this object */ | ||
514 | if (down_interruptible(&dev->sem)) { | ||
515 | retval = -ERESTARTSYS; | ||
516 | goto exit; | ||
517 | } | ||
518 | |||
519 | /* verify that the device wasn't unplugged */ | ||
520 | if (dev->intf == NULL) { | ||
521 | retval = -ENODEV; | ||
522 | err("No device or device unplugged %d\n", retval); | ||
523 | goto unlock_exit; | ||
524 | } | ||
525 | |||
526 | while (dev->ring_head == dev->ring_tail) { | ||
527 | if (file->f_flags & O_NONBLOCK) { | ||
528 | retval = -EAGAIN; | ||
529 | goto unlock_exit; | ||
530 | } | ||
531 | dev->interrupt_in_done = 0 ; | ||
532 | retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done); | ||
533 | if (retval < 0) { | ||
534 | goto unlock_exit; | ||
535 | } | ||
536 | } | ||
537 | |||
538 | alphatrack_ocmd_info(&dev->intf->dev, &(*dev->ring_buffer)[dev->ring_tail].cmd, "%s", ": copying to userspace"); | ||
539 | |||
540 | c = 0; | ||
541 | while((c < count) && (dev->ring_tail != dev->ring_head)) { | ||
542 | if (copy_to_user(&buffer[c], &(*dev->ring_buffer)[dev->ring_tail], INPUT_CMD_SIZE)) { | ||
543 | retval = -EFAULT; | ||
544 | goto unlock_exit; | ||
545 | } | ||
546 | dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; | ||
547 | c+=INPUT_CMD_SIZE; | ||
548 | dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); | ||
549 | } | ||
550 | retval = c; | ||
551 | |||
552 | unlock_exit: | ||
553 | /* unlock the device */ | ||
554 | up(&dev->sem); | ||
555 | |||
556 | exit: | ||
557 | return retval; | ||
558 | } | ||
559 | |||
560 | /** | ||
561 | * usb_alphatrack_write | ||
562 | */ | ||
563 | static ssize_t usb_alphatrack_write(struct file *file, const char __user *buffer, | ||
564 | size_t count, loff_t *ppos) | ||
565 | { | ||
566 | struct usb_alphatrack *dev; | ||
567 | size_t bytes_to_write; | ||
568 | int retval = 0; | ||
569 | |||
570 | dev = file->private_data; | ||
571 | |||
572 | /* verify that we actually have some data to write */ | ||
573 | if (count == 0) | ||
574 | goto exit; | ||
575 | |||
576 | /* lock this object */ | ||
577 | if (down_interruptible(&dev->sem)) { | ||
578 | retval = -ERESTARTSYS; | ||
579 | goto exit; | ||
580 | } | ||
581 | |||
582 | /* verify that the device wasn't unplugged */ | ||
583 | if (dev->intf == NULL) { | ||
584 | retval = -ENODEV; | ||
585 | err("No device or device unplugged %d\n", retval); | ||
586 | goto unlock_exit; | ||
587 | } | ||
588 | |||
589 | /* wait until previous transfer is finished */ | ||
590 | if (dev->interrupt_out_busy) { | ||
591 | if (file->f_flags & O_NONBLOCK) { | ||
592 | retval = -EAGAIN; | ||
593 | goto unlock_exit; | ||
594 | } | ||
595 | retval = wait_event_interruptible(dev->write_wait, !dev->interrupt_out_busy); | ||
596 | if (retval < 0) { | ||
597 | goto unlock_exit; | ||
598 | } | ||
599 | } | ||
600 | |||
601 | /* write the data into interrupt_out_buffer from userspace */ | ||
602 | /* FIXME - if you write more than 12 bytes this breaks */ | ||
603 | bytes_to_write = min(count, write_buffer_size*dev->interrupt_out_endpoint_size); | ||
604 | if (bytes_to_write < count) | ||
605 | dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write); | ||
606 | |||
607 | dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __FUNCTION__, count, bytes_to_write); | ||
608 | |||
609 | if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) { | ||
610 | retval = -EFAULT; | ||
611 | goto unlock_exit; | ||
612 | } | ||
613 | |||
614 | if (dev->interrupt_out_endpoint == NULL) { | ||
615 | err("Endpoint should not be be null! \n"); | ||
616 | goto unlock_exit; | ||
617 | } | ||
618 | |||
619 | /* send off the urb */ | ||
620 | usb_fill_int_urb(dev->interrupt_out_urb, | ||
621 | interface_to_usbdev(dev->intf), | ||
622 | usb_sndintpipe(interface_to_usbdev(dev->intf), | ||
623 | dev->interrupt_out_endpoint->bEndpointAddress), | ||
624 | dev->interrupt_out_buffer, | ||
625 | bytes_to_write, | ||
626 | usb_alphatrack_interrupt_out_callback, | ||
627 | dev, | ||
628 | dev->interrupt_out_interval); | ||
629 | dev->interrupt_out_busy = 1; | ||
630 | atomic_inc(&dev->writes_pending); | ||
631 | wmb(); | ||
632 | |||
633 | retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL); | ||
634 | if (retval) { | ||
635 | dev->interrupt_out_busy = 0; | ||
636 | err("Couldn't submit interrupt_out_urb %d\n", retval); | ||
637 | atomic_dec(&dev->writes_pending); | ||
638 | goto unlock_exit; | ||
639 | } | ||
640 | retval = bytes_to_write; | ||
641 | |||
642 | unlock_exit: | ||
643 | /* unlock the device */ | ||
644 | up(&dev->sem); | ||
645 | |||
646 | exit: | ||
647 | return retval; | ||
648 | } | ||
649 | |||
650 | /* file operations needed when we register this driver */ | ||
651 | static const struct file_operations usb_alphatrack_fops = { | ||
652 | .owner = THIS_MODULE, | ||
653 | .read = usb_alphatrack_read, | ||
654 | .write = usb_alphatrack_write, | ||
655 | .open = usb_alphatrack_open, | ||
656 | .release = usb_alphatrack_release, | ||
657 | .poll = usb_alphatrack_poll, | ||
658 | }; | ||
659 | |||
660 | /* | ||
661 | * usb class driver info in order to get a minor number from the usb core, | ||
662 | * and to have the device registered with the driver core | ||
663 | */ | ||
664 | |||
665 | static struct usb_class_driver usb_alphatrack_class = { | ||
666 | .name = "alphatrack%d", | ||
667 | .fops = &usb_alphatrack_fops, | ||
668 | .minor_base = USB_ALPHATRACK_MINOR_BASE, | ||
669 | }; | ||
670 | |||
671 | |||
672 | /** | ||
673 | * usb_alphatrack_probe | ||
674 | * | ||
675 | * Called by the usb core when a new device is connected that it thinks | ||
676 | * this driver might be interested in. | ||
677 | */ | ||
678 | static int usb_alphatrack_probe(struct usb_interface *intf, const struct usb_device_id *id) | ||
679 | { | ||
680 | struct usb_device *udev = interface_to_usbdev(intf); | ||
681 | struct usb_alphatrack *dev = NULL; | ||
682 | struct usb_host_interface *iface_desc; | ||
683 | struct usb_endpoint_descriptor *endpoint; | ||
684 | int i; | ||
685 | int true_size; | ||
686 | int retval = -ENOMEM; | ||
687 | |||
688 | /* allocate memory for our device state and intialize it */ | ||
689 | |||
690 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
691 | if (dev == NULL) { | ||
692 | dev_err(&intf->dev, "Out of memory\n"); | ||
693 | goto exit; | ||
694 | } | ||
695 | init_MUTEX(&dev->sem); | ||
696 | dev->intf = intf; | ||
697 | init_waitqueue_head(&dev->read_wait); | ||
698 | init_waitqueue_head(&dev->write_wait); | ||
699 | |||
700 | iface_desc = intf->cur_altsetting; | ||
701 | |||
702 | /* set up the endpoint information */ | ||
703 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | ||
704 | endpoint = &iface_desc->endpoint[i].desc; | ||
705 | |||
706 | if (usb_endpoint_is_int_in(endpoint)) | ||
707 | dev->interrupt_in_endpoint = endpoint; | ||
708 | |||
709 | if (usb_endpoint_is_int_out(endpoint)) | ||
710 | dev->interrupt_out_endpoint = endpoint; | ||
711 | } | ||
712 | if (dev->interrupt_in_endpoint == NULL) { | ||
713 | dev_err(&intf->dev, "Interrupt in endpoint not found\n"); | ||
714 | goto error; | ||
715 | } | ||
716 | if (dev->interrupt_out_endpoint == NULL) | ||
717 | dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n"); | ||
718 | |||
719 | dev->interrupt_in_endpoint_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize); | ||
720 | |||
721 | if (dev->interrupt_in_endpoint_size != 64) | ||
722 | dev_warn(&intf->dev, "Interrupt in endpoint size is not 64!\n"); | ||
723 | |||
724 | if(ring_buffer_size == 0) { ring_buffer_size = RING_BUFFER_SIZE; } | ||
725 | |||
726 | true_size = min(ring_buffer_size,RING_BUFFER_SIZE); | ||
727 | |||
728 | /* FIXME - there are more usb_alloc routines for dma correctness. Needed? */ | ||
729 | |||
730 | // dev->ring_buffer = kmalloc((true_size*sizeof(struct alphatrack_icmd))+12, GFP_KERNEL); | ||
731 | dev->ring_buffer = kmalloc((true_size*sizeof(struct alphatrack_icmd)), GFP_KERNEL); | ||
732 | |||
733 | if (!dev->ring_buffer) { | ||
734 | dev_err(&intf->dev, "Couldn't allocate input ring_buffer of size %d\n",true_size); | ||
735 | goto error; | ||
736 | } | ||
737 | |||
738 | dev->interrupt_in_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL); | ||
739 | |||
740 | if (!dev->interrupt_in_buffer) { | ||
741 | dev_err(&intf->dev, "Couldn't allocate interrupt_in_buffer\n"); | ||
742 | goto error; | ||
743 | } | ||
744 | dev->oldi_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL); | ||
745 | if (!dev->oldi_buffer) { | ||
746 | dev_err(&intf->dev, "Couldn't allocate old buffer\n"); | ||
747 | goto error; | ||
748 | } | ||
749 | dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
750 | if (!dev->interrupt_in_urb) { | ||
751 | dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n"); | ||
752 | goto error; | ||
753 | } | ||
754 | |||
755 | dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize) : | ||
756 | udev->descriptor.bMaxPacketSize0; | ||
757 | |||
758 | if (dev->interrupt_out_endpoint_size !=64) | ||
759 | dev_warn(&intf->dev, "Interrupt out endpoint size is not 64!)\n"); | ||
760 | |||
761 | if(write_buffer_size == 0) { write_buffer_size = WRITE_BUFFER_SIZE; } | ||
762 | true_size = min(write_buffer_size,WRITE_BUFFER_SIZE); | ||
763 | |||
764 | dev->interrupt_out_buffer = kmalloc(true_size*dev->interrupt_out_endpoint_size, GFP_KERNEL); | ||
765 | |||
766 | if (!dev->interrupt_out_buffer) { | ||
767 | dev_err(&intf->dev, "Couldn't allocate interrupt_out_buffer\n"); | ||
768 | goto error; | ||
769 | } | ||
770 | |||
771 | dev->write_buffer = kmalloc(sizeof(struct alphatrack_ocmd)*true_size, GFP_KERNEL); | ||
772 | |||
773 | if (!dev->write_buffer) { | ||
774 | dev_err(&intf->dev, "Couldn't allocate write_buffer \n"); | ||
775 | goto error; | ||
776 | } | ||
777 | |||
778 | dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
779 | if (!dev->interrupt_out_urb) { | ||
780 | dev_err(&intf->dev, "Couldn't allocate interrupt_out_urb\n"); | ||
781 | goto error; | ||
782 | } | ||
783 | dev->interrupt_in_interval = min_interrupt_in_interval > dev->interrupt_in_endpoint->bInterval ? min_interrupt_in_interval : dev->interrupt_in_endpoint->bInterval; | ||
784 | if (dev->interrupt_out_endpoint) | ||
785 | dev->interrupt_out_interval = min_interrupt_out_interval > dev->interrupt_out_endpoint->bInterval ? min_interrupt_out_interval : dev->interrupt_out_endpoint->bInterval; | ||
786 | |||
787 | /* we can register the device now, as it is ready */ | ||
788 | usb_set_intfdata(intf, dev); | ||
789 | |||
790 | atomic_set(&dev->writes_pending,0); | ||
791 | retval = usb_register_dev(intf, &usb_alphatrack_class); | ||
792 | if (retval) { | ||
793 | /* something prevented us from registering this driver */ | ||
794 | dev_err(&intf->dev, "Not able to get a minor for this device.\n"); | ||
795 | usb_set_intfdata(intf, NULL); | ||
796 | goto error; | ||
797 | } | ||
798 | |||
799 | /* let the user know what node this device is now attached to */ | ||
800 | dev_info(&intf->dev, "Alphatrack Device #%d now attached to major %d minor %d\n", | ||
801 | (intf->minor - USB_ALPHATRACK_MINOR_BASE), USB_MAJOR, intf->minor); | ||
802 | |||
803 | #if ALPHATRACK_HAVE_SYSFS | ||
804 | if((retval = device_create_file(&intf->dev, &dev_attr_event))) goto error; | ||
805 | if((retval = device_create_file(&intf->dev, &dev_attr_dump_state))) goto error; | ||
806 | if((retval = device_create_file(&intf->dev, &dev_attr_enable))) goto error; | ||
807 | if((retval = device_create_file(&intf->dev, &dev_attr_offline))) goto error; | ||
808 | |||
809 | /* exercise sysfs */ | ||
810 | |||
811 | set_lights("32767"); // turn on all the lights | ||
812 | set_fader0("1023"); // Move fader to max | ||
813 | set_screen("INITIALIZING ALPHATRACK..."); | ||
814 | set_lights("0"); | ||
815 | set_fader0("0"); | ||
816 | set_screen(" "); | ||
817 | |||
818 | #endif | ||
819 | |||
820 | exit: | ||
821 | return retval; | ||
822 | |||
823 | error: | ||
824 | usb_alphatrack_delete(dev); | ||
825 | |||
826 | return retval; | ||
827 | } | ||
828 | |||
829 | /** | ||
830 | * usb_alphatrack_disconnect | ||
831 | * | ||
832 | * Called by the usb core when the device is removed from the system. | ||
833 | */ | ||
834 | static void usb_alphatrack_disconnect(struct usb_interface *intf) | ||
835 | { | ||
836 | struct usb_alphatrack *dev; | ||
837 | int minor; | ||
838 | |||
839 | mutex_lock(&disconnect_mutex); | ||
840 | |||
841 | dev = usb_get_intfdata(intf); | ||
842 | usb_set_intfdata(intf, NULL); | ||
843 | |||
844 | down(&dev->sem); | ||
845 | |||
846 | minor = intf->minor; | ||
847 | |||
848 | /* give back our minor */ | ||
849 | usb_deregister_dev(intf, &usb_alphatrack_class); | ||
850 | |||
851 | /* if the device is not opened, then we clean up right now */ | ||
852 | if (!dev->open_count) { | ||
853 | up(&dev->sem); | ||
854 | usb_alphatrack_delete(dev); | ||
855 | } else { | ||
856 | dev->intf = NULL; | ||
857 | up(&dev->sem); | ||
858 | } | ||
859 | |||
860 | atomic_set(&dev->writes_pending,0); | ||
861 | mutex_unlock(&disconnect_mutex); | ||
862 | |||
863 | dev_info(&intf->dev, "Alphatrack Surface #%d now disconnected\n", | ||
864 | (minor - USB_ALPHATRACK_MINOR_BASE)); | ||
865 | } | ||
866 | |||
867 | /* usb specific object needed to register this driver with the usb subsystem */ | ||
868 | static struct usb_driver usb_alphatrack_driver = { | ||
869 | .name = "alphatrack", | ||
870 | .probe = usb_alphatrack_probe, | ||
871 | .disconnect = usb_alphatrack_disconnect, | ||
872 | .id_table = usb_alphatrack_table, | ||
873 | }; | ||
874 | |||
875 | /** | ||
876 | * usb_alphatrack_init | ||
877 | */ | ||
878 | static int __init usb_alphatrack_init(void) | ||
879 | { | ||
880 | int retval; | ||
881 | |||
882 | /* register this driver with the USB subsystem */ | ||
883 | retval = usb_register(&usb_alphatrack_driver); | ||
884 | if (retval) | ||
885 | err("usb_register failed for the "__FILE__" driver. Error number %d\n", retval); | ||
886 | |||
887 | return retval; | ||
888 | } | ||
889 | |||
890 | /** | ||
891 | * usb_alphatrack_exit | ||
892 | */ | ||
893 | static void __exit usb_alphatrack_exit(void) | ||
894 | { | ||
895 | /* deregister this driver with the USB subsystem */ | ||
896 | usb_deregister(&usb_alphatrack_driver); | ||
897 | } | ||
898 | |||
899 | module_init(usb_alphatrack_init); | ||
900 | module_exit(usb_alphatrack_exit); | ||
901 | |||
diff --git a/drivers/staging/frontier/alphatrack.h b/drivers/staging/frontier/alphatrack.h new file mode 100644 index 000000000000..3dd7d54f49a8 --- /dev/null +++ b/drivers/staging/frontier/alphatrack.h | |||
@@ -0,0 +1,117 @@ | |||
1 | #define show_set_bit(a) show_set_mbit(alphatrack,a) | ||
2 | #define show_set_cmd(a) show_set_mcmd(alphatrack,a) | ||
3 | #define show_set_int(a) show_set_mint(alphatrack,a) | ||
4 | #define show_set_char(a) show_set_mchar(alphatrack,a) | ||
5 | #define show_set_light(a) show_set_ebit(alphatrack,LightID,lights,a) | ||
6 | #define show_set_button(a) show_set_ebit(alphatrack,ButtonID,button,a) | ||
7 | |||
8 | struct alphatrack_icmd { | ||
9 | unsigned char cmd[12]; | ||
10 | }; | ||
11 | |||
12 | struct alphatrack_ocmd { | ||
13 | unsigned char cmd[8]; | ||
14 | }; | ||
15 | |||
16 | enum LightID { | ||
17 | LIGHT_EQ = 0, | ||
18 | LIGHT_OUT, | ||
19 | LIGHT_F2, | ||
20 | LIGHT_SEND, | ||
21 | LIGHT_IN, | ||
22 | LIGHT_F1, | ||
23 | LIGHT_PAN, | ||
24 | LIGHT_UNDEF1, | ||
25 | LIGHT_UNDEF2, | ||
26 | LIGHT_SHIFT, | ||
27 | LIGHT_TRACKMUTE, | ||
28 | LIGHT_TRACKSOLO, | ||
29 | LIGHT_TRACKREC, | ||
30 | LIGHT_READ, | ||
31 | LIGHT_WRITE, | ||
32 | LIGHT_ANYSOLO, | ||
33 | LIGHT_AUTO, | ||
34 | LIGHT_F4, | ||
35 | LIGHT_RECORD, | ||
36 | LIGHT_WINDOW, | ||
37 | LIGHT_PLUGIN, | ||
38 | LIGHT_F3, | ||
39 | LIGHT_LOOP | ||
40 | }; | ||
41 | |||
42 | static const char *Lightname[] = { "eq", | ||
43 | "out", | ||
44 | "f2", | ||
45 | "send", | ||
46 | "in", | ||
47 | "f1", | ||
48 | "pan", | ||
49 | "undef1", | ||
50 | "undef2", | ||
51 | "shift", | ||
52 | "mute", | ||
53 | "tracksolo", | ||
54 | "trackrec", | ||
55 | "read", | ||
56 | "write", | ||
57 | "anysolo", | ||
58 | "auto", | ||
59 | "f4", | ||
60 | "record", | ||
61 | "window", | ||
62 | "plugin", | ||
63 | "f3", | ||
64 | "loop", | ||
65 | NULL }; | ||
66 | |||
67 | #define BUTTONMASK_BATTERY 0x00004000 | ||
68 | #define BUTTONMASK_BACKLIGHT 0x00008000 | ||
69 | #define BUTTONMASK_FASTFORWARD 0x04000000 | ||
70 | #define BUTTONMASK_TRACKMUTE 0x00040000 | ||
71 | #define BUTTONMASK_TRACKSOLO 0x00800000 | ||
72 | #define BUTTONMASK_TRACKLEFT 0x80000000 | ||
73 | #define BUTTONMASK_RECORD 0x02000000 | ||
74 | #define BUTTONMASK_SHIFT 0x20000000 | ||
75 | #define BUTTONMASK_PUNCH 0x00800000 | ||
76 | #define BUTTONMASK_TRACKRIGHT 0x00020000 | ||
77 | #define BUTTONMASK_REWIND 0x01000000 | ||
78 | #define BUTTONMASK_STOP 0x10000000 | ||
79 | #define BUTTONMASK_LOOP 0x00010000 | ||
80 | #define BUTTONMASK_TRACKREC 0x00001000 | ||
81 | #define BUTTONMASK_PLAY 0x08000000 | ||
82 | #define BUTTONMASK_TOUCH1 0x00000008 | ||
83 | #define BUTTONMASK_TOUCH2 0x00000010 | ||
84 | #define BUTTONMASK_TOUCH3 0x00000020 | ||
85 | |||
86 | #define BUTTONMASK_PRESS1 0x00000009 | ||
87 | #define BUTTONMASK_PRESS2 0x00008010 | ||
88 | #define BUTTONMASK_PRESS3 0x00002020 | ||
89 | |||
90 | // last 3 bytes are the slider position | ||
91 | // 40 is the actual slider moving, the most sig bits, and 3 lsb | ||
92 | |||
93 | #define BUTTONMASK_FLIP 0x40000000 | ||
94 | #define BUTTONMASK_F1 0x00100000 | ||
95 | #define BUTTONMASK_F2 0x00400000 | ||
96 | #define BUTTONMASK_F3 0x00200000 | ||
97 | #define BUTTONMASK_F4 0x00080000 | ||
98 | #define BUTTONMASK_PAN 0x00000200 | ||
99 | #define BUTTONMASK_SEND 0x00000800 | ||
100 | #define BUTTONMASK_EQ 0x00004000 | ||
101 | #define BUTTONMASK_PLUGIN 0x00000400 | ||
102 | #define BUTTONMASK_AUTO 0x00000100 | ||
103 | |||
104 | |||
105 | // #define BUTTONMASK_FOOTSWITCH FIXME | ||
106 | |||
107 | // Lookup. name. midi out. midi in. | ||
108 | |||
109 | struct buttonmap_t { | ||
110 | u32 mask; | ||
111 | short midi_in; | ||
112 | short midi_out; | ||
113 | char *name; | ||
114 | // void (*function) (buttonmap_t *); | ||
115 | void (*function) (void); | ||
116 | }; | ||
117 | |||
diff --git a/drivers/staging/frontier/alphatrack_sysfs.c b/drivers/staging/frontier/alphatrack_sysfs.c new file mode 100644 index 000000000000..21af6a2d70bb --- /dev/null +++ b/drivers/staging/frontier/alphatrack_sysfs.c | |||
@@ -0,0 +1,279 @@ | |||
1 | /* This was an attempt - ultimately proved pointless - at making a full fledged sysfs interface to the alphatrack */ | ||
2 | /* won't even compile at present */ | ||
3 | |||
4 | char *alphatrack_sys_margs; | ||
5 | spinlock_t alphatrack_sys_margs_lock; | ||
6 | |||
7 | struct alphatrack_attr { | ||
8 | struct attribute attr; | ||
9 | ssize_t (*show)(struct device *, char *); | ||
10 | ssize_t (*store)(struct device *, const char *, size_t); | ||
11 | }; | ||
12 | |||
13 | #define ALPHATRACK_ATTR(name, mode, show, store) \ | ||
14 | static struct alphatrack_attr alphatrack_attr_##name = __ATTR(name, mode, show, store) | ||
15 | |||
16 | /* now a great deal of callback code generation */ | ||
17 | |||
18 | // FOREACH_LIGHT(show_set_light) | ||
19 | // FOREACH_BUTTON(show_set_button) | ||
20 | |||
21 | show_set_light(LIGHT_RECORD); show_set_light(LIGHT_EQ); show_set_light(LIGHT_OUT); | ||
22 | show_set_light(LIGHT_F2); show_set_light(LIGHT_SEND); show_set_light(LIGHT_IN); | ||
23 | show_set_light(LIGHT_F1); show_set_light(LIGHT_PAN); show_set_light(LIGHT_UNDEF1); | ||
24 | show_set_light(LIGHT_UNDEF2); show_set_light(LIGHT_SHIFT); show_set_light(LIGHT_TRACKMUTE); | ||
25 | show_set_light(LIGHT_TRACKSOLO); show_set_light(LIGHT_TRACKREC); show_set_light(LIGHT_READ); | ||
26 | show_set_light(LIGHT_WRITE); show_set_light(LIGHT_ANYSOLO); show_set_light(LIGHT_AUTO); | ||
27 | show_set_light(LIGHT_F4); show_set_light(LIGHT_RECORD); show_set_light(LIGHT_WINDOW); | ||
28 | show_set_light(LIGHT_PLUGIN); show_set_light(LIGHT_F3); show_set_light(LIGHT_LOOP); | ||
29 | |||
30 | show_set_opt(enable); show_set_opt(offline); show_set_opt(compress_fader); show_set_opt(dump_state); | ||
31 | show_set_int(fader); show_set_int(event); | ||
32 | |||
33 | |||
34 | static ssize_t show_lights(struct device *dev, struct device_attribute *attr, char *buf) | ||
35 | { | ||
36 | struct usb_interface *intf = to_usb_interface(dev); | ||
37 | struct usb_alphatrack *t = usb_get_intfdata(intf); | ||
38 | return sprintf(buf, "%d\n", t->lights); | ||
39 | } | ||
40 | |||
41 | static ssize_t set_lights(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | ||
42 | { | ||
43 | struct usb_interface *intf = to_usb_interface(dev); | ||
44 | struct usb_alphatrack *t = usb_get_intfdata(intf); | ||
45 | int temp = simple_strtoul(buf, NULL, 10); | ||
46 | t->lights = temp; | ||
47 | return count; | ||
48 | } | ||
49 | |||
50 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_lights, set_lights); | ||
51 | |||
52 | |||
53 | ALPHATRACK_ATTR(LightRecord, 0200, NULL, LightRecord_store); | ||
54 | |||
55 | static struct attribute *alphatrack_attrs[] = { | ||
56 | &alphatrack_attr_LightRecord.attr, | ||
57 | NULL, | ||
58 | }; | ||
59 | |||
60 | static ssize_t alphatrack_attr_show(struct kobject *kobj, struct attribute *attr, | ||
61 | char *buf) | ||
62 | { | ||
63 | struct device *sdp = container_of(kobj, struct device, kobj); | ||
64 | struct alphatrack_attr *a = container_of(attr, struct alphatrack_attr, attr); | ||
65 | return a->show ? a->show(sdp, buf) : 0; | ||
66 | } | ||
67 | |||
68 | static ssize_t alphatrack_attr_store(struct kobject *kobj, struct attribute *attr, | ||
69 | const char *buf, size_t len) | ||
70 | { | ||
71 | struct device *sdp = container_of(kobj, struct device, kobj); | ||
72 | struct alphatrack_attr *a = container_of(attr, struct alphatrack_attr, attr); | ||
73 | return a->store ? a->store(sdp, buf, len) : len; | ||
74 | } | ||
75 | |||
76 | static struct sysfs_ops alphatrack_attr_ops = { | ||
77 | .show = alphatrack_attr_show, | ||
78 | .store = alphatrack_attr_store, | ||
79 | }; | ||
80 | |||
81 | static struct kobj_type alphatrack_ktype = { | ||
82 | .default_attrs = alphatrack_attrs, | ||
83 | .sysfs_ops = &alphatrack_attr_ops, | ||
84 | }; | ||
85 | |||
86 | static struct kset alphatrack_kset = { | ||
87 | .subsys = &fs_subsys, | ||
88 | .kobj = {.name = "alphatrack"}, | ||
89 | .ktype = &alphatrack_ktype, | ||
90 | }; | ||
91 | |||
92 | |||
93 | static struct attribute *lights_attrs[] = { | ||
94 | &tune_attr_demote_secs.attr, | ||
95 | NULL, | ||
96 | }; | ||
97 | |||
98 | |||
99 | static struct attribute_group leds_group = { | ||
100 | .name = "leds", | ||
101 | .attrs = lights_attrs, | ||
102 | }; | ||
103 | |||
104 | static struct attribute_group faders_group = { | ||
105 | .name = "faders", | ||
106 | .attrs = faders_attrs, | ||
107 | }; | ||
108 | |||
109 | static struct attribute_group lcds_group = { | ||
110 | .name = "lcds", | ||
111 | .attrs = lcds_attrs, | ||
112 | }; | ||
113 | |||
114 | static struct attribute_group wheels_group = { | ||
115 | .name = "wheels", | ||
116 | .attrs = wheels_attrs, | ||
117 | }; | ||
118 | |||
119 | static struct attribute_group touchsurfaces_group = { | ||
120 | .name = "touchsurfaces", | ||
121 | .attrs = touchsurfaces_attrs, | ||
122 | }; | ||
123 | |||
124 | static struct attribute_group buttons_group = { | ||
125 | .name = "buttons", | ||
126 | .attrs = buttons_attrs, | ||
127 | }; | ||
128 | |||
129 | |||
130 | int alphatrack_sys_fs_add(struct device *sdp) | ||
131 | { | ||
132 | int error; | ||
133 | |||
134 | sdp->kobj.kset = &alphatrack_kset; | ||
135 | sdp->kobj.ktype = &alphatrack_ktype; | ||
136 | |||
137 | // error = kobject_set_name(&sdp->kobj, "%s", sdp->sd_table_name); | ||
138 | error = kobject_set_name(&sdp->kobj, "%s", "alphatrack"); | ||
139 | if (error) | ||
140 | goto fail; | ||
141 | |||
142 | error = kobject_register(&sdp->kobj); | ||
143 | if (error) | ||
144 | goto fail; | ||
145 | |||
146 | error = sysfs_create_group(&sdp->kobj, &lcds_group); | ||
147 | if (error) | ||
148 | goto fail_reg; | ||
149 | |||
150 | error = sysfs_create_group(&sdp->kobj, &leds_group); | ||
151 | if (error) | ||
152 | goto fail_leds; | ||
153 | |||
154 | error = sysfs_create_group(&sdp->kobj, &wheels_group); | ||
155 | if (error) | ||
156 | goto fail_wheels; | ||
157 | |||
158 | error = sysfs_create_group(&sdp->kobj, &faders_group); | ||
159 | if (error) | ||
160 | goto fail_lcds; | ||
161 | |||
162 | error = sysfs_create_group(&sdp->kobj, &buttons_group); | ||
163 | if (error) | ||
164 | goto fail_faders; | ||
165 | |||
166 | error = sysfs_create_group(&sdp->kobj, &touchsurfaces_group); | ||
167 | if (error) | ||
168 | goto fail_buttons; | ||
169 | |||
170 | return 0; | ||
171 | |||
172 | |||
173 | fail_buttons: | ||
174 | sysfs_remove_group(&sdp->kobj, &buttons_group); | ||
175 | fail_faders: | ||
176 | sysfs_remove_group(&sdp->kobj, &faders_group); | ||
177 | fail_wheels: | ||
178 | sysfs_remove_group(&sdp->kobj, &wheels_group); | ||
179 | fail_lcds: | ||
180 | sysfs_remove_group(&sdp->kobj, &lcds_group); | ||
181 | fail_leds: | ||
182 | sysfs_remove_group(&sdp->kobj, &leds_group); | ||
183 | |||
184 | |||
185 | |||
186 | fail_reg: | ||
187 | kobject_unregister(&sdp->kobj); | ||
188 | fail: | ||
189 | fs_err(sdp, "error %d adding sysfs files", error); | ||
190 | return error; | ||
191 | } | ||
192 | |||
193 | // int sysfs_create_link(struct kobject *kobj, | ||
194 | // struct kobject *target, | ||
195 | // char *name); | ||
196 | |||
197 | void alphatrack_sys_fs_del(struct device *sdp) | ||
198 | { | ||
199 | sysfs_remove_group(&sdp->kobj, &touchsurfaces_group); | ||
200 | sysfs_remove_group(&sdp->kobj, &buttons_group); | ||
201 | sysfs_remove_group(&sdp->kobj, &faders_group); | ||
202 | sysfs_remove_group(&sdp->kobj, &lcds_group); | ||
203 | sysfs_remove_group(&sdp->kobj, &wheels_group); | ||
204 | sysfs_remove_group(&sdp->kobj, &leds_group) | ||
205 | //void sysfs_remove_link(struct kobject *kobj, char *name); | ||
206 | kobject_unregister(&sdp->kobj); | ||
207 | } | ||
208 | |||
209 | int alphatrack_sys_init(void) | ||
210 | { | ||
211 | alphatrack_sys_margs = NULL; | ||
212 | spin_lock_init(&alphatrack_sys_margs_lock); | ||
213 | return kset_register(&alphatrack_kset); | ||
214 | } | ||
215 | |||
216 | void alphatrack_sys_uninit(void) | ||
217 | { | ||
218 | kfree(alphatrack_sys_margs); | ||
219 | kset_unregister(&alphatrack_kset); | ||
220 | } | ||
221 | |||
222 | |||
223 | //decl_subsys(char *name, struct kobj_type *type, | ||
224 | // struct kset_hotplug_ops *hotplug_ops); | ||
225 | |||
226 | /* End of all the crazy sysfs stuff */ | ||
227 | |||
228 | #define SYSEX_INQUIRE signed char *SYSEX_INQUIRE[] = { 0xf0,0x7e,0x00,0x06,0x01,0x17 }; | ||
229 | |||
230 | #define COMMAND(NAME,CONT_NAME) { BUTTONMASK_##NAME, ((0x90 << 8) | CONT_NAME), ((0x90 << 8) | CONT_NAME), #NAME, NAME ## _set } | ||
231 | #define ROTARY(NAME,CONT_NAME) { FADER_##NAME, ((0xb0 << 8) | CONT_NAME), ((0xb0 << 8) | CONT_NAME), #NAME, NAME ## _set } | ||
232 | #define SPOSITION(NAME,CONT_NAME) { BUTTON_##NAME ((0xe9 << 8) | CONT_NAME), #NAME, NAME ## _set } | ||
233 | #define ENDCOMMAND { 0,NULL,0,NULL,NULL} | ||
234 | |||
235 | /* Now that we've generated all our callbacks */ | ||
236 | |||
237 | static struct buttonmap_t buttonmap[] = | ||
238 | { | ||
239 | COMMAND (REWIND,0x5b), | ||
240 | COMMAND (FASTFORWARD,0x5c), | ||
241 | COMMAND (STOP,0x5d), | ||
242 | COMMAND (PLAY,0x5e), | ||
243 | COMMAND (RECORD,0x5f), | ||
244 | COMMAND (SHIFT,0x46), | ||
245 | COMMAND (TRACKLEFT,0x57), | ||
246 | COMMAND (TRACKRIGHT,0x58), | ||
247 | COMMAND (LOOP,0x56), | ||
248 | COMMAND (FLIP,0x32), | ||
249 | COMMAND (MUTE,0x10), | ||
250 | COMMAND (F1,0x36), | ||
251 | COMMAND (F2,0x37), | ||
252 | COMMAND (F3,0x38), | ||
253 | COMMAND (F4,0x39), | ||
254 | COMMAND (SOLO,0x08), | ||
255 | COMMAND (ANY,0x73), | ||
256 | COMMAND (PAN,0x2a), | ||
257 | COMMAND (SEND,0x29), | ||
258 | COMMAND (EQ,0x2c), | ||
259 | COMMAND (PLUGIN,0x2b), | ||
260 | COMMAND (AUTO,0x4a), | ||
261 | COMMAND (TRACKREC,0x00), | ||
262 | COMMAND (FOOTSWITCH1,0x67), | ||
263 | COMMAND (KNOBTOUCH1,0x78), | ||
264 | COMMAND (KNOBPUSH1,0x20), | ||
265 | ROTARY (KNOBTURN1,0x10), | ||
266 | COMMAND (KNOBTOUCH2,0x79), | ||
267 | COMMAND (KNOBPUSH2,0x21), | ||
268 | ROTARY (KNOBTURN2,0x11), | ||
269 | COMMAND (KNOBTOUCH3,0x7a), | ||
270 | COMMAND (KNOBPUSH3,0x22), | ||
271 | ROTARY (KNOBTURN3,0x12), | ||
272 | COMMAND (FADERTOUCH1,0x68), | ||
273 | COMMAND (STRIPTOUCH1,0x74), | ||
274 | COMMAND (STRIPTOUCH2,0x6b), | ||
275 | SPOSITION (STRIPPOS1,0x00), | ||
276 | ENDCOMMAND | ||
277 | }; | ||
278 | |||
279 | |||
diff --git a/drivers/staging/frontier/frontier_compat.h b/drivers/staging/frontier/frontier_compat.h new file mode 100644 index 000000000000..00450e637ac8 --- /dev/null +++ b/drivers/staging/frontier/frontier_compat.h | |||
@@ -0,0 +1,63 @@ | |||
1 | /* USB defines for older kernels */ | ||
2 | |||
3 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | ||
4 | |||
5 | /** | ||
6 | * usb_endpoint_dir_out - check if the endpoint has OUT direction | ||
7 | * @epd: endpoint to be checked | ||
8 | * | ||
9 | * Returns true if the endpoint is of type OUT, otherwise it returns false. | ||
10 | */ | ||
11 | |||
12 | static inline int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd) | ||
13 | { | ||
14 | return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); | ||
15 | } | ||
16 | |||
17 | static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd) | ||
18 | { | ||
19 | return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN); | ||
20 | } | ||
21 | |||
22 | |||
23 | /** | ||
24 | * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type | ||
25 | * @epd: endpoint to be checked | ||
26 | * | ||
27 | * Returns true if the endpoint is of type interrupt, otherwise it returns | ||
28 | * false. | ||
29 | */ | ||
30 | static inline int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd) | ||
31 | { | ||
32 | return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == | ||
33 | USB_ENDPOINT_XFER_INT); | ||
34 | } | ||
35 | |||
36 | |||
37 | /** | ||
38 | * usb_endpoint_is_int_in - check if the endpoint is interrupt IN | ||
39 | * @epd: endpoint to be checked | ||
40 | * | ||
41 | * Returns true if the endpoint has interrupt transfer type and IN direction, | ||
42 | * otherwise it returns false. | ||
43 | */ | ||
44 | |||
45 | static inline int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd) | ||
46 | { | ||
47 | return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd)); | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT | ||
52 | * @epd: endpoint to be checked | ||
53 | * | ||
54 | * Returns true if the endpoint has interrupt transfer type and OUT direction, | ||
55 | * otherwise it returns false. | ||
56 | */ | ||
57 | |||
58 | static inline int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd) | ||
59 | { | ||
60 | return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd)); | ||
61 | } | ||
62 | |||
63 | #endif /* older kernel versions */ | ||
diff --git a/drivers/staging/frontier/surface_sysfs.h b/drivers/staging/frontier/surface_sysfs.h new file mode 100644 index 000000000000..d50a562d658a --- /dev/null +++ b/drivers/staging/frontier/surface_sysfs.h | |||
@@ -0,0 +1,100 @@ | |||
1 | /* If you are going to abuse the preprocessor, why not ABUSE the preprocessor? | ||
2 | I stuck this header in a separate file so I don't have to look at it */ | ||
3 | |||
4 | // FIXME Need locking or atomic ops | ||
5 | |||
6 | #define show_set_mbit(dname,value,bit) \ | ||
7 | static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ | ||
8 | { \ | ||
9 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
10 | struct usb_##dname *t = usb_get_intfdata(intf); \ | ||
11 | int temp = (1 && (t->value & (1 << bit))); \ | ||
12 | return sprintf(buf, "%d\n", temp); \ | ||
13 | } \ | ||
14 | static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ | ||
15 | { \ | ||
16 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
17 | struct usb_##dname *t = usb_get_intfdata(intf); \ | ||
18 | int temp = simple_strtoul(buf, NULL, 10); \ | ||
19 | if(temp > 0) { long b = 1 << bit; t->value |= b; } \ | ||
20 | else { long b = ~(1 << bit); t->value &= b ; \ | ||
21 | return count; \ | ||
22 | } \ | ||
23 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value); | ||
24 | |||
25 | #define show_set_ebit(dname,enumname,value,bit) \ | ||
26 | static ssize_t show_##bit(struct device *dev, struct device_attribute *attr, char *buf) \ | ||
27 | { \ | ||
28 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
29 | struct usb_##dname *t = usb_get_intfdata(intf); \ | ||
30 | enum enumname l = bit; \ | ||
31 | int temp = t->value & (1 << l); \ | ||
32 | return sprintf(buf, "%d\n", temp); \ | ||
33 | } \ | ||
34 | static ssize_t set_##bit(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ | ||
35 | { \ | ||
36 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
37 | struct usb_##dname *t = usb_get_intfdata(intf); \ | ||
38 | int temp = simple_strtoul(buf, NULL, 10); \ | ||
39 | enum enumname l = bit;\ | ||
40 | long b = 1 << l; \ | ||
41 | if(temp > 0) { t->value |= b; } \ | ||
42 | else { t->value &= ~b ; \ | ||
43 | return count; \ | ||
44 | } \ | ||
45 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value); | ||
46 | |||
47 | // FIXME FOR CORRECTLY SETTING HEX from a string | ||
48 | #define show_set_mcmd(dname,value) \ | ||
49 | static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ | ||
50 | { \ | ||
51 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
52 | struct usb_##dname *t = usb_get_intfdata(intf); \ | ||
53 | int count = 0;\ | ||
54 | int i; \ | ||
55 | for (i = 0,i<sizeof(dname); i++) count += snprintf(buf, "%02x",t->dname[i]); \ | ||
56 | return(count);\ | ||
57 | } \ | ||
58 | static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ | ||
59 | { \ | ||
60 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
61 | struct usb_##dname *t = usb_get_intfdata(intf); \ | ||
62 | int temp = simple_strtoul(buf, NULL, 10); \ | ||
63 | t->value = temp; \ | ||
64 | return count; \ | ||
65 | } \ | ||
66 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value); | ||
67 | |||
68 | #define show_set_mint(dname,value) \ | ||
69 | static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ | ||
70 | { \ | ||
71 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
72 | struct usb_##dname *t = usb_get_intfdata(intf); \ | ||
73 | return sprintf(buf, "%d\n", t->value); \ | ||
74 | } \ | ||
75 | static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ | ||
76 | { \ | ||
77 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
78 | struct usb_##dname *t = usb_get_intfdata(intf); \ | ||
79 | int temp = simple_strtoul(buf, NULL, 10); \ | ||
80 | t->value = temp; \ | ||
81 | return count; \ | ||
82 | } \ | ||
83 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value); | ||
84 | |||
85 | #define show_set_mchar(dname,value) \ | ||
86 | static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ | ||
87 | { \ | ||
88 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
89 | struct usb_##dname *t = usb_get_intfdata(intf); \ | ||
90 | return sprintf(buf, "%c\n", t->value); \ | ||
91 | } \ | ||
92 | static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ | ||
93 | { \ | ||
94 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
95 | struct usb_##dname *t = usb_get_intfdata(intf); \ | ||
96 | int temp = simple_strtoul(buf, NULL, 10); \ | ||
97 | t->value = temp; \ | ||
98 | return count; \ | ||
99 | } \ | ||
100 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value); | ||
diff --git a/drivers/staging/frontier/tranzport.c b/drivers/staging/frontier/tranzport.c new file mode 100644 index 000000000000..32d9c5084c88 --- /dev/null +++ b/drivers/staging/frontier/tranzport.c | |||
@@ -0,0 +1,1017 @@ | |||
1 | /* | ||
2 | * Frontier Designs Tranzport driver | ||
3 | * | ||
4 | * Copyright (C) 2007 Michael Taht (m@taht.net) | ||
5 | * | ||
6 | * Based on the usbled driver and ldusb drivers by | ||
7 | * | ||
8 | * Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com) | ||
9 | * Copyright (C) 2005 Michael Hund <mhund@ld-didactic.de> | ||
10 | * | ||
11 | * The ldusb driver was, in turn, derived from Lego USB Tower driver | ||
12 | * Copyright (C) 2003 David Glance <advidgsf@sourceforge.net> | ||
13 | * 2001-2004 Juergen Stuber <starblue@users.sourceforge.net> | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or | ||
16 | * modify it under the terms of the GNU General Public License as | ||
17 | * published by the Free Software Foundation, version 2. | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * This driver uses a ring buffer for time critical reading of | ||
23 | * interrupt in reports and provides read and write methods for | ||
24 | * raw interrupt reports. | ||
25 | */ | ||
26 | |||
27 | /* Note: this currently uses a dumb ringbuffer for reads and writes. | ||
28 | * A more optimal driver would cache and kill off outstanding urbs that are | ||
29 | * now invalid, and ignore ones that already were in the queue but valid | ||
30 | * as we only have 17 commands for the tranzport. In particular this is | ||
31 | * key for getting lights to flash in time as otherwise many commands | ||
32 | * can be buffered up before the light change makes it to the interface. | ||
33 | */ | ||
34 | |||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/slab.h> | ||
39 | #include <linux/module.h> | ||
40 | #include <linux/mutex.h> | ||
41 | #include <linux/version.h> | ||
42 | |||
43 | #include <asm/uaccess.h> | ||
44 | #include <linux/input.h> | ||
45 | #include <linux/usb.h> | ||
46 | #include <linux/poll.h> | ||
47 | |||
48 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | ||
49 | #include frontier_compat.h | ||
50 | #endif | ||
51 | |||
52 | /* Define these values to match your devices */ | ||
53 | #define VENDOR_ID 0x165b | ||
54 | #define PRODUCT_ID 0x8101 | ||
55 | |||
56 | #ifdef CONFIG_USB_DYNAMIC_MINORS | ||
57 | #define USB_TRANZPORT_MINOR_BASE 0 | ||
58 | #else | ||
59 | // FIXME 176 - is the ldusb driver's minor - apply for a minor soon | ||
60 | #define USB_TRANZPORT_MINOR_BASE 177 | ||
61 | #endif | ||
62 | |||
63 | /* table of devices that work with this driver */ | ||
64 | static struct usb_device_id usb_tranzport_table [] = { | ||
65 | { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, | ||
66 | { } /* Terminating entry */ | ||
67 | }; | ||
68 | |||
69 | MODULE_DEVICE_TABLE(usb, usb_tranzport_table); | ||
70 | MODULE_VERSION("0.33"); | ||
71 | MODULE_AUTHOR("Mike Taht <m@taht.net>"); | ||
72 | MODULE_DESCRIPTION("Tranzport USB Driver"); | ||
73 | MODULE_LICENSE("GPL"); | ||
74 | MODULE_SUPPORTED_DEVICE("Frontier Designs Tranzport Control Surface"); | ||
75 | |||
76 | /* These two aren't done yet */ | ||
77 | |||
78 | #define SUPPRESS_EXTRA_ONLINE_EVENTS 0 | ||
79 | #define BUFFERED_WRITES 0 | ||
80 | |||
81 | #define SUPPRESS_EXTRA_OFFLINE_EVENTS 1 | ||
82 | #define COMPRESS_WHEEL_EVENTS 1 | ||
83 | #define BUFFERED_READS 1 | ||
84 | #define RING_BUFFER_SIZE 1000 | ||
85 | #define WRITE_BUFFER_SIZE 34 | ||
86 | #define TRANZPORT_USB_TIMEOUT 10 | ||
87 | |||
88 | |||
89 | static int debug = 0; | ||
90 | |||
91 | /* Use our own dbg macro */ | ||
92 | #define dbg_info(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) | ||
93 | |||
94 | /* Module parameters */ | ||
95 | |||
96 | module_param(debug, int, S_IRUGO | S_IWUSR); | ||
97 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | ||
98 | |||
99 | /* All interrupt in transfers are collected in a ring buffer to | ||
100 | * avoid racing conditions and get better performance of the driver. | ||
101 | */ | ||
102 | |||
103 | static int ring_buffer_size = RING_BUFFER_SIZE; | ||
104 | |||
105 | module_param(ring_buffer_size, int, S_IRUGO); | ||
106 | MODULE_PARM_DESC(ring_buffer_size, "Read ring buffer size in reports"); | ||
107 | |||
108 | /* The write_buffer can one day contain more than one interrupt out transfer. | ||
109 | */ | ||
110 | static int write_buffer_size = WRITE_BUFFER_SIZE; | ||
111 | module_param(write_buffer_size, int, S_IRUGO); | ||
112 | MODULE_PARM_DESC(write_buffer_size, "Write buffer size"); | ||
113 | |||
114 | /* | ||
115 | * Increase the interval for debugging purposes. | ||
116 | * or set to 1 to use the standard interval from the endpoint descriptors. | ||
117 | */ | ||
118 | |||
119 | static int min_interrupt_in_interval = TRANZPORT_USB_TIMEOUT; | ||
120 | module_param(min_interrupt_in_interval, int, 0); | ||
121 | MODULE_PARM_DESC(min_interrupt_in_interval, "Minimum interrupt in interval in ms"); | ||
122 | |||
123 | static int min_interrupt_out_interval = TRANZPORT_USB_TIMEOUT; | ||
124 | module_param(min_interrupt_out_interval, int, 0); | ||
125 | MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in ms"); | ||
126 | |||
127 | struct tranzport_cmd { | ||
128 | unsigned char cmd[8]; | ||
129 | }; | ||
130 | |||
131 | enum LightID { | ||
132 | LightRecord = 0, | ||
133 | LightTrackrec, | ||
134 | LightTrackmute, | ||
135 | LightTracksolo, | ||
136 | LightAnysolo, | ||
137 | LightLoop, | ||
138 | LightPunch | ||
139 | }; | ||
140 | |||
141 | static const char *Lightname[8] = { "LightRecord", | ||
142 | "LightTracrec", | ||
143 | "LightTrackmute", | ||
144 | "LightTrackSolo", | ||
145 | "LightAnySolo", | ||
146 | "LightLoop", | ||
147 | "LightPunch", | ||
148 | NULL }; | ||
149 | |||
150 | |||
151 | /* Structure to hold all of our device specific stuff */ | ||
152 | |||
153 | struct usb_tranzport { | ||
154 | struct semaphore sem; /* locks this structure */ | ||
155 | struct usb_interface* intf; /* save off the usb interface pointer */ | ||
156 | |||
157 | int open_count; /* number of times this port has been opened */ | ||
158 | |||
159 | struct tranzport_cmd (*ring_buffer)[RING_BUFFER_SIZE]; /* just make c happy */ | ||
160 | unsigned int ring_head; | ||
161 | unsigned int ring_tail; | ||
162 | |||
163 | wait_queue_head_t read_wait; | ||
164 | wait_queue_head_t write_wait; | ||
165 | |||
166 | unsigned char* interrupt_in_buffer; | ||
167 | struct usb_endpoint_descriptor* interrupt_in_endpoint; | ||
168 | struct urb* interrupt_in_urb; | ||
169 | int interrupt_in_interval; | ||
170 | size_t interrupt_in_endpoint_size; | ||
171 | int interrupt_in_running; | ||
172 | int interrupt_in_done; | ||
173 | |||
174 | char* interrupt_out_buffer; | ||
175 | struct usb_endpoint_descriptor* interrupt_out_endpoint; | ||
176 | struct urb* interrupt_out_urb; | ||
177 | int interrupt_out_interval; | ||
178 | size_t interrupt_out_endpoint_size; | ||
179 | int interrupt_out_busy; | ||
180 | |||
181 | /* Sysfs and translation support */ | ||
182 | |||
183 | int event; /* alternate interface to events */ | ||
184 | int wheel; /* - for negative, 0 for none, + for positive */ | ||
185 | unsigned char dump_state; /* 0 if disabled 1 if enabled */ | ||
186 | unsigned char enable; /* 0 if disabled 1 if enabled */ | ||
187 | unsigned char offline; /* if the device is out of range or asleep */ | ||
188 | unsigned char compress_wheel; /* flag to compress wheel events */ | ||
189 | unsigned char light; /* 7 bits used */ | ||
190 | unsigned char last_cmd[8]; | ||
191 | unsigned char last_input[8]; | ||
192 | unsigned char screen[40]; // We'll also have cells | ||
193 | |||
194 | }; | ||
195 | |||
196 | /* prevent races between open() and disconnect() */ | ||
197 | static DEFINE_MUTEX(disconnect_mutex); | ||
198 | |||
199 | static struct usb_driver usb_tranzport_driver; | ||
200 | |||
201 | /** | ||
202 | * usb_tranzport_abort_transfers | ||
203 | * aborts transfers and frees associated data structures | ||
204 | */ | ||
205 | static void usb_tranzport_abort_transfers(struct usb_tranzport *dev) | ||
206 | { | ||
207 | /* shutdown transfer */ | ||
208 | if (dev->interrupt_in_running) { | ||
209 | dev->interrupt_in_running = 0; | ||
210 | if (dev->intf) | ||
211 | usb_kill_urb(dev->interrupt_in_urb); | ||
212 | } | ||
213 | if (dev->interrupt_out_busy) | ||
214 | if (dev->intf) | ||
215 | usb_kill_urb(dev->interrupt_out_urb); | ||
216 | } | ||
217 | |||
218 | // FIXME ~light not good enough or correct - need atomic set_bit | ||
219 | |||
220 | #define show_set_light(value) \ | ||
221 | static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ | ||
222 | { \ | ||
223 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
224 | struct usb_tranzport *t = usb_get_intfdata(intf); \ | ||
225 | enum LightID light = value; \ | ||
226 | int temp = (1 && (t->light & (1 << light))); \ | ||
227 | return sprintf(buf, "%d\n", temp ); \ | ||
228 | } \ | ||
229 | static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ | ||
230 | { \ | ||
231 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
232 | struct usb_tranzport *t = usb_get_intfdata(intf); \ | ||
233 | int temp = simple_strtoul(buf, NULL, 10); \ | ||
234 | enum LightID light = (temp << value) & (t->light << value); \ | ||
235 | t->light = (t->light & ~light) ; \ | ||
236 | return count; \ | ||
237 | } \ | ||
238 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value); | ||
239 | |||
240 | show_set_light(LightRecord); | ||
241 | show_set_light(LightTrackrec); | ||
242 | show_set_light(LightTrackmute); | ||
243 | show_set_light(LightTracksolo); | ||
244 | show_set_light(LightAnysolo); | ||
245 | show_set_light(LightLoop); | ||
246 | show_set_light(LightPunch); | ||
247 | |||
248 | |||
249 | #define show_set_int(value) \ | ||
250 | static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ | ||
251 | { \ | ||
252 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
253 | struct usb_tranzport *t = usb_get_intfdata(intf); \ | ||
254 | \ | ||
255 | return sprintf(buf, "%d\n", t->value); \ | ||
256 | } \ | ||
257 | static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ | ||
258 | { \ | ||
259 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
260 | struct usb_tranzport *t = usb_get_intfdata(intf); \ | ||
261 | int temp = simple_strtoul(buf, NULL, 10); \ | ||
262 | \ | ||
263 | t->value = temp; \ | ||
264 | return count; \ | ||
265 | } \ | ||
266 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value); | ||
267 | |||
268 | show_set_int(enable); | ||
269 | show_set_int(offline); | ||
270 | show_set_int(compress_wheel); | ||
271 | show_set_int(dump_state); | ||
272 | show_set_int(wheel); | ||
273 | show_set_int(event); | ||
274 | |||
275 | #define show_set_cmd(value) \ | ||
276 | static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ | ||
277 | { \ | ||
278 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
279 | struct usb_tranzport *t = usb_get_intfdata(intf); \ | ||
280 | \ | ||
281 | return sprintf(buf, "%d\n", t->value); \ | ||
282 | } \ | ||
283 | static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ | ||
284 | { \ | ||
285 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
286 | struct usb_tranzport *t = usb_get_intfdata(intf); \ | ||
287 | int temp = simple_strtoul(buf, NULL, 10); \ | ||
288 | \ | ||
289 | t->value = temp; \ | ||
290 | return count; \ | ||
291 | } \ | ||
292 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value); | ||
293 | |||
294 | |||
295 | |||
296 | |||
297 | /** | ||
298 | * usb_tranzport_delete | ||
299 | */ | ||
300 | static void usb_tranzport_delete(struct usb_tranzport *dev) | ||
301 | { | ||
302 | usb_tranzport_abort_transfers(dev); | ||
303 | /* This is just too twisted to be correct */ | ||
304 | if(dev->intf != NULL) { | ||
305 | device_remove_file(&dev->intf->dev, &dev_attr_LightRecord); | ||
306 | device_remove_file(&dev->intf->dev, &dev_attr_LightTrackrec); | ||
307 | device_remove_file(&dev->intf->dev, &dev_attr_LightTrackmute); | ||
308 | device_remove_file(&dev->intf->dev, &dev_attr_LightTracksolo); | ||
309 | device_remove_file(&dev->intf->dev, &dev_attr_LightTrackmute); | ||
310 | device_remove_file(&dev->intf->dev, &dev_attr_LightAnysolo); | ||
311 | device_remove_file(&dev->intf->dev, &dev_attr_LightLoop); | ||
312 | device_remove_file(&dev->intf->dev, &dev_attr_LightPunch); | ||
313 | device_remove_file(&dev->intf->dev, &dev_attr_wheel); | ||
314 | device_remove_file(&dev->intf->dev, &dev_attr_enable); | ||
315 | device_remove_file(&dev->intf->dev, &dev_attr_event); | ||
316 | device_remove_file(&dev->intf->dev, &dev_attr_offline); | ||
317 | device_remove_file(&dev->intf->dev, &dev_attr_compress_wheel); | ||
318 | |||
319 | device_remove_file(&dev->intf->dev, &dev_attr_dump_state); | ||
320 | } | ||
321 | |||
322 | /* free data structures */ | ||
323 | usb_free_urb(dev->interrupt_in_urb); | ||
324 | usb_free_urb(dev->interrupt_out_urb); | ||
325 | kfree(dev->ring_buffer); | ||
326 | kfree(dev->interrupt_in_buffer); | ||
327 | kfree(dev->interrupt_out_buffer); | ||
328 | kfree(dev); | ||
329 | } | ||
330 | |||
331 | /** | ||
332 | * usb_tranzport_interrupt_in_callback | ||
333 | */ | ||
334 | |||
335 | static void usb_tranzport_interrupt_in_callback(struct urb *urb) | ||
336 | { | ||
337 | struct usb_tranzport *dev = urb->context; | ||
338 | unsigned int next_ring_head; | ||
339 | int retval = -1; | ||
340 | |||
341 | if (urb->status) { | ||
342 | if (urb->status == -ENOENT || | ||
343 | urb->status == -ECONNRESET || | ||
344 | urb->status == -ESHUTDOWN) { | ||
345 | goto exit; | ||
346 | } else { | ||
347 | dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n", | ||
348 | __FUNCTION__, urb->status); | ||
349 | goto resubmit; /* maybe we can recover */ | ||
350 | } | ||
351 | } | ||
352 | |||
353 | if (urb->actual_length != 8) { | ||
354 | dev_warn(&dev->intf->dev, | ||
355 | "Urb length was %d bytes!! Do something intelligent \n", urb->actual_length); | ||
356 | } else { | ||
357 | dbg_info(&dev->intf->dev, "%s: received: %02x%02x%02x%02x%02x%02x%02x%02x\n", | ||
358 | __FUNCTION__, dev->interrupt_in_buffer[0],dev->interrupt_in_buffer[1],dev->interrupt_in_buffer[2],dev->interrupt_in_buffer[3],dev->interrupt_in_buffer[4],dev->interrupt_in_buffer[5],dev->interrupt_in_buffer[6],dev->interrupt_in_buffer[7]); | ||
359 | #if SUPPRESS_EXTRA_OFFLINE_EVENTS | ||
360 | if(dev->offline == 2 && dev->interrupt_in_buffer[1] == 0xff) { goto resubmit; } | ||
361 | if(dev->offline == 1 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 2; goto resubmit; } | ||
362 | |||
363 | /* Always pass one offline event up the stack */ | ||
364 | if(dev->offline > 0 && dev->interrupt_in_buffer[1] != 0xff) { dev->offline = 0; } | ||
365 | if(dev->offline == 0 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 1; } | ||
366 | |||
367 | #endif | ||
368 | dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); | ||
369 | |||
370 | next_ring_head = (dev->ring_head+1) % ring_buffer_size; | ||
371 | |||
372 | if (next_ring_head != dev->ring_tail) { | ||
373 | memcpy(&((*dev->ring_buffer)[dev->ring_head]), dev->interrupt_in_buffer, urb->actual_length); | ||
374 | dev->ring_head = next_ring_head; | ||
375 | retval = 0; | ||
376 | memset(dev->interrupt_in_buffer, 0, urb->actual_length); | ||
377 | } else { | ||
378 | dev_warn(&dev->intf->dev, | ||
379 | "Ring buffer overflow, %d bytes dropped\n", | ||
380 | urb->actual_length); | ||
381 | memset(dev->interrupt_in_buffer, 0, urb->actual_length); | ||
382 | } | ||
383 | } | ||
384 | |||
385 | resubmit: | ||
386 | /* resubmit if we're still running */ | ||
387 | if (dev->interrupt_in_running && dev->intf) { | ||
388 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); | ||
389 | if (retval) | ||
390 | dev_err(&dev->intf->dev, | ||
391 | "usb_submit_urb failed (%d)\n", retval); | ||
392 | } | ||
393 | |||
394 | exit: | ||
395 | dev->interrupt_in_done = 1; | ||
396 | wake_up_interruptible(&dev->read_wait); | ||
397 | } | ||
398 | |||
399 | /** | ||
400 | * usb_tranzport_interrupt_out_callback | ||
401 | */ | ||
402 | static void usb_tranzport_interrupt_out_callback(struct urb *urb) | ||
403 | { | ||
404 | struct usb_tranzport *dev = urb->context; | ||
405 | |||
406 | /* sync/async unlink faults aren't errors */ | ||
407 | if (urb->status && !(urb->status == -ENOENT || | ||
408 | urb->status == -ECONNRESET || | ||
409 | urb->status == -ESHUTDOWN)) | ||
410 | dbg_info(&dev->intf->dev, | ||
411 | "%s - nonzero write interrupt status received: %d\n", | ||
412 | __FUNCTION__, urb->status); | ||
413 | |||
414 | dev->interrupt_out_busy = 0; | ||
415 | wake_up_interruptible(&dev->write_wait); | ||
416 | } | ||
417 | |||
418 | /** | ||
419 | * usb_tranzport_open | ||
420 | */ | ||
421 | static int usb_tranzport_open(struct inode *inode, struct file *file) | ||
422 | { | ||
423 | struct usb_tranzport *dev; | ||
424 | int subminor; | ||
425 | int retval = 0; | ||
426 | struct usb_interface *interface; | ||
427 | |||
428 | nonseekable_open(inode, file); | ||
429 | subminor = iminor(inode); | ||
430 | |||
431 | mutex_lock(&disconnect_mutex); | ||
432 | |||
433 | interface = usb_find_interface(&usb_tranzport_driver, subminor); | ||
434 | |||
435 | if (!interface) { | ||
436 | err("%s - error, can't find device for minor %d\n", | ||
437 | __FUNCTION__, subminor); | ||
438 | retval = -ENODEV; | ||
439 | goto unlock_disconnect_exit; | ||
440 | } | ||
441 | |||
442 | dev = usb_get_intfdata(interface); | ||
443 | |||
444 | if (!dev) { | ||
445 | retval = -ENODEV; | ||
446 | goto unlock_disconnect_exit; | ||
447 | } | ||
448 | |||
449 | /* lock this device */ | ||
450 | if (down_interruptible(&dev->sem)) { | ||
451 | retval = -ERESTARTSYS; | ||
452 | goto unlock_disconnect_exit; | ||
453 | } | ||
454 | |||
455 | /* allow opening only once */ | ||
456 | if (dev->open_count) { | ||
457 | retval = -EBUSY; | ||
458 | goto unlock_exit; | ||
459 | } | ||
460 | dev->open_count = 1; | ||
461 | |||
462 | /* initialize in direction */ | ||
463 | dev->ring_head = 0; | ||
464 | dev->ring_tail = 0; | ||
465 | usb_fill_int_urb(dev->interrupt_in_urb, | ||
466 | interface_to_usbdev(interface), | ||
467 | usb_rcvintpipe(interface_to_usbdev(interface), | ||
468 | dev->interrupt_in_endpoint->bEndpointAddress), | ||
469 | dev->interrupt_in_buffer, | ||
470 | dev->interrupt_in_endpoint_size, | ||
471 | usb_tranzport_interrupt_in_callback, | ||
472 | dev, | ||
473 | dev->interrupt_in_interval); | ||
474 | |||
475 | dev->interrupt_in_running = 1; | ||
476 | dev->interrupt_in_done = 0; | ||
477 | dev->enable = 1; | ||
478 | dev->offline = 0; | ||
479 | dev->compress_wheel = 1; | ||
480 | |||
481 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); | ||
482 | if (retval) { | ||
483 | dev_err(&interface->dev, "Couldn't submit interrupt_in_urb %d\n", retval); | ||
484 | dev->interrupt_in_running = 0; | ||
485 | dev->open_count = 0; | ||
486 | goto unlock_exit; | ||
487 | } | ||
488 | |||
489 | /* save device in the file's private structure */ | ||
490 | file->private_data = dev; | ||
491 | |||
492 | |||
493 | unlock_exit: | ||
494 | up(&dev->sem); | ||
495 | |||
496 | unlock_disconnect_exit: | ||
497 | mutex_unlock(&disconnect_mutex); | ||
498 | |||
499 | return retval; | ||
500 | } | ||
501 | |||
502 | /** | ||
503 | * usb_tranzport_release | ||
504 | */ | ||
505 | static int usb_tranzport_release(struct inode *inode, struct file *file) | ||
506 | { | ||
507 | struct usb_tranzport *dev; | ||
508 | int retval = 0; | ||
509 | |||
510 | dev = file->private_data; | ||
511 | |||
512 | if (dev == NULL) { | ||
513 | retval = -ENODEV; | ||
514 | goto exit; | ||
515 | } | ||
516 | |||
517 | if (down_interruptible(&dev->sem)) { | ||
518 | retval = -ERESTARTSYS; | ||
519 | goto exit; | ||
520 | } | ||
521 | |||
522 | if (dev->open_count != 1) { | ||
523 | retval = -ENODEV; | ||
524 | goto unlock_exit; | ||
525 | } | ||
526 | |||
527 | if (dev->intf == NULL) { | ||
528 | /* the device was unplugged before the file was released */ | ||
529 | up(&dev->sem); | ||
530 | /* unlock here as usb_tranzport_delete frees dev */ | ||
531 | usb_tranzport_delete(dev); | ||
532 | retval = -ENODEV; | ||
533 | goto exit; | ||
534 | } | ||
535 | |||
536 | /* wait until write transfer is finished */ | ||
537 | if (dev->interrupt_out_busy) | ||
538 | wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy, 2 * HZ); | ||
539 | usb_tranzport_abort_transfers(dev); | ||
540 | dev->open_count = 0; | ||
541 | |||
542 | unlock_exit: | ||
543 | up(&dev->sem); | ||
544 | |||
545 | exit: | ||
546 | return retval; | ||
547 | } | ||
548 | |||
549 | /** | ||
550 | * usb_tranzport_poll | ||
551 | */ | ||
552 | static unsigned int usb_tranzport_poll(struct file *file, poll_table *wait) | ||
553 | { | ||
554 | struct usb_tranzport *dev; | ||
555 | unsigned int mask = 0; | ||
556 | |||
557 | dev = file->private_data; | ||
558 | |||
559 | poll_wait(file, &dev->read_wait, wait); | ||
560 | poll_wait(file, &dev->write_wait, wait); | ||
561 | |||
562 | if (dev->ring_head != dev->ring_tail) | ||
563 | mask |= POLLIN | POLLRDNORM; | ||
564 | if (!dev->interrupt_out_busy) | ||
565 | mask |= POLLOUT | POLLWRNORM; | ||
566 | |||
567 | return mask; | ||
568 | } | ||
569 | |||
570 | /** | ||
571 | * usb_tranzport_read | ||
572 | */ | ||
573 | static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t count, | ||
574 | loff_t *ppos) | ||
575 | { | ||
576 | struct usb_tranzport *dev; | ||
577 | size_t bytes_to_read; | ||
578 | int retval = 0; | ||
579 | |||
580 | #if BUFFERED_READS | ||
581 | int c = 0; | ||
582 | #endif | ||
583 | |||
584 | #if COMPRESS_WHEEL_EVENTS | ||
585 | signed char oldwheel; | ||
586 | signed char newwheel; | ||
587 | int cancompress = 1; | ||
588 | int next_tail; | ||
589 | #endif | ||
590 | |||
591 | /* do I have such a thing as a null event? */ | ||
592 | |||
593 | dev = file->private_data; | ||
594 | |||
595 | /* verify that we actually have some data to read */ | ||
596 | if (count == 0) | ||
597 | goto exit; | ||
598 | |||
599 | /* lock this object */ | ||
600 | if (down_interruptible(&dev->sem)) { | ||
601 | retval = -ERESTARTSYS; | ||
602 | goto exit; | ||
603 | } | ||
604 | |||
605 | /* verify that the device wasn't unplugged */ | ||
606 | if (dev->intf == NULL) { | ||
607 | retval = -ENODEV; | ||
608 | err("No device or device unplugged %d\n", retval); | ||
609 | goto unlock_exit; | ||
610 | } | ||
611 | |||
612 | while (dev->ring_head == dev->ring_tail) { | ||
613 | |||
614 | if (file->f_flags & O_NONBLOCK) { | ||
615 | retval = -EAGAIN; | ||
616 | goto unlock_exit; | ||
617 | } | ||
618 | // atomic_cmp_exchange(&dev->interrupt_in_done,0,0); | ||
619 | dev->interrupt_in_done = 0 ; /* tiny race - FIXME: make atomic? */ | ||
620 | retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done); | ||
621 | if (retval < 0) { | ||
622 | goto unlock_exit; | ||
623 | } | ||
624 | } | ||
625 | |||
626 | dbg_info(&dev->intf->dev, "%s: copying to userspace: %02x%02x%02x%02x%02x%02x%02x%02x\n", | ||
627 | __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); | ||
628 | |||
629 | #if BUFFERED_READS | ||
630 | c = 0; | ||
631 | while((c < count) && (dev->ring_tail != dev->ring_head)) { | ||
632 | |||
633 | /* This started off in the lower level service routine, and I moved it here. Then my brain died. Not done yet. */ | ||
634 | #if COMPRESS_WHEEL_EVENTS | ||
635 | next_tail = (dev->ring_tail+1) % ring_buffer_size; | ||
636 | if(dev->compress_wheel) cancompress = 1; | ||
637 | while(dev->ring_head != next_tail && cancompress == 1 ) { | ||
638 | newwheel = (*dev->ring_buffer)[next_tail].cmd[6]; | ||
639 | oldwheel = (*dev->ring_buffer)[dev->ring_tail].cmd[6]; | ||
640 | // if both are wheel events, and no buttons have changes (FIXME, do I have to check?), | ||
641 | // and we are the same sign, we can compress +- 7F | ||
642 | // FIXME: saner check for overflow! - max of +- 7F | ||
643 | // FIXME the math is wrong for going in reverse, actually, as the midi spec doesn't allow signed chars | ||
644 | |||
645 | dbg_info(&dev->intf->dev, "%s: trying to compress: %02x%02x%02x%02x%02x %02x %02x %02x\n", | ||
646 | __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); | ||
647 | |||
648 | |||
649 | if(((*dev->ring_buffer)[dev->ring_tail].cmd[6] != 0 && | ||
650 | (*dev->ring_buffer)[next_tail].cmd[6] != 0 ) && | ||
651 | ((newwheel > 0 && oldwheel > 0) || | ||
652 | (newwheel < 0 && oldwheel < 0)) && | ||
653 | ((*dev->ring_buffer)[dev->ring_tail].cmd[2] == (*dev->ring_buffer)[next_tail].cmd[2]) && | ||
654 | ((*dev->ring_buffer)[dev->ring_tail].cmd[3] == (*dev->ring_buffer)[next_tail].cmd[3]) && | ||
655 | ((*dev->ring_buffer)[dev->ring_tail].cmd[4] == (*dev->ring_buffer)[next_tail].cmd[4]) && | ||
656 | ((*dev->ring_buffer)[dev->ring_tail].cmd[5] == (*dev->ring_buffer)[next_tail].cmd[5])) | ||
657 | { | ||
658 | dbg_info(&dev->intf->dev, "%s: should compress: %02x%02x%02x%02x%02x%02x%02x%02x\n", | ||
659 | __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); | ||
660 | |||
661 | newwheel += oldwheel; | ||
662 | if(oldwheel > 0 && !(newwheel > 0)) { | ||
663 | newwheel = 0x7f; | ||
664 | cancompress = 0; | ||
665 | } | ||
666 | if(oldwheel < 0 && !(newwheel < 0)) { | ||
667 | newwheel = 0x80; | ||
668 | cancompress = 0; | ||
669 | } | ||
670 | |||
671 | (*dev->ring_buffer)[next_tail].cmd[6] = newwheel; | ||
672 | dev->ring_tail = next_tail; | ||
673 | next_tail = (dev->ring_tail+1) % ring_buffer_size; | ||
674 | } else { | ||
675 | cancompress = 0; | ||
676 | } | ||
677 | } | ||
678 | #endif /* COMPRESS_WHEEL_EVENTS */ | ||
679 | |||
680 | if (copy_to_user(&buffer[c], &(*dev->ring_buffer)[dev->ring_tail], 8)) { | ||
681 | retval = -EFAULT; | ||
682 | goto unlock_exit; | ||
683 | } | ||
684 | |||
685 | dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; | ||
686 | c+=8; | ||
687 | dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); | ||
688 | } | ||
689 | retval = c; | ||
690 | |||
691 | #else | ||
692 | if (copy_to_user(buffer, &(*dev->ring_buffer)[dev->ring_tail], 8)) { | ||
693 | retval = -EFAULT; | ||
694 | goto unlock_exit; | ||
695 | } | ||
696 | |||
697 | dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; | ||
698 | dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); | ||
699 | |||
700 | retval = 8; | ||
701 | #endif /* BUFFERED_READS */ | ||
702 | |||
703 | unlock_exit: | ||
704 | /* unlock the device */ | ||
705 | up(&dev->sem); | ||
706 | |||
707 | exit: | ||
708 | return retval; | ||
709 | } | ||
710 | |||
711 | /** | ||
712 | * usb_tranzport_write | ||
713 | */ | ||
714 | static ssize_t usb_tranzport_write(struct file *file, const char __user *buffer, | ||
715 | size_t count, loff_t *ppos) | ||
716 | { | ||
717 | struct usb_tranzport *dev; | ||
718 | size_t bytes_to_write; | ||
719 | int retval = 0; | ||
720 | |||
721 | dev = file->private_data; | ||
722 | |||
723 | /* verify that we actually have some data to write */ | ||
724 | if (count == 0) | ||
725 | goto exit; | ||
726 | |||
727 | /* lock this object */ | ||
728 | if (down_interruptible(&dev->sem)) { | ||
729 | retval = -ERESTARTSYS; | ||
730 | goto exit; | ||
731 | } | ||
732 | |||
733 | /* verify that the device wasn't unplugged */ | ||
734 | if (dev->intf == NULL) { | ||
735 | retval = -ENODEV; | ||
736 | err("No device or device unplugged %d\n", retval); | ||
737 | goto unlock_exit; | ||
738 | } | ||
739 | |||
740 | /* wait until previous transfer is finished */ | ||
741 | if (dev->interrupt_out_busy) { | ||
742 | if (file->f_flags & O_NONBLOCK) { | ||
743 | retval = -EAGAIN; | ||
744 | goto unlock_exit; | ||
745 | } | ||
746 | retval = wait_event_interruptible(dev->write_wait, !dev->interrupt_out_busy); | ||
747 | if (retval < 0) { | ||
748 | goto unlock_exit; | ||
749 | } | ||
750 | } | ||
751 | |||
752 | /* write the data into interrupt_out_buffer from userspace */ | ||
753 | bytes_to_write = min(count, write_buffer_size*dev->interrupt_out_endpoint_size); | ||
754 | if (bytes_to_write < count) | ||
755 | dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write); | ||
756 | |||
757 | dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __FUNCTION__, count, bytes_to_write); | ||
758 | |||
759 | if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) { | ||
760 | retval = -EFAULT; | ||
761 | goto unlock_exit; | ||
762 | } | ||
763 | |||
764 | if (dev->interrupt_out_endpoint == NULL) { | ||
765 | err("Endpoint should not be be null! \n"); | ||
766 | goto unlock_exit; | ||
767 | } | ||
768 | |||
769 | /* send off the urb */ | ||
770 | usb_fill_int_urb(dev->interrupt_out_urb, | ||
771 | interface_to_usbdev(dev->intf), | ||
772 | usb_sndintpipe(interface_to_usbdev(dev->intf), | ||
773 | dev->interrupt_out_endpoint->bEndpointAddress), | ||
774 | dev->interrupt_out_buffer, | ||
775 | bytes_to_write, | ||
776 | usb_tranzport_interrupt_out_callback, | ||
777 | dev, | ||
778 | dev->interrupt_out_interval); | ||
779 | |||
780 | dev->interrupt_out_busy = 1; | ||
781 | wmb(); | ||
782 | |||
783 | retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL); | ||
784 | if (retval) { | ||
785 | dev->interrupt_out_busy = 0; | ||
786 | err("Couldn't submit interrupt_out_urb %d\n", retval); | ||
787 | goto unlock_exit; | ||
788 | } | ||
789 | retval = bytes_to_write; | ||
790 | |||
791 | unlock_exit: | ||
792 | /* unlock the device */ | ||
793 | up(&dev->sem); | ||
794 | |||
795 | exit: | ||
796 | return retval; | ||
797 | } | ||
798 | |||
799 | /* file operations needed when we register this driver */ | ||
800 | static const struct file_operations usb_tranzport_fops = { | ||
801 | .owner = THIS_MODULE, | ||
802 | .read = usb_tranzport_read, | ||
803 | .write = usb_tranzport_write, | ||
804 | .open = usb_tranzport_open, | ||
805 | .release = usb_tranzport_release, | ||
806 | .poll = usb_tranzport_poll, | ||
807 | }; | ||
808 | |||
809 | /* | ||
810 | * usb class driver info in order to get a minor number from the usb core, | ||
811 | * and to have the device registered with the driver core | ||
812 | */ | ||
813 | static struct usb_class_driver usb_tranzport_class = { | ||
814 | .name = "tranzport%d", | ||
815 | .fops = &usb_tranzport_fops, | ||
816 | .minor_base = USB_TRANZPORT_MINOR_BASE, | ||
817 | }; | ||
818 | |||
819 | |||
820 | /** | ||
821 | * usb_tranzport_probe | ||
822 | * | ||
823 | * Called by the usb core when a new device is connected that it thinks | ||
824 | * this driver might be interested in. | ||
825 | */ | ||
826 | static int usb_tranzport_probe(struct usb_interface *intf, const struct usb_device_id *id) | ||
827 | { | ||
828 | struct usb_device *udev = interface_to_usbdev(intf); | ||
829 | struct usb_tranzport *dev = NULL; | ||
830 | struct usb_host_interface *iface_desc; | ||
831 | struct usb_endpoint_descriptor *endpoint; | ||
832 | int i; | ||
833 | int true_size; | ||
834 | int retval = -ENOMEM; | ||
835 | |||
836 | /* allocate memory for our device state and intialize it */ | ||
837 | |||
838 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
839 | if (dev == NULL) { | ||
840 | dev_err(&intf->dev, "Out of memory\n"); | ||
841 | goto exit; | ||
842 | } | ||
843 | init_MUTEX(&dev->sem); | ||
844 | dev->intf = intf; | ||
845 | init_waitqueue_head(&dev->read_wait); | ||
846 | init_waitqueue_head(&dev->write_wait); | ||
847 | |||
848 | iface_desc = intf->cur_altsetting; | ||
849 | |||
850 | /* set up the endpoint information */ | ||
851 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | ||
852 | endpoint = &iface_desc->endpoint[i].desc; | ||
853 | |||
854 | if (usb_endpoint_is_int_in(endpoint)) | ||
855 | dev->interrupt_in_endpoint = endpoint; | ||
856 | |||
857 | if (usb_endpoint_is_int_out(endpoint)) | ||
858 | dev->interrupt_out_endpoint = endpoint; | ||
859 | } | ||
860 | if (dev->interrupt_in_endpoint == NULL) { | ||
861 | dev_err(&intf->dev, "Interrupt in endpoint not found\n"); | ||
862 | goto error; | ||
863 | } | ||
864 | if (dev->interrupt_out_endpoint == NULL) | ||
865 | dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n"); | ||
866 | |||
867 | |||
868 | dev->interrupt_in_endpoint_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize); | ||
869 | |||
870 | if (dev->interrupt_in_endpoint_size != 8) | ||
871 | dev_warn(&intf->dev, "Interrupt in endpoint size is not 8!\n"); | ||
872 | |||
873 | if(ring_buffer_size == 0) { ring_buffer_size = RING_BUFFER_SIZE; } | ||
874 | true_size = min(ring_buffer_size,RING_BUFFER_SIZE); | ||
875 | /* FIXME - there are more usb_alloc routines for dma correctness. Needed? */ | ||
876 | |||
877 | dev->ring_buffer = kmalloc((true_size*sizeof(struct tranzport_cmd))+8, GFP_KERNEL); | ||
878 | |||
879 | if (!dev->ring_buffer) { | ||
880 | dev_err(&intf->dev, "Couldn't allocate ring_buffer of size %d\n",true_size); | ||
881 | goto error; | ||
882 | } | ||
883 | dev->interrupt_in_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL); | ||
884 | if (!dev->interrupt_in_buffer) { | ||
885 | dev_err(&intf->dev, "Couldn't allocate interrupt_in_buffer\n"); | ||
886 | goto error; | ||
887 | } | ||
888 | dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
889 | if (!dev->interrupt_in_urb) { | ||
890 | dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n"); | ||
891 | goto error; | ||
892 | } | ||
893 | dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize) : | ||
894 | udev->descriptor.bMaxPacketSize0; | ||
895 | |||
896 | if (dev->interrupt_out_endpoint_size !=8) | ||
897 | dev_warn(&intf->dev, "Interrupt out endpoint size is not 8!)\n"); | ||
898 | |||
899 | dev->interrupt_out_buffer = kmalloc(write_buffer_size*dev->interrupt_out_endpoint_size, GFP_KERNEL); | ||
900 | if (!dev->interrupt_out_buffer) { | ||
901 | dev_err(&intf->dev, "Couldn't allocate interrupt_out_buffer\n"); | ||
902 | goto error; | ||
903 | } | ||
904 | dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
905 | if (!dev->interrupt_out_urb) { | ||
906 | dev_err(&intf->dev, "Couldn't allocate interrupt_out_urb\n"); | ||
907 | goto error; | ||
908 | } | ||
909 | dev->interrupt_in_interval = min_interrupt_in_interval > dev->interrupt_in_endpoint->bInterval ? min_interrupt_in_interval : dev->interrupt_in_endpoint->bInterval; | ||
910 | if (dev->interrupt_out_endpoint) | ||
911 | dev->interrupt_out_interval = min_interrupt_out_interval > dev->interrupt_out_endpoint->bInterval ? min_interrupt_out_interval : dev->interrupt_out_endpoint->bInterval; | ||
912 | |||
913 | /* we can register the device now, as it is ready */ | ||
914 | usb_set_intfdata(intf, dev); | ||
915 | |||
916 | retval = usb_register_dev(intf, &usb_tranzport_class); | ||
917 | if (retval) { | ||
918 | /* something prevented us from registering this driver */ | ||
919 | dev_err(&intf->dev, "Not able to get a minor for this device.\n"); | ||
920 | usb_set_intfdata(intf, NULL); | ||
921 | goto error; | ||
922 | } | ||
923 | |||
924 | if((retval = device_create_file(&intf->dev, &dev_attr_LightRecord))) goto error; | ||
925 | if((retval = device_create_file(&intf->dev, &dev_attr_LightTrackrec))) goto error; | ||
926 | if((retval = device_create_file(&intf->dev, &dev_attr_LightTrackmute))) goto error; | ||
927 | if((retval = device_create_file(&intf->dev, &dev_attr_LightTracksolo))) goto error; | ||
928 | if((retval = device_create_file(&intf->dev, &dev_attr_LightAnysolo))) goto error; | ||
929 | if((retval = device_create_file(&intf->dev, &dev_attr_LightLoop))) goto error; | ||
930 | if((retval = device_create_file(&intf->dev, &dev_attr_LightPunch))) goto error; | ||
931 | if((retval = device_create_file(&intf->dev, &dev_attr_wheel))) goto error; | ||
932 | if((retval = device_create_file(&intf->dev, &dev_attr_event))) goto error; | ||
933 | if((retval = device_create_file(&intf->dev, &dev_attr_dump_state))) goto error; | ||
934 | if((retval = device_create_file(&intf->dev, &dev_attr_compress_wheel))) goto error; | ||
935 | if((retval = device_create_file(&intf->dev, &dev_attr_enable))) goto error; | ||
936 | if((retval = device_create_file(&intf->dev, &dev_attr_offline))) goto error; | ||
937 | |||
938 | /* let the user know what node this device is now attached to */ | ||
939 | dev_info(&intf->dev, "Tranzport Device #%d now attached to major %d minor %d\n", | ||
940 | (intf->minor - USB_TRANZPORT_MINOR_BASE), USB_MAJOR, intf->minor); | ||
941 | |||
942 | exit: | ||
943 | return retval; | ||
944 | |||
945 | error: | ||
946 | usb_tranzport_delete(dev); | ||
947 | |||
948 | return retval; | ||
949 | } | ||
950 | |||
951 | /** | ||
952 | * usb_tranzport_disconnect | ||
953 | * | ||
954 | * Called by the usb core when the device is removed from the system. | ||
955 | */ | ||
956 | static void usb_tranzport_disconnect(struct usb_interface *intf) | ||
957 | { | ||
958 | struct usb_tranzport *dev; | ||
959 | int minor; | ||
960 | mutex_lock(&disconnect_mutex); | ||
961 | dev = usb_get_intfdata(intf); | ||
962 | usb_set_intfdata(intf, NULL); | ||
963 | down(&dev->sem); | ||
964 | minor = intf->minor; | ||
965 | /* give back our minor */ | ||
966 | usb_deregister_dev(intf, &usb_tranzport_class); | ||
967 | |||
968 | /* if the device is not opened, then we clean up right now */ | ||
969 | if (!dev->open_count) { | ||
970 | up(&dev->sem); | ||
971 | usb_tranzport_delete(dev); | ||
972 | } else { | ||
973 | dev->intf = NULL; | ||
974 | up(&dev->sem); | ||
975 | } | ||
976 | |||
977 | mutex_unlock(&disconnect_mutex); | ||
978 | |||
979 | dev_info(&intf->dev, "Tranzport Surface #%d now disconnected\n", | ||
980 | (minor - USB_TRANZPORT_MINOR_BASE)); | ||
981 | } | ||
982 | |||
983 | /* usb specific object needed to register this driver with the usb subsystem */ | ||
984 | static struct usb_driver usb_tranzport_driver = { | ||
985 | .name = "tranzport", | ||
986 | .probe = usb_tranzport_probe, | ||
987 | .disconnect = usb_tranzport_disconnect, | ||
988 | .id_table = usb_tranzport_table, | ||
989 | }; | ||
990 | |||
991 | /** | ||
992 | * usb_tranzport_init | ||
993 | */ | ||
994 | static int __init usb_tranzport_init(void) | ||
995 | { | ||
996 | int retval; | ||
997 | |||
998 | /* register this driver with the USB subsystem */ | ||
999 | retval = usb_register(&usb_tranzport_driver); | ||
1000 | if (retval) | ||
1001 | err("usb_register failed for the "__FILE__" driver. Error number %d\n", retval); | ||
1002 | |||
1003 | return retval; | ||
1004 | } | ||
1005 | |||
1006 | /** | ||
1007 | * usb_tranzport_exit | ||
1008 | */ | ||
1009 | static void __exit usb_tranzport_exit(void) | ||
1010 | { | ||
1011 | /* deregister this driver with the USB subsystem */ | ||
1012 | usb_deregister(&usb_tranzport_driver); | ||
1013 | } | ||
1014 | |||
1015 | module_init(usb_tranzport_init); | ||
1016 | module_exit(usb_tranzport_exit); | ||
1017 | |||