Commit | Line | Data |
---|---|---|
03270634 SH |
1 | /* |
2 | * adutux - driver for ADU devices from Ontrak Control Systems | |
3 | * This is an experimental driver. Use at your own risk. | |
4 | * This driver is not supported by Ontrak Control Systems. | |
5 | * | |
6 | * Copyright (c) 2003 John Homppi (SCO, leave this notice here) | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License as | |
10 | * published by the Free Software Foundation; either version 2 of | |
11 | * the License, or (at your option) any later version. | |
12 | * | |
13 | * derived from the Lego USB Tower driver 0.56: | |
14 | * Copyright (c) 2003 David Glance <davidgsf@sourceforge.net> | |
15 | * 2001 Juergen Stuber <stuber@loria.fr> | |
16 | * that was derived from USB Skeleton driver - 0.5 | |
17 | * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) | |
18 | * | |
19 | */ | |
20 | ||
21 | #include <linux/kernel.h> | |
22 | #include <linux/errno.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/slab.h> | |
25 | #include <linux/module.h> | |
26 | #include <linux/usb.h> | |
8293c568 | 27 | #include <linux/mutex.h> |
03270634 SH |
28 | #include <asm/uaccess.h> |
29 | ||
30 | #ifdef CONFIG_USB_DEBUG | |
31 | static int debug = 5; | |
32 | #else | |
33 | static int debug = 1; | |
34 | #endif | |
35 | ||
36 | /* Use our own dbg macro */ | |
37 | #undef dbg | |
38 | #define dbg(lvl, format, arg...) \ | |
39 | do { \ | |
40 | if (debug >= lvl) \ | |
f45ba776 | 41 | printk(KERN_DEBUG "%s: " format "\n", __FILE__, ##arg); \ |
03270634 SH |
42 | } while (0) |
43 | ||
44 | ||
45 | /* Version Information */ | |
46 | #define DRIVER_VERSION "v0.0.13" | |
47 | #define DRIVER_AUTHOR "John Homppi" | |
48 | #define DRIVER_DESC "adutux (see www.ontrak.net)" | |
49 | ||
50 | /* Module parameters */ | |
51 | module_param(debug, int, S_IRUGO | S_IWUSR); | |
52 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | |
53 | ||
54 | /* Define these values to match your device */ | |
55 | #define ADU_VENDOR_ID 0x0a07 | |
56 | #define ADU_PRODUCT_ID 0x0064 | |
57 | ||
58 | /* table of devices that work with this driver */ | |
33b9e162 | 59 | static const struct usb_device_id device_table[] = { |
03270634 SH |
60 | { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID) }, /* ADU100 */ |
61 | { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+20) }, /* ADU120 */ | |
62 | { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+30) }, /* ADU130 */ | |
63 | { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+100) }, /* ADU200 */ | |
64 | { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+108) }, /* ADU208 */ | |
65 | { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+118) }, /* ADU218 */ | |
66 | { }/* Terminating entry */ | |
67 | }; | |
68 | ||
69 | MODULE_DEVICE_TABLE(usb, device_table); | |
70 | ||
71 | #ifdef CONFIG_USB_DYNAMIC_MINORS | |
72 | #define ADU_MINOR_BASE 0 | |
73 | #else | |
74 | #define ADU_MINOR_BASE 67 | |
75 | #endif | |
76 | ||
77 | /* we can have up to this number of device plugged in at once */ | |
78 | #define MAX_DEVICES 16 | |
79 | ||
80 | #define COMMAND_TIMEOUT (2*HZ) /* 60 second timeout for a command */ | |
81 | ||
f08812d5 PZ |
82 | /* |
83 | * The locking scheme is a vanilla 3-lock: | |
84 | * adu_device.buflock: A spinlock, covers what IRQs touch. | |
85 | * adutux_mutex: A Static lock to cover open_count. It would also cover | |
86 | * any globals, but we don't have them in 2.6. | |
87 | * adu_device.mtx: A mutex to hold across sleepers like copy_from_user. | |
88 | * It covers all of adu_device, except the open_count | |
89 | * and what .buflock covers. | |
90 | */ | |
91 | ||
03270634 SH |
92 | /* Structure to hold all of our device specific stuff */ |
93 | struct adu_device { | |
f08812d5 | 94 | struct mutex mtx; |
03270634 SH |
95 | struct usb_device* udev; /* save off the usb device pointer */ |
96 | struct usb_interface* interface; | |
f08812d5 | 97 | unsigned int minor; /* the starting minor number for this device */ |
03270634 SH |
98 | char serial_number[8]; |
99 | ||
100 | int open_count; /* number of times this port has been opened */ | |
101 | ||
102 | char* read_buffer_primary; | |
103 | int read_buffer_length; | |
104 | char* read_buffer_secondary; | |
105 | int secondary_head; | |
106 | int secondary_tail; | |
107 | spinlock_t buflock; | |
108 | ||
109 | wait_queue_head_t read_wait; | |
110 | wait_queue_head_t write_wait; | |
111 | ||
112 | char* interrupt_in_buffer; | |
113 | struct usb_endpoint_descriptor* interrupt_in_endpoint; | |
114 | struct urb* interrupt_in_urb; | |
115 | int read_urb_finished; | |
116 | ||
117 | char* interrupt_out_buffer; | |
118 | struct usb_endpoint_descriptor* interrupt_out_endpoint; | |
119 | struct urb* interrupt_out_urb; | |
f08812d5 | 120 | int out_urb_finished; |
03270634 SH |
121 | }; |
122 | ||
f08812d5 PZ |
123 | static DEFINE_MUTEX(adutux_mutex); |
124 | ||
03270634 SH |
125 | static struct usb_driver adu_driver; |
126 | ||
127 | static void adu_debug_data(int level, const char *function, int size, | |
128 | const unsigned char *data) | |
129 | { | |
130 | int i; | |
131 | ||
132 | if (debug < level) | |
133 | return; | |
134 | ||
f45ba776 JP |
135 | printk(KERN_DEBUG "%s: %s - length = %d, data = ", |
136 | __FILE__, function, size); | |
03270634 SH |
137 | for (i = 0; i < size; ++i) |
138 | printk("%.2x ", data[i]); | |
139 | printk("\n"); | |
140 | } | |
141 | ||
142 | /** | |
143 | * adu_abort_transfers | |
144 | * aborts transfers and frees associated data structures | |
145 | */ | |
146 | static void adu_abort_transfers(struct adu_device *dev) | |
147 | { | |
f08812d5 | 148 | unsigned long flags; |
03270634 | 149 | |
441b62c1 | 150 | dbg(2," %s : enter", __func__); |
03270634 SH |
151 | |
152 | if (dev->udev == NULL) { | |
441b62c1 | 153 | dbg(1," %s : udev is null", __func__); |
03270634 SH |
154 | goto exit; |
155 | } | |
156 | ||
03270634 | 157 | /* shutdown transfer */ |
f08812d5 PZ |
158 | |
159 | /* XXX Anchor these instead */ | |
160 | spin_lock_irqsave(&dev->buflock, flags); | |
161 | if (!dev->read_urb_finished) { | |
162 | spin_unlock_irqrestore(&dev->buflock, flags); | |
163 | usb_kill_urb(dev->interrupt_in_urb); | |
164 | } else | |
165 | spin_unlock_irqrestore(&dev->buflock, flags); | |
166 | ||
167 | spin_lock_irqsave(&dev->buflock, flags); | |
168 | if (!dev->out_urb_finished) { | |
169 | spin_unlock_irqrestore(&dev->buflock, flags); | |
170 | usb_kill_urb(dev->interrupt_out_urb); | |
171 | } else | |
172 | spin_unlock_irqrestore(&dev->buflock, flags); | |
03270634 SH |
173 | |
174 | exit: | |
441b62c1 | 175 | dbg(2," %s : leave", __func__); |
03270634 SH |
176 | } |
177 | ||
178 | static void adu_delete(struct adu_device *dev) | |
179 | { | |
441b62c1 | 180 | dbg(2, "%s enter", __func__); |
03270634 | 181 | |
03270634 SH |
182 | /* free data structures */ |
183 | usb_free_urb(dev->interrupt_in_urb); | |
184 | usb_free_urb(dev->interrupt_out_urb); | |
185 | kfree(dev->read_buffer_primary); | |
186 | kfree(dev->read_buffer_secondary); | |
187 | kfree(dev->interrupt_in_buffer); | |
188 | kfree(dev->interrupt_out_buffer); | |
189 | kfree(dev); | |
190 | ||
441b62c1 | 191 | dbg(2, "%s : leave", __func__); |
03270634 SH |
192 | } |
193 | ||
7d12e780 | 194 | static void adu_interrupt_in_callback(struct urb *urb) |
03270634 SH |
195 | { |
196 | struct adu_device *dev = urb->context; | |
24497a00 | 197 | int status = urb->status; |
03270634 | 198 | |
441b62c1 HH |
199 | dbg(4," %s : enter, status %d", __func__, status); |
200 | adu_debug_data(5, __func__, urb->actual_length, | |
03270634 SH |
201 | urb->transfer_buffer); |
202 | ||
203 | spin_lock(&dev->buflock); | |
204 | ||
24497a00 | 205 | if (status != 0) { |
f6c1ceaa ON |
206 | if ((status != -ENOENT) && (status != -ECONNRESET) && |
207 | (status != -ESHUTDOWN)) { | |
03270634 | 208 | dbg(1," %s : nonzero status received: %d", |
441b62c1 | 209 | __func__, status); |
03270634 SH |
210 | } |
211 | goto exit; | |
212 | } | |
213 | ||
214 | if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) { | |
215 | if (dev->read_buffer_length < | |
29cc8897 | 216 | (4 * usb_endpoint_maxp(dev->interrupt_in_endpoint)) - |
03270634 SH |
217 | (urb->actual_length)) { |
218 | memcpy (dev->read_buffer_primary + | |
219 | dev->read_buffer_length, | |
220 | dev->interrupt_in_buffer, urb->actual_length); | |
221 | ||
222 | dev->read_buffer_length += urb->actual_length; | |
441b62c1 | 223 | dbg(2," %s reading %d ", __func__, |
03270634 SH |
224 | urb->actual_length); |
225 | } else { | |
441b62c1 | 226 | dbg(1," %s : read_buffer overflow", __func__); |
03270634 SH |
227 | } |
228 | } | |
229 | ||
230 | exit: | |
231 | dev->read_urb_finished = 1; | |
232 | spin_unlock(&dev->buflock); | |
233 | /* always wake up so we recover from errors */ | |
234 | wake_up_interruptible(&dev->read_wait); | |
441b62c1 | 235 | adu_debug_data(5, __func__, urb->actual_length, |
03270634 | 236 | urb->transfer_buffer); |
441b62c1 | 237 | dbg(4," %s : leave, status %d", __func__, status); |
03270634 SH |
238 | } |
239 | ||
7d12e780 | 240 | static void adu_interrupt_out_callback(struct urb *urb) |
03270634 SH |
241 | { |
242 | struct adu_device *dev = urb->context; | |
24497a00 | 243 | int status = urb->status; |
03270634 | 244 | |
441b62c1 HH |
245 | dbg(4," %s : enter, status %d", __func__, status); |
246 | adu_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer); | |
03270634 | 247 | |
24497a00 GKH |
248 | if (status != 0) { |
249 | if ((status != -ENOENT) && | |
250 | (status != -ECONNRESET)) { | |
03270634 | 251 | dbg(1, " %s :nonzero status received: %d", |
441b62c1 | 252 | __func__, status); |
03270634 SH |
253 | } |
254 | goto exit; | |
255 | } | |
256 | ||
f08812d5 PZ |
257 | spin_lock(&dev->buflock); |
258 | dev->out_urb_finished = 1; | |
259 | wake_up(&dev->write_wait); | |
260 | spin_unlock(&dev->buflock); | |
03270634 SH |
261 | exit: |
262 | ||
441b62c1 | 263 | adu_debug_data(5, __func__, urb->actual_length, |
03270634 | 264 | urb->transfer_buffer); |
441b62c1 | 265 | dbg(4," %s : leave, status %d", __func__, status); |
03270634 SH |
266 | } |
267 | ||
268 | static int adu_open(struct inode *inode, struct file *file) | |
269 | { | |
270 | struct adu_device *dev = NULL; | |
271 | struct usb_interface *interface; | |
272 | int subminor; | |
f08812d5 | 273 | int retval; |
03270634 | 274 | |
441b62c1 | 275 | dbg(2,"%s : enter", __func__); |
03270634 SH |
276 | |
277 | subminor = iminor(inode); | |
278 | ||
f08812d5 | 279 | if ((retval = mutex_lock_interruptible(&adutux_mutex))) { |
441b62c1 | 280 | dbg(2, "%s : mutex lock failed", __func__); |
f08812d5 PZ |
281 | goto exit_no_lock; |
282 | } | |
283 | ||
03270634 SH |
284 | interface = usb_find_interface(&adu_driver, subminor); |
285 | if (!interface) { | |
fd3f1917 GKH |
286 | printk(KERN_ERR "adutux: %s - error, can't find device for " |
287 | "minor %d\n", __func__, subminor); | |
03270634 SH |
288 | retval = -ENODEV; |
289 | goto exit_no_device; | |
290 | } | |
291 | ||
292 | dev = usb_get_intfdata(interface); | |
f08812d5 | 293 | if (!dev || !dev->udev) { |
03270634 SH |
294 | retval = -ENODEV; |
295 | goto exit_no_device; | |
296 | } | |
297 | ||
f08812d5 PZ |
298 | /* check that nobody else is using the device */ |
299 | if (dev->open_count) { | |
300 | retval = -EBUSY; | |
03270634 SH |
301 | goto exit_no_device; |
302 | } | |
303 | ||
03270634 | 304 | ++dev->open_count; |
441b62c1 | 305 | dbg(2,"%s : open count %d", __func__, dev->open_count); |
03270634 SH |
306 | |
307 | /* save device in the file's private structure */ | |
308 | file->private_data = dev; | |
309 | ||
f08812d5 PZ |
310 | /* initialize in direction */ |
311 | dev->read_buffer_length = 0; | |
03270634 | 312 | |
f08812d5 PZ |
313 | /* fixup first read by having urb waiting for it */ |
314 | usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, | |
315 | usb_rcvintpipe(dev->udev, | |
316 | dev->interrupt_in_endpoint->bEndpointAddress), | |
317 | dev->interrupt_in_buffer, | |
29cc8897 | 318 | usb_endpoint_maxp(dev->interrupt_in_endpoint), |
f08812d5 PZ |
319 | adu_interrupt_in_callback, dev, |
320 | dev->interrupt_in_endpoint->bInterval); | |
321 | dev->read_urb_finished = 0; | |
322 | if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL)) | |
323 | dev->read_urb_finished = 1; | |
324 | /* we ignore failure */ | |
325 | /* end of fixup for first read */ | |
326 | ||
327 | /* initialize out direction */ | |
328 | dev->out_urb_finished = 1; | |
329 | ||
330 | retval = 0; | |
03270634 SH |
331 | |
332 | exit_no_device: | |
f08812d5 PZ |
333 | mutex_unlock(&adutux_mutex); |
334 | exit_no_lock: | |
441b62c1 | 335 | dbg(2,"%s : leave, return value %d ", __func__, retval); |
03270634 SH |
336 | return retval; |
337 | } | |
338 | ||
f08812d5 | 339 | static void adu_release_internal(struct adu_device *dev) |
03270634 | 340 | { |
441b62c1 | 341 | dbg(2," %s : enter", __func__); |
03270634 | 342 | |
03270634 SH |
343 | /* decrement our usage count for the device */ |
344 | --dev->open_count; | |
441b62c1 | 345 | dbg(2," %s : open count %d", __func__, dev->open_count); |
03270634 SH |
346 | if (dev->open_count <= 0) { |
347 | adu_abort_transfers(dev); | |
348 | dev->open_count = 0; | |
349 | } | |
350 | ||
441b62c1 | 351 | dbg(2," %s : leave", __func__); |
03270634 SH |
352 | } |
353 | ||
354 | static int adu_release(struct inode *inode, struct file *file) | |
355 | { | |
f08812d5 | 356 | struct adu_device *dev; |
03270634 SH |
357 | int retval = 0; |
358 | ||
441b62c1 | 359 | dbg(2," %s : enter", __func__); |
03270634 SH |
360 | |
361 | if (file == NULL) { | |
441b62c1 | 362 | dbg(1," %s : file is NULL", __func__); |
03270634 SH |
363 | retval = -ENODEV; |
364 | goto exit; | |
365 | } | |
366 | ||
367 | dev = file->private_data; | |
03270634 | 368 | if (dev == NULL) { |
441b62c1 | 369 | dbg(1," %s : object is NULL", __func__); |
03270634 SH |
370 | retval = -ENODEV; |
371 | goto exit; | |
372 | } | |
373 | ||
f08812d5 | 374 | mutex_lock(&adutux_mutex); /* not interruptible */ |
03270634 SH |
375 | |
376 | if (dev->open_count <= 0) { | |
441b62c1 | 377 | dbg(1," %s : device not opened", __func__); |
03270634 | 378 | retval = -ENODEV; |
46c9844c | 379 | goto unlock; |
03270634 SH |
380 | } |
381 | ||
f08812d5 | 382 | adu_release_internal(dev); |
d4ead16f AS |
383 | if (dev->udev == NULL) { |
384 | /* the device was unplugged before the file was released */ | |
f08812d5 PZ |
385 | if (!dev->open_count) /* ... and we're the last user */ |
386 | adu_delete(dev); | |
d4ead16f | 387 | } |
46c9844c | 388 | unlock: |
f08812d5 | 389 | mutex_unlock(&adutux_mutex); |
46c9844c | 390 | exit: |
441b62c1 | 391 | dbg(2," %s : leave, return value %d", __func__, retval); |
03270634 SH |
392 | return retval; |
393 | } | |
394 | ||
395 | static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, | |
396 | loff_t *ppos) | |
397 | { | |
398 | struct adu_device *dev; | |
399 | size_t bytes_read = 0; | |
400 | size_t bytes_to_read = count; | |
401 | int i; | |
402 | int retval = 0; | |
403 | int timeout = 0; | |
404 | int should_submit = 0; | |
405 | unsigned long flags; | |
406 | DECLARE_WAITQUEUE(wait, current); | |
407 | ||
441b62c1 | 408 | dbg(2," %s : enter, count = %Zd, file=%p", __func__, count, file); |
03270634 SH |
409 | |
410 | dev = file->private_data; | |
441b62c1 | 411 | dbg(2," %s : dev=%p", __func__, dev); |
f08812d5 | 412 | |
8293c568 | 413 | if (mutex_lock_interruptible(&dev->mtx)) |
03270634 SH |
414 | return -ERESTARTSYS; |
415 | ||
416 | /* verify that the device wasn't unplugged */ | |
f08812d5 | 417 | if (dev->udev == NULL) { |
03270634 | 418 | retval = -ENODEV; |
fd3f1917 GKH |
419 | printk(KERN_ERR "adutux: No device or device unplugged %d\n", |
420 | retval); | |
03270634 SH |
421 | goto exit; |
422 | } | |
423 | ||
424 | /* verify that some data was requested */ | |
425 | if (count == 0) { | |
441b62c1 | 426 | dbg(1," %s : read request of 0 bytes", __func__); |
03270634 SH |
427 | goto exit; |
428 | } | |
429 | ||
430 | timeout = COMMAND_TIMEOUT; | |
441b62c1 | 431 | dbg(2," %s : about to start looping", __func__); |
03270634 SH |
432 | while (bytes_to_read) { |
433 | int data_in_secondary = dev->secondary_tail - dev->secondary_head; | |
434 | dbg(2," %s : while, data_in_secondary=%d, status=%d", | |
441b62c1 | 435 | __func__, data_in_secondary, |
03270634 SH |
436 | dev->interrupt_in_urb->status); |
437 | ||
438 | if (data_in_secondary) { | |
439 | /* drain secondary buffer */ | |
440 | int amount = bytes_to_read < data_in_secondary ? bytes_to_read : data_in_secondary; | |
441 | i = copy_to_user(buffer, dev->read_buffer_secondary+dev->secondary_head, amount); | |
1865a9c3 | 442 | if (i) { |
03270634 SH |
443 | retval = -EFAULT; |
444 | goto exit; | |
445 | } | |
446 | dev->secondary_head += (amount - i); | |
447 | bytes_read += (amount - i); | |
448 | bytes_to_read -= (amount - i); | |
449 | if (i) { | |
450 | retval = bytes_read ? bytes_read : -EFAULT; | |
451 | goto exit; | |
452 | } | |
453 | } else { | |
454 | /* we check the primary buffer */ | |
455 | spin_lock_irqsave (&dev->buflock, flags); | |
456 | if (dev->read_buffer_length) { | |
457 | /* we secure access to the primary */ | |
458 | char *tmp; | |
459 | dbg(2," %s : swap, read_buffer_length = %d", | |
441b62c1 | 460 | __func__, dev->read_buffer_length); |
03270634 SH |
461 | tmp = dev->read_buffer_secondary; |
462 | dev->read_buffer_secondary = dev->read_buffer_primary; | |
463 | dev->read_buffer_primary = tmp; | |
464 | dev->secondary_head = 0; | |
465 | dev->secondary_tail = dev->read_buffer_length; | |
466 | dev->read_buffer_length = 0; | |
467 | spin_unlock_irqrestore(&dev->buflock, flags); | |
468 | /* we have a free buffer so use it */ | |
469 | should_submit = 1; | |
470 | } else { | |
471 | /* even the primary was empty - we may need to do IO */ | |
f08812d5 | 472 | if (!dev->read_urb_finished) { |
03270634 SH |
473 | /* somebody is doing IO */ |
474 | spin_unlock_irqrestore(&dev->buflock, flags); | |
441b62c1 | 475 | dbg(2," %s : submitted already", __func__); |
03270634 SH |
476 | } else { |
477 | /* we must initiate input */ | |
441b62c1 | 478 | dbg(2," %s : initiate input", __func__); |
03270634 | 479 | dev->read_urb_finished = 0; |
f08812d5 | 480 | spin_unlock_irqrestore(&dev->buflock, flags); |
03270634 SH |
481 | |
482 | usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, | |
483 | usb_rcvintpipe(dev->udev, | |
484 | dev->interrupt_in_endpoint->bEndpointAddress), | |
485 | dev->interrupt_in_buffer, | |
29cc8897 | 486 | usb_endpoint_maxp(dev->interrupt_in_endpoint), |
03270634 SH |
487 | adu_interrupt_in_callback, |
488 | dev, | |
489 | dev->interrupt_in_endpoint->bInterval); | |
f08812d5 PZ |
490 | retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); |
491 | if (retval) { | |
492 | dev->read_urb_finished = 1; | |
03270634 SH |
493 | if (retval == -ENOMEM) { |
494 | retval = bytes_read ? bytes_read : -ENOMEM; | |
495 | } | |
441b62c1 | 496 | dbg(2," %s : submit failed", __func__); |
03270634 SH |
497 | goto exit; |
498 | } | |
499 | } | |
500 | ||
501 | /* we wait for I/O to complete */ | |
502 | set_current_state(TASK_INTERRUPTIBLE); | |
503 | add_wait_queue(&dev->read_wait, &wait); | |
f08812d5 PZ |
504 | spin_lock_irqsave(&dev->buflock, flags); |
505 | if (!dev->read_urb_finished) { | |
506 | spin_unlock_irqrestore(&dev->buflock, flags); | |
03270634 | 507 | timeout = schedule_timeout(COMMAND_TIMEOUT); |
f08812d5 PZ |
508 | } else { |
509 | spin_unlock_irqrestore(&dev->buflock, flags); | |
03270634 | 510 | set_current_state(TASK_RUNNING); |
f08812d5 | 511 | } |
03270634 SH |
512 | remove_wait_queue(&dev->read_wait, &wait); |
513 | ||
514 | if (timeout <= 0) { | |
441b62c1 | 515 | dbg(2," %s : timeout", __func__); |
03270634 SH |
516 | retval = bytes_read ? bytes_read : -ETIMEDOUT; |
517 | goto exit; | |
518 | } | |
519 | ||
520 | if (signal_pending(current)) { | |
441b62c1 | 521 | dbg(2," %s : signal pending", __func__); |
03270634 SH |
522 | retval = bytes_read ? bytes_read : -EINTR; |
523 | goto exit; | |
524 | } | |
525 | } | |
526 | } | |
527 | } | |
528 | ||
529 | retval = bytes_read; | |
530 | /* if the primary buffer is empty then use it */ | |
f08812d5 PZ |
531 | spin_lock_irqsave(&dev->buflock, flags); |
532 | if (should_submit && dev->read_urb_finished) { | |
533 | dev->read_urb_finished = 0; | |
534 | spin_unlock_irqrestore(&dev->buflock, flags); | |
03270634 SH |
535 | usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, |
536 | usb_rcvintpipe(dev->udev, | |
537 | dev->interrupt_in_endpoint->bEndpointAddress), | |
f08812d5 | 538 | dev->interrupt_in_buffer, |
29cc8897 | 539 | usb_endpoint_maxp(dev->interrupt_in_endpoint), |
f08812d5 PZ |
540 | adu_interrupt_in_callback, |
541 | dev, | |
542 | dev->interrupt_in_endpoint->bInterval); | |
543 | if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL) != 0) | |
544 | dev->read_urb_finished = 1; | |
03270634 | 545 | /* we ignore failure */ |
f08812d5 PZ |
546 | } else { |
547 | spin_unlock_irqrestore(&dev->buflock, flags); | |
03270634 SH |
548 | } |
549 | ||
550 | exit: | |
551 | /* unlock the device */ | |
8293c568 | 552 | mutex_unlock(&dev->mtx); |
03270634 | 553 | |
441b62c1 | 554 | dbg(2," %s : leave, return value %d", __func__, retval); |
03270634 SH |
555 | return retval; |
556 | } | |
557 | ||
558 | static ssize_t adu_write(struct file *file, const __user char *buffer, | |
559 | size_t count, loff_t *ppos) | |
560 | { | |
f08812d5 | 561 | DECLARE_WAITQUEUE(waita, current); |
03270634 SH |
562 | struct adu_device *dev; |
563 | size_t bytes_written = 0; | |
564 | size_t bytes_to_write; | |
565 | size_t buffer_size; | |
f08812d5 | 566 | unsigned long flags; |
ebc3ac14 | 567 | int retval; |
03270634 | 568 | |
441b62c1 | 569 | dbg(2," %s : enter, count = %Zd", __func__, count); |
03270634 SH |
570 | |
571 | dev = file->private_data; | |
572 | ||
8293c568 | 573 | retval = mutex_lock_interruptible(&dev->mtx); |
ebc3ac14 ON |
574 | if (retval) |
575 | goto exit_nolock; | |
03270634 SH |
576 | |
577 | /* verify that the device wasn't unplugged */ | |
f08812d5 | 578 | if (dev->udev == NULL) { |
03270634 | 579 | retval = -ENODEV; |
fd3f1917 GKH |
580 | printk(KERN_ERR "adutux: No device or device unplugged %d\n", |
581 | retval); | |
03270634 SH |
582 | goto exit; |
583 | } | |
584 | ||
585 | /* verify that we actually have some data to write */ | |
586 | if (count == 0) { | |
441b62c1 | 587 | dbg(1," %s : write request of 0 bytes", __func__); |
03270634 SH |
588 | goto exit; |
589 | } | |
590 | ||
03270634 | 591 | while (count > 0) { |
f08812d5 PZ |
592 | add_wait_queue(&dev->write_wait, &waita); |
593 | set_current_state(TASK_INTERRUPTIBLE); | |
594 | spin_lock_irqsave(&dev->buflock, flags); | |
595 | if (!dev->out_urb_finished) { | |
596 | spin_unlock_irqrestore(&dev->buflock, flags); | |
03270634 | 597 | |
f08812d5 PZ |
598 | mutex_unlock(&dev->mtx); |
599 | if (signal_pending(current)) { | |
441b62c1 | 600 | dbg(1," %s : interrupted", __func__); |
f08812d5 | 601 | set_current_state(TASK_RUNNING); |
03270634 | 602 | retval = -EINTR; |
f08812d5 | 603 | goto exit_onqueue; |
03270634 | 604 | } |
f08812d5 | 605 | if (schedule_timeout(COMMAND_TIMEOUT) == 0) { |
441b62c1 | 606 | dbg(1, "%s - command timed out.", __func__); |
f08812d5 PZ |
607 | retval = -ETIMEDOUT; |
608 | goto exit_onqueue; | |
609 | } | |
610 | remove_wait_queue(&dev->write_wait, &waita); | |
8293c568 | 611 | retval = mutex_lock_interruptible(&dev->mtx); |
ebc3ac14 ON |
612 | if (retval) { |
613 | retval = bytes_written ? bytes_written : retval; | |
614 | goto exit_nolock; | |
615 | } | |
03270634 | 616 | |
441b62c1 | 617 | dbg(4," %s : in progress, count = %Zd", __func__, count); |
03270634 | 618 | } else { |
f08812d5 PZ |
619 | spin_unlock_irqrestore(&dev->buflock, flags); |
620 | set_current_state(TASK_RUNNING); | |
621 | remove_wait_queue(&dev->write_wait, &waita); | |
441b62c1 | 622 | dbg(4," %s : sending, count = %Zd", __func__, count); |
03270634 SH |
623 | |
624 | /* write the data into interrupt_out_buffer from userspace */ | |
29cc8897 | 625 | buffer_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); |
03270634 SH |
626 | bytes_to_write = count > buffer_size ? buffer_size : count; |
627 | dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", | |
441b62c1 | 628 | __func__, buffer_size, count, bytes_to_write); |
03270634 SH |
629 | |
630 | if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) { | |
631 | retval = -EFAULT; | |
632 | goto exit; | |
633 | } | |
634 | ||
635 | /* send off the urb */ | |
636 | usb_fill_int_urb( | |
637 | dev->interrupt_out_urb, | |
638 | dev->udev, | |
639 | usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress), | |
640 | dev->interrupt_out_buffer, | |
641 | bytes_to_write, | |
642 | adu_interrupt_out_callback, | |
643 | dev, | |
f08812d5 | 644 | dev->interrupt_out_endpoint->bInterval); |
03270634 | 645 | dev->interrupt_out_urb->actual_length = bytes_to_write; |
f08812d5 | 646 | dev->out_urb_finished = 0; |
03270634 SH |
647 | retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL); |
648 | if (retval < 0) { | |
f08812d5 | 649 | dev->out_urb_finished = 1; |
fd3f1917 GKH |
650 | dev_err(&dev->udev->dev, "Couldn't submit " |
651 | "interrupt_out_urb %d\n", retval); | |
03270634 SH |
652 | goto exit; |
653 | } | |
654 | ||
655 | buffer += bytes_to_write; | |
656 | count -= bytes_to_write; | |
657 | ||
658 | bytes_written += bytes_to_write; | |
659 | } | |
660 | } | |
f08812d5 PZ |
661 | mutex_unlock(&dev->mtx); |
662 | return bytes_written; | |
03270634 SH |
663 | |
664 | exit: | |
8293c568 | 665 | mutex_unlock(&dev->mtx); |
ebc3ac14 | 666 | exit_nolock: |
441b62c1 | 667 | dbg(2," %s : leave, return value %d", __func__, retval); |
f08812d5 | 668 | return retval; |
03270634 | 669 | |
f08812d5 PZ |
670 | exit_onqueue: |
671 | remove_wait_queue(&dev->write_wait, &waita); | |
03270634 SH |
672 | return retval; |
673 | } | |
674 | ||
675 | /* file operations needed when we register this driver */ | |
00977a59 | 676 | static const struct file_operations adu_fops = { |
03270634 SH |
677 | .owner = THIS_MODULE, |
678 | .read = adu_read, | |
679 | .write = adu_write, | |
680 | .open = adu_open, | |
681 | .release = adu_release, | |
6038f373 | 682 | .llseek = noop_llseek, |
03270634 SH |
683 | }; |
684 | ||
685 | /* | |
686 | * usb class driver info in order to get a minor number from the usb core, | |
687 | * and to have the device registered with devfs and the driver core | |
688 | */ | |
689 | static struct usb_class_driver adu_class = { | |
690 | .name = "usb/adutux%d", | |
691 | .fops = &adu_fops, | |
692 | .minor_base = ADU_MINOR_BASE, | |
693 | }; | |
694 | ||
695 | /** | |
696 | * adu_probe | |
697 | * | |
698 | * Called by the usb core when a new device is connected that it thinks | |
699 | * this driver might be interested in. | |
700 | */ | |
701 | static int adu_probe(struct usb_interface *interface, | |
702 | const struct usb_device_id *id) | |
703 | { | |
704 | struct usb_device *udev = interface_to_usbdev(interface); | |
705 | struct adu_device *dev = NULL; | |
706 | struct usb_host_interface *iface_desc; | |
707 | struct usb_endpoint_descriptor *endpoint; | |
708 | int retval = -ENODEV; | |
709 | int in_end_size; | |
710 | int out_end_size; | |
711 | int i; | |
712 | ||
441b62c1 | 713 | dbg(2," %s : enter", __func__); |
03270634 SH |
714 | |
715 | if (udev == NULL) { | |
716 | dev_err(&interface->dev, "udev is NULL.\n"); | |
717 | goto exit; | |
718 | } | |
719 | ||
b595076a | 720 | /* allocate memory for our device state and initialize it */ |
03270634 SH |
721 | dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL); |
722 | if (dev == NULL) { | |
723 | dev_err(&interface->dev, "Out of memory\n"); | |
724 | retval = -ENOMEM; | |
725 | goto exit; | |
726 | } | |
727 | ||
8293c568 | 728 | mutex_init(&dev->mtx); |
03270634 SH |
729 | spin_lock_init(&dev->buflock); |
730 | dev->udev = udev; | |
731 | init_waitqueue_head(&dev->read_wait); | |
732 | init_waitqueue_head(&dev->write_wait); | |
733 | ||
734 | iface_desc = &interface->altsetting[0]; | |
735 | ||
736 | /* set up the endpoint information */ | |
737 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | |
738 | endpoint = &iface_desc->endpoint[i].desc; | |
739 | ||
740 | if (usb_endpoint_is_int_in(endpoint)) | |
741 | dev->interrupt_in_endpoint = endpoint; | |
742 | ||
743 | if (usb_endpoint_is_int_out(endpoint)) | |
744 | dev->interrupt_out_endpoint = endpoint; | |
745 | } | |
746 | if (dev->interrupt_in_endpoint == NULL) { | |
747 | dev_err(&interface->dev, "interrupt in endpoint not found\n"); | |
748 | goto error; | |
749 | } | |
750 | if (dev->interrupt_out_endpoint == NULL) { | |
751 | dev_err(&interface->dev, "interrupt out endpoint not found\n"); | |
752 | goto error; | |
753 | } | |
754 | ||
29cc8897 KM |
755 | in_end_size = usb_endpoint_maxp(dev->interrupt_in_endpoint); |
756 | out_end_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); | |
03270634 SH |
757 | |
758 | dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL); | |
759 | if (!dev->read_buffer_primary) { | |
760 | dev_err(&interface->dev, "Couldn't allocate read_buffer_primary\n"); | |
761 | retval = -ENOMEM; | |
762 | goto error; | |
763 | } | |
764 | ||
765 | /* debug code prime the buffer */ | |
766 | memset(dev->read_buffer_primary, 'a', in_end_size); | |
767 | memset(dev->read_buffer_primary + in_end_size, 'b', in_end_size); | |
768 | memset(dev->read_buffer_primary + (2 * in_end_size), 'c', in_end_size); | |
769 | memset(dev->read_buffer_primary + (3 * in_end_size), 'd', in_end_size); | |
770 | ||
771 | dev->read_buffer_secondary = kmalloc((4 * in_end_size), GFP_KERNEL); | |
772 | if (!dev->read_buffer_secondary) { | |
773 | dev_err(&interface->dev, "Couldn't allocate read_buffer_secondary\n"); | |
774 | retval = -ENOMEM; | |
775 | goto error; | |
776 | } | |
777 | ||
778 | /* debug code prime the buffer */ | |
779 | memset(dev->read_buffer_secondary, 'e', in_end_size); | |
780 | memset(dev->read_buffer_secondary + in_end_size, 'f', in_end_size); | |
781 | memset(dev->read_buffer_secondary + (2 * in_end_size), 'g', in_end_size); | |
782 | memset(dev->read_buffer_secondary + (3 * in_end_size), 'h', in_end_size); | |
783 | ||
784 | dev->interrupt_in_buffer = kmalloc(in_end_size, GFP_KERNEL); | |
785 | if (!dev->interrupt_in_buffer) { | |
786 | dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n"); | |
787 | goto error; | |
788 | } | |
789 | ||
790 | /* debug code prime the buffer */ | |
791 | memset(dev->interrupt_in_buffer, 'i', in_end_size); | |
792 | ||
793 | dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); | |
794 | if (!dev->interrupt_in_urb) { | |
795 | dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n"); | |
796 | goto error; | |
797 | } | |
798 | dev->interrupt_out_buffer = kmalloc(out_end_size, GFP_KERNEL); | |
799 | if (!dev->interrupt_out_buffer) { | |
800 | dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer\n"); | |
801 | goto error; | |
802 | } | |
803 | dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); | |
804 | if (!dev->interrupt_out_urb) { | |
805 | dev_err(&interface->dev, "Couldn't allocate interrupt_out_urb\n"); | |
806 | goto error; | |
807 | } | |
808 | ||
809 | if (!usb_string(udev, udev->descriptor.iSerialNumber, dev->serial_number, | |
810 | sizeof(dev->serial_number))) { | |
811 | dev_err(&interface->dev, "Could not retrieve serial number\n"); | |
812 | goto error; | |
813 | } | |
441b62c1 | 814 | dbg(2," %s : serial_number=%s", __func__, dev->serial_number); |
03270634 SH |
815 | |
816 | /* we can register the device now, as it is ready */ | |
817 | usb_set_intfdata(interface, dev); | |
818 | ||
819 | retval = usb_register_dev(interface, &adu_class); | |
820 | ||
821 | if (retval) { | |
822 | /* something prevented us from registering this driver */ | |
823 | dev_err(&interface->dev, "Not able to get a minor for this device.\n"); | |
824 | usb_set_intfdata(interface, NULL); | |
825 | goto error; | |
826 | } | |
827 | ||
828 | dev->minor = interface->minor; | |
829 | ||
830 | /* let the user know what node this device is now attached to */ | |
898eb71c | 831 | dev_info(&interface->dev, "ADU%d %s now attached to /dev/usb/adutux%d\n", |
03270634 SH |
832 | udev->descriptor.idProduct, dev->serial_number, |
833 | (dev->minor - ADU_MINOR_BASE)); | |
834 | exit: | |
441b62c1 | 835 | dbg(2," %s : leave, return value %p (dev)", __func__, dev); |
03270634 SH |
836 | |
837 | return retval; | |
838 | ||
839 | error: | |
840 | adu_delete(dev); | |
841 | return retval; | |
842 | } | |
843 | ||
844 | /** | |
845 | * adu_disconnect | |
846 | * | |
847 | * Called by the usb core when the device is removed from the system. | |
848 | */ | |
849 | static void adu_disconnect(struct usb_interface *interface) | |
850 | { | |
851 | struct adu_device *dev; | |
852 | int minor; | |
853 | ||
441b62c1 | 854 | dbg(2," %s : enter", __func__); |
03270634 | 855 | |
03270634 | 856 | dev = usb_get_intfdata(interface); |
03270634 | 857 | |
f08812d5 PZ |
858 | mutex_lock(&dev->mtx); /* not interruptible */ |
859 | dev->udev = NULL; /* poison */ | |
03270634 | 860 | minor = dev->minor; |
03270634 | 861 | usb_deregister_dev(interface, &adu_class); |
f08812d5 | 862 | mutex_unlock(&dev->mtx); |
03270634 | 863 | |
f08812d5 PZ |
864 | mutex_lock(&adutux_mutex); |
865 | usb_set_intfdata(interface, NULL); | |
d4ead16f | 866 | |
03270634 | 867 | /* if the device is not opened, then we clean up right now */ |
441b62c1 | 868 | dbg(2," %s : open count %d", __func__, dev->open_count); |
f08812d5 | 869 | if (!dev->open_count) |
03270634 | 870 | adu_delete(dev); |
f08812d5 PZ |
871 | |
872 | mutex_unlock(&adutux_mutex); | |
03270634 | 873 | |
898eb71c | 874 | dev_info(&interface->dev, "ADU device adutux%d now disconnected\n", |
03270634 SH |
875 | (minor - ADU_MINOR_BASE)); |
876 | ||
441b62c1 | 877 | dbg(2," %s : leave", __func__); |
03270634 SH |
878 | } |
879 | ||
880 | /* usb specific object needed to register this driver with the usb subsystem */ | |
881 | static struct usb_driver adu_driver = { | |
882 | .name = "adutux", | |
883 | .probe = adu_probe, | |
884 | .disconnect = adu_disconnect, | |
885 | .id_table = device_table, | |
886 | }; | |
887 | ||
65db4305 | 888 | module_usb_driver(adu_driver); |
03270634 SH |
889 | |
890 | MODULE_AUTHOR(DRIVER_AUTHOR); | |
891 | MODULE_DESCRIPTION(DRIVER_DESC); | |
892 | MODULE_LICENSE("GPL"); |