diff options
author | Manuel Francisco Naranjo <naranjo.manuel@gmail.com> | 2006-08-09 15:35:12 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-09-27 14:58:59 -0400 |
commit | 3fe70ba2272c123cf38e4c577bf220f8bcf25366 (patch) | |
tree | 14a29be4109d2f3c537b954734825916369b981e /drivers/usb/serial/aircable.c | |
parent | 78aef519ed07797f94cff1d0d66dd01704474916 (diff) |
Add AIRcable USB Bluetooth Dongle Driver
Add driver for AIRcable USB Bluetooth dongle.
Signed-off-by: Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/serial/aircable.c')
-rw-r--r-- | drivers/usb/serial/aircable.c | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c new file mode 100644 index 000000000000..8aaf7db93992 --- /dev/null +++ b/drivers/usb/serial/aircable.c | |||
@@ -0,0 +1,625 @@ | |||
1 | /* | ||
2 | * AIRcable USB Bluetooth Dongle Driver. | ||
3 | * | ||
4 | * Copyright (C) 2006 Manuel Francisco Naranjo (naranjo.manuel@gmail.com) | ||
5 | * This program is free software; you can redistribute it and/or modify it under | ||
6 | * the terms of the GNU General Public License version 2 as published by the | ||
7 | * Free Software Foundation. | ||
8 | * | ||
9 | * The device works as an standard CDC device, it has 2 interfaces, the first | ||
10 | * one is for firmware access and the second is the serial one. | ||
11 | * The protocol is very simply, there are two posibilities reading or writing. | ||
12 | * When writting the first urb must have a Header that starts with 0x20 0x29 the | ||
13 | * next two bytes must say how much data will be sended. | ||
14 | * When reading the process is almost equal except that the header starts with | ||
15 | * 0x00 0x20. | ||
16 | * | ||
17 | * The device simply need some stuff to understand data comming from the usb | ||
18 | * buffer: The First and Second byte is used for a Header, the Third and Fourth | ||
19 | * tells the device the amount of information the package holds. | ||
20 | * Packages are 60 bytes long Header Stuff. | ||
21 | * When writting to the device the first two bytes of the header are 0x20 0x29 | ||
22 | * When reading the bytes are 0x00 0x20, or 0x00 0x10, there is an strange | ||
23 | * situation, when too much data arrives to the device because it sends the data | ||
24 | * but with out the header. I will use a simply hack to override this situation, | ||
25 | * if there is data coming that does not contain any header, then that is data | ||
26 | * that must go directly to the tty, as there is no documentation about if there | ||
27 | * is any other control code, I will simply check for the first | ||
28 | * one. | ||
29 | * | ||
30 | * The driver registers himself with the USB-serial core and the USB Core. I had | ||
31 | * to implement a probe function agains USB-serial, because other way, the | ||
32 | * driver was attaching himself to both interfaces. I have tryed with different | ||
33 | * configurations of usb_serial_driver with out exit, only the probe function | ||
34 | * could handle this correctly. | ||
35 | * | ||
36 | * I have taken some info from a Greg Kroah-Hartman article: | ||
37 | * http://www.linuxjournal.com/article/6573 | ||
38 | * And from Linux Device Driver Kit CD, which is a great work, the authors taken | ||
39 | * the work to recompile lots of information an knowladge in drivers development | ||
40 | * and made it all avaible inside a cd. | ||
41 | * URL: http://kernel.org/pub/linux/kernel/people/gregkh/ddk/ | ||
42 | * | ||
43 | */ | ||
44 | |||
45 | #include <linux/tty.h> | ||
46 | #include <linux/tty_flip.h> | ||
47 | #include <linux/circ_buf.h> | ||
48 | #include <linux/usb.h> | ||
49 | #include <linux/usb/serial.h> | ||
50 | |||
51 | static int debug; | ||
52 | |||
53 | /* Vendor and Product ID */ | ||
54 | #define AIRCABLE_VID 0x16CA | ||
55 | #define AIRCABLE_USB_PID 0x1502 | ||
56 | |||
57 | /* write buffer size defines */ | ||
58 | #define AIRCABLE_BUF_SIZE 2048 | ||
59 | |||
60 | /* Protocol Stuff */ | ||
61 | #define HCI_HEADER_LENGTH 0x4 | ||
62 | #define TX_HEADER_0 0x20 | ||
63 | #define TX_HEADER_1 0x29 | ||
64 | #define RX_HEADER_0 0x00 | ||
65 | #define RX_HEADER_1 0x20 | ||
66 | #define MAX_HCI_FRAMESIZE 60 | ||
67 | #define HCI_COMPLETE_FRAME 64 | ||
68 | |||
69 | /* rx_flags */ | ||
70 | #define THROTTLED 0x01 | ||
71 | #define ACTUALLY_THROTTLED 0x02 | ||
72 | |||
73 | /* | ||
74 | * Version Information | ||
75 | */ | ||
76 | #define DRIVER_VERSION "v1.0b2" | ||
77 | #define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>" | ||
78 | #define DRIVER_DESC "AIRcable USB Driver" | ||
79 | |||
80 | /* ID table that will be registered with USB core */ | ||
81 | static struct usb_device_id id_table [] = { | ||
82 | { USB_DEVICE(AIRCABLE_VID, AIRCABLE_USB_PID) }, | ||
83 | { }, | ||
84 | }; | ||
85 | MODULE_DEVICE_TABLE(usb, id_table); | ||
86 | |||
87 | |||
88 | /* Internal Structure */ | ||
89 | struct aircable_private { | ||
90 | spinlock_t rx_lock; /* spinlock for the receive lines */ | ||
91 | struct circ_buf *tx_buf; /* write buffer */ | ||
92 | struct circ_buf *rx_buf; /* read buffer */ | ||
93 | int rx_flags; /* for throttilng */ | ||
94 | struct work_struct rx_work; /* work cue for the receiving line */ | ||
95 | }; | ||
96 | |||
97 | /* Private methods */ | ||
98 | |||
99 | /* Circular Buffer Methods, code from ti_usb_3410_5052 used */ | ||
100 | /* | ||
101 | * serial_buf_clear | ||
102 | * | ||
103 | * Clear out all data in the circular buffer. | ||
104 | */ | ||
105 | static void serial_buf_clear(struct circ_buf *cb) | ||
106 | { | ||
107 | cb->head = cb->tail = 0; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * serial_buf_alloc | ||
112 | * | ||
113 | * Allocate a circular buffer and all associated memory. | ||
114 | */ | ||
115 | static struct circ_buf *serial_buf_alloc(void) | ||
116 | { | ||
117 | struct circ_buf *cb; | ||
118 | cb = kmalloc(sizeof(struct circ_buf), GFP_KERNEL); | ||
119 | if (cb == NULL) | ||
120 | return NULL; | ||
121 | cb->buf = kmalloc(AIRCABLE_BUF_SIZE, GFP_KERNEL); | ||
122 | if (cb->buf == NULL) { | ||
123 | kfree(cb); | ||
124 | return NULL; | ||
125 | } | ||
126 | serial_buf_clear(cb); | ||
127 | return cb; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * serial_buf_free | ||
132 | * | ||
133 | * Free the buffer and all associated memory. | ||
134 | */ | ||
135 | static void serial_buf_free(struct circ_buf *cb) | ||
136 | { | ||
137 | kfree(cb->buf); | ||
138 | kfree(cb); | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * serial_buf_data_avail | ||
143 | * | ||
144 | * Return the number of bytes of data available in the circular | ||
145 | * buffer. | ||
146 | */ | ||
147 | static int serial_buf_data_avail(struct circ_buf *cb) | ||
148 | { | ||
149 | return CIRC_CNT(cb->head,cb->tail,AIRCABLE_BUF_SIZE); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * serial_buf_put | ||
154 | * | ||
155 | * Copy data data from a user buffer and put it into the circular buffer. | ||
156 | * Restrict to the amount of space available. | ||
157 | * | ||
158 | * Return the number of bytes copied. | ||
159 | */ | ||
160 | static int serial_buf_put(struct circ_buf *cb, const char *buf, int count) | ||
161 | { | ||
162 | int c, ret = 0; | ||
163 | while (1) { | ||
164 | c = CIRC_SPACE_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE); | ||
165 | if (count < c) | ||
166 | c = count; | ||
167 | if (c <= 0) | ||
168 | break; | ||
169 | memcpy(cb->buf + cb->head, buf, c); | ||
170 | cb->head = (cb->head + c) & (AIRCABLE_BUF_SIZE-1); | ||
171 | buf += c; | ||
172 | count -= c; | ||
173 | ret= c; | ||
174 | } | ||
175 | return ret; | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * serial_buf_get | ||
180 | * | ||
181 | * Get data from the circular buffer and copy to the given buffer. | ||
182 | * Restrict to the amount of data available. | ||
183 | * | ||
184 | * Return the number of bytes copied. | ||
185 | */ | ||
186 | static int serial_buf_get(struct circ_buf *cb, char *buf, int count) | ||
187 | { | ||
188 | int c, ret = 0; | ||
189 | while (1) { | ||
190 | c = CIRC_CNT_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE); | ||
191 | if (count < c) | ||
192 | c = count; | ||
193 | if (c <= 0) | ||
194 | break; | ||
195 | memcpy(buf, cb->buf + cb->tail, c); | ||
196 | cb->tail = (cb->tail + c) & (AIRCABLE_BUF_SIZE-1); | ||
197 | buf += c; | ||
198 | count -= c; | ||
199 | ret= c; | ||
200 | } | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | /* End of circula buffer methods */ | ||
205 | |||
206 | static void aircable_send(struct usb_serial_port *port) | ||
207 | { | ||
208 | int count, result; | ||
209 | struct aircable_private *priv = usb_get_serial_port_data(port); | ||
210 | unsigned char* buf; | ||
211 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
212 | if (port->write_urb_busy) | ||
213 | return; | ||
214 | |||
215 | count = min(serial_buf_data_avail(priv->tx_buf), MAX_HCI_FRAMESIZE); | ||
216 | if (count == 0) | ||
217 | return; | ||
218 | |||
219 | buf = kzalloc(count + HCI_HEADER_LENGTH, GFP_ATOMIC); | ||
220 | if (!buf) { | ||
221 | err("%s- kzalloc(%Zd) failed.", __FUNCTION__, | ||
222 | count + HCI_HEADER_LENGTH); | ||
223 | return; | ||
224 | } | ||
225 | |||
226 | buf[0] = TX_HEADER_0; | ||
227 | buf[1] = TX_HEADER_1; | ||
228 | buf[2] = (unsigned char)count; | ||
229 | buf[3] = (unsigned char)(count >> 8); | ||
230 | serial_buf_get(priv->tx_buf,buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE); | ||
231 | |||
232 | memcpy(port->write_urb->transfer_buffer, buf, | ||
233 | count + HCI_HEADER_LENGTH); | ||
234 | |||
235 | kfree(buf); | ||
236 | port->write_urb_busy = 1; | ||
237 | usb_serial_debug_data(debug, &port->dev, __FUNCTION__, | ||
238 | count + HCI_HEADER_LENGTH, | ||
239 | port->write_urb->transfer_buffer); | ||
240 | port->write_urb->transfer_buffer_length = count + HCI_HEADER_LENGTH; | ||
241 | port->write_urb->dev = port->serial->dev; | ||
242 | result = usb_submit_urb(port->write_urb, GFP_ATOMIC); | ||
243 | |||
244 | if (result) { | ||
245 | dev_err(&port->dev, | ||
246 | "%s - failed submitting write urb, error %d\n", | ||
247 | __FUNCTION__, result); | ||
248 | port->write_urb_busy = 0; | ||
249 | } | ||
250 | |||
251 | schedule_work(&port->work); | ||
252 | } | ||
253 | |||
254 | static void aircable_read(void *params) | ||
255 | { | ||
256 | struct usb_serial_port *port = params; | ||
257 | struct aircable_private *priv = usb_get_serial_port_data(port); | ||
258 | struct tty_struct *tty; | ||
259 | unsigned char *data; | ||
260 | int count; | ||
261 | if (priv->rx_flags & THROTTLED){ | ||
262 | if (priv->rx_flags & ACTUALLY_THROTTLED) | ||
263 | schedule_work(&priv->rx_work); | ||
264 | return; | ||
265 | } | ||
266 | |||
267 | /* By now I will flush data to the tty in packages of no more than | ||
268 | * 64 bytes, to ensure I do not get throttled. | ||
269 | * Ask USB mailing list for better aproach. | ||
270 | */ | ||
271 | tty = port->tty; | ||
272 | |||
273 | if (!tty) | ||
274 | schedule_work(&priv->rx_work); | ||
275 | |||
276 | count = min(64, serial_buf_data_avail(priv->rx_buf)); | ||
277 | |||
278 | if (count <= 0) | ||
279 | return; //We have finished sending everything. | ||
280 | |||
281 | tty_prepare_flip_string(tty, &data, count); | ||
282 | if (!data){ | ||
283 | err("%s- kzalloc(%Zd) failed.", __FUNCTION__, count); | ||
284 | return; | ||
285 | } | ||
286 | |||
287 | serial_buf_get(priv->rx_buf, data, count); | ||
288 | |||
289 | tty_flip_buffer_push(tty); | ||
290 | |||
291 | if (serial_buf_data_avail(priv->rx_buf)) | ||
292 | schedule_work(&priv->rx_work); | ||
293 | |||
294 | return; | ||
295 | } | ||
296 | /* End of private methods */ | ||
297 | |||
298 | static int aircable_probe(struct usb_serial *serial, | ||
299 | const struct usb_device_id *id) | ||
300 | { | ||
301 | struct usb_host_interface *iface_desc = serial->interface->cur_altsetting; | ||
302 | struct usb_endpoint_descriptor *endpoint; | ||
303 | int num_bulk_out=0; | ||
304 | int i; | ||
305 | |||
306 | for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { | ||
307 | endpoint = &iface_desc->endpoint[i].desc; | ||
308 | if (((endpoint->bEndpointAddress & 0x80) == 0x00) && | ||
309 | ((endpoint->bmAttributes & 3) == 0x02)) { | ||
310 | /* we found our bulk out endpoint */ | ||
311 | dbg("found bulk out on endpoint %d", i); | ||
312 | ++num_bulk_out; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | if (num_bulk_out == 0) { | ||
317 | dbg("Invalid interface, discarding"); | ||
318 | return -ENODEV; | ||
319 | } | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int aircable_attach (struct usb_serial *serial) | ||
325 | { | ||
326 | struct usb_serial_port *port = serial->port[0]; | ||
327 | struct aircable_private *priv; | ||
328 | |||
329 | priv = kzalloc(sizeof(struct aircable_private), GFP_KERNEL); | ||
330 | if (!priv){ | ||
331 | err("%s- kmalloc(%Zd) failed.", __FUNCTION__, | ||
332 | sizeof(struct aircable_private)); | ||
333 | return -ENOMEM; | ||
334 | } | ||
335 | |||
336 | /* Allocation of Circular Buffers */ | ||
337 | priv->tx_buf = serial_buf_alloc(); | ||
338 | if (priv->tx_buf == NULL) { | ||
339 | kfree(priv); | ||
340 | return -ENOMEM; | ||
341 | } | ||
342 | |||
343 | priv->rx_buf = serial_buf_alloc(); | ||
344 | if (priv->rx_buf == NULL) { | ||
345 | kfree(priv->tx_buf); | ||
346 | kfree(priv); | ||
347 | return -ENOMEM; | ||
348 | } | ||
349 | |||
350 | priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); | ||
351 | INIT_WORK(&priv->rx_work, aircable_read, port); | ||
352 | |||
353 | usb_set_serial_port_data(serial->port[0], priv); | ||
354 | |||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | static void aircable_shutdown(struct usb_serial *serial) | ||
359 | { | ||
360 | |||
361 | struct usb_serial_port *port = serial->port[0]; | ||
362 | struct aircable_private *priv = usb_get_serial_port_data(port); | ||
363 | |||
364 | dbg("%s", __FUNCTION__); | ||
365 | |||
366 | if (priv) { | ||
367 | serial_buf_free(priv->tx_buf); | ||
368 | serial_buf_free(priv->rx_buf); | ||
369 | usb_set_serial_port_data(port, NULL); | ||
370 | kfree(priv); | ||
371 | } | ||
372 | } | ||
373 | |||
374 | static int aircable_write_room(struct usb_serial_port *port) | ||
375 | { | ||
376 | struct aircable_private *priv = usb_get_serial_port_data(port); | ||
377 | return serial_buf_data_avail(priv->tx_buf); | ||
378 | } | ||
379 | |||
380 | static int aircable_write(struct usb_serial_port *port, | ||
381 | const unsigned char *source, int count) | ||
382 | { | ||
383 | struct aircable_private *priv = usb_get_serial_port_data(port); | ||
384 | int temp; | ||
385 | |||
386 | dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); | ||
387 | |||
388 | usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, source); | ||
389 | |||
390 | if (!count){ | ||
391 | dbg("%s - write request of 0 bytes", __FUNCTION__); | ||
392 | return count; | ||
393 | } | ||
394 | |||
395 | temp = serial_buf_put(priv->tx_buf, source, count); | ||
396 | |||
397 | aircable_send(port); | ||
398 | |||
399 | if (count > AIRCABLE_BUF_SIZE) | ||
400 | count = AIRCABLE_BUF_SIZE; | ||
401 | |||
402 | return count; | ||
403 | |||
404 | } | ||
405 | |||
406 | static void aircable_write_bulk_callback(struct urb *urb, struct pt_regs *regs) | ||
407 | { | ||
408 | struct usb_serial_port *port = urb->context; | ||
409 | int result; | ||
410 | |||
411 | dbg("%s - urb->status: %d", __FUNCTION__ , urb->status); | ||
412 | |||
413 | /* This has been taken from cypress_m8.c cypress_write_int_callback */ | ||
414 | switch (urb->status) { | ||
415 | case 0: | ||
416 | /* success */ | ||
417 | break; | ||
418 | case -ECONNRESET: | ||
419 | case -ENOENT: | ||
420 | case -ESHUTDOWN: | ||
421 | /* this urb is terminated, clean up */ | ||
422 | dbg("%s - urb shutting down with status: %d", | ||
423 | __FUNCTION__, urb->status); | ||
424 | port->write_urb_busy = 0; | ||
425 | return; | ||
426 | default: | ||
427 | /* error in the urb, so we have to resubmit it */ | ||
428 | dbg("%s - Overflow in write", __FUNCTION__); | ||
429 | dbg("%s - nonzero write bulk status received: %d", | ||
430 | __FUNCTION__, urb->status); | ||
431 | port->write_urb->transfer_buffer_length = 1; | ||
432 | port->write_urb->dev = port->serial->dev; | ||
433 | result = usb_submit_urb(port->write_urb, GFP_KERNEL); | ||
434 | if (result) | ||
435 | dev_err(&urb->dev->dev, | ||
436 | "%s - failed resubmitting write urb, error %d\n", | ||
437 | __FUNCTION__, result); | ||
438 | else | ||
439 | return; | ||
440 | } | ||
441 | |||
442 | port->write_urb_busy = 0; | ||
443 | |||
444 | aircable_send(port); | ||
445 | } | ||
446 | |||
447 | static void aircable_read_bulk_callback(struct urb *urb, struct pt_regs *regs) | ||
448 | { | ||
449 | struct usb_serial_port *port = urb->context; | ||
450 | struct aircable_private *priv = usb_get_serial_port_data(port); | ||
451 | struct tty_struct *tty; | ||
452 | unsigned long no_packages, remaining, package_length, i; | ||
453 | int result, shift = 0; | ||
454 | unsigned char *temp; | ||
455 | |||
456 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
457 | |||
458 | if (urb->status) { | ||
459 | dbg("%s - urb->status = %d", __FUNCTION__, urb->status); | ||
460 | if (!port->open_count) { | ||
461 | dbg("%s - port is closed, exiting.", __FUNCTION__); | ||
462 | return; | ||
463 | } | ||
464 | if (urb->status == -EPROTO) { | ||
465 | dbg("%s - caught -EPROTO, resubmitting the urb", | ||
466 | __FUNCTION__); | ||
467 | usb_fill_bulk_urb(port->read_urb, port->serial->dev, | ||
468 | usb_rcvbulkpipe(port->serial->dev, | ||
469 | port->bulk_in_endpointAddress), | ||
470 | port->read_urb->transfer_buffer, | ||
471 | port->read_urb->transfer_buffer_length, | ||
472 | aircable_read_bulk_callback, port); | ||
473 | |||
474 | result = usb_submit_urb(urb, GFP_ATOMIC); | ||
475 | if (result) | ||
476 | dev_err(&urb->dev->dev, | ||
477 | "%s - failed resubmitting read urb, error %d\n", | ||
478 | __FUNCTION__, result); | ||
479 | return; | ||
480 | } | ||
481 | dbg("%s - unable to handle the error, exiting.", __FUNCTION__); | ||
482 | return; | ||
483 | } | ||
484 | |||
485 | usb_serial_debug_data(debug, &port->dev, __FUNCTION__, | ||
486 | urb->actual_length,urb->transfer_buffer); | ||
487 | |||
488 | tty = port->tty; | ||
489 | if (tty && urb->actual_length) { | ||
490 | if (urb->actual_length <= 2) { | ||
491 | /* This is an incomplete package */ | ||
492 | serial_buf_put(priv->rx_buf, urb->transfer_buffer, | ||
493 | urb->actual_length); | ||
494 | } else { | ||
495 | temp = urb->transfer_buffer; | ||
496 | if (temp[0] == RX_HEADER_0) | ||
497 | shift = HCI_HEADER_LENGTH; | ||
498 | |||
499 | remaining = urb->actual_length; | ||
500 | no_packages = urb->actual_length / (HCI_COMPLETE_FRAME); | ||
501 | |||
502 | if (urb->actual_length % HCI_COMPLETE_FRAME != 0) | ||
503 | no_packages+=1; | ||
504 | |||
505 | for (i = 0; i < no_packages ;i++) { | ||
506 | if (remaining > (HCI_COMPLETE_FRAME)) | ||
507 | package_length = HCI_COMPLETE_FRAME; | ||
508 | else | ||
509 | package_length = remaining; | ||
510 | remaining -= package_length; | ||
511 | |||
512 | serial_buf_put(priv->rx_buf, | ||
513 | urb->transfer_buffer + shift + | ||
514 | (HCI_COMPLETE_FRAME) * (i), | ||
515 | package_length - shift); | ||
516 | } | ||
517 | } | ||
518 | aircable_read(port); | ||
519 | } | ||
520 | |||
521 | /* Schedule the next read _if_ we are still open */ | ||
522 | if (port->open_count) { | ||
523 | usb_fill_bulk_urb(port->read_urb, port->serial->dev, | ||
524 | usb_rcvbulkpipe(port->serial->dev, | ||
525 | port->bulk_in_endpointAddress), | ||
526 | port->read_urb->transfer_buffer, | ||
527 | port->read_urb->transfer_buffer_length, | ||
528 | aircable_read_bulk_callback, port); | ||
529 | |||
530 | result = usb_submit_urb(urb, GFP_ATOMIC); | ||
531 | if (result) | ||
532 | dev_err(&urb->dev->dev, | ||
533 | "%s - failed resubmitting read urb, error %d\n", | ||
534 | __FUNCTION__, result); | ||
535 | } | ||
536 | |||
537 | return; | ||
538 | } | ||
539 | |||
540 | /* Based on ftdi_sio.c throttle */ | ||
541 | static void aircable_throttle(struct usb_serial_port *port) | ||
542 | { | ||
543 | struct aircable_private *priv = usb_get_serial_port_data(port); | ||
544 | unsigned long flags; | ||
545 | |||
546 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
547 | |||
548 | spin_lock_irqsave(&priv->rx_lock, flags); | ||
549 | priv->rx_flags |= THROTTLED; | ||
550 | spin_unlock_irqrestore(&priv->rx_lock, flags); | ||
551 | } | ||
552 | |||
553 | /* Based on ftdi_sio.c unthrottle */ | ||
554 | static void aircable_unthrottle(struct usb_serial_port *port) | ||
555 | { | ||
556 | struct aircable_private *priv = usb_get_serial_port_data(port); | ||
557 | int actually_throttled; | ||
558 | unsigned long flags; | ||
559 | |||
560 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
561 | |||
562 | spin_lock_irqsave(&priv->rx_lock, flags); | ||
563 | actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; | ||
564 | priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); | ||
565 | spin_unlock_irqrestore(&priv->rx_lock, flags); | ||
566 | |||
567 | if (actually_throttled) | ||
568 | schedule_work(&priv->rx_work); | ||
569 | } | ||
570 | |||
571 | static struct usb_serial_driver aircable_device = { | ||
572 | .description = "aircable", | ||
573 | .id_table = id_table, | ||
574 | .num_ports = 1, | ||
575 | .attach = aircable_attach, | ||
576 | .probe = aircable_probe, | ||
577 | .shutdown = aircable_shutdown, | ||
578 | .write = aircable_write, | ||
579 | .write_room = aircable_write_room, | ||
580 | .write_bulk_callback = aircable_write_bulk_callback, | ||
581 | .read_bulk_callback = aircable_read_bulk_callback, | ||
582 | .throttle = aircable_throttle, | ||
583 | .unthrottle = aircable_unthrottle, | ||
584 | }; | ||
585 | |||
586 | static struct usb_driver aircable_driver = { | ||
587 | .name = "aircable", | ||
588 | .probe = usb_serial_probe, | ||
589 | .disconnect = usb_serial_disconnect, | ||
590 | .id_table = id_table, | ||
591 | }; | ||
592 | |||
593 | static int __init aircable_init (void) | ||
594 | { | ||
595 | int retval; | ||
596 | retval = usb_serial_register(&aircable_device); | ||
597 | if (retval) | ||
598 | goto failed_serial_register; | ||
599 | retval = usb_register(&aircable_driver); | ||
600 | if (retval) | ||
601 | goto failed_usb_register; | ||
602 | return 0; | ||
603 | |||
604 | failed_serial_register: | ||
605 | usb_serial_deregister(&aircable_device); | ||
606 | failed_usb_register: | ||
607 | return retval; | ||
608 | } | ||
609 | |||
610 | static void __exit aircable_exit (void) | ||
611 | { | ||
612 | usb_deregister(&aircable_driver); | ||
613 | usb_serial_deregister(&aircable_device); | ||
614 | } | ||
615 | |||
616 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
617 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
618 | MODULE_VERSION(DRIVER_VERSION); | ||
619 | MODULE_LICENSE("GPL"); | ||
620 | |||
621 | module_init(aircable_init); | ||
622 | module_exit(aircable_exit); | ||
623 | |||
624 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
625 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | ||