Commit | Line | Data |
---|---|---|
3fe70ba2 MFN |
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. | |
beb7dd86 | 12 | * When writing the first urb must have a Header that starts with 0x20 0x29 the |
3fe70ba2 MFN |
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. | |
beb7dd86 | 21 | * When writing to the device the first two bytes of the header are 0x20 0x29 |
3fe70ba2 MFN |
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> | |
5a0e3ad6 | 46 | #include <linux/slab.h> |
3fe70ba2 MFN |
47 | #include <linux/tty_flip.h> |
48 | #include <linux/circ_buf.h> | |
49 | #include <linux/usb.h> | |
50 | #include <linux/usb/serial.h> | |
51 | ||
52 | static int debug; | |
53 | ||
54 | /* Vendor and Product ID */ | |
55 | #define AIRCABLE_VID 0x16CA | |
56 | #define AIRCABLE_USB_PID 0x1502 | |
57 | ||
58 | /* write buffer size defines */ | |
59 | #define AIRCABLE_BUF_SIZE 2048 | |
60 | ||
61 | /* Protocol Stuff */ | |
62 | #define HCI_HEADER_LENGTH 0x4 | |
63 | #define TX_HEADER_0 0x20 | |
64 | #define TX_HEADER_1 0x29 | |
65 | #define RX_HEADER_0 0x00 | |
66 | #define RX_HEADER_1 0x20 | |
67 | #define MAX_HCI_FRAMESIZE 60 | |
68 | #define HCI_COMPLETE_FRAME 64 | |
69 | ||
70 | /* rx_flags */ | |
71 | #define THROTTLED 0x01 | |
72 | #define ACTUALLY_THROTTLED 0x02 | |
73 | ||
74 | /* | |
75 | * Version Information | |
76 | */ | |
77 | #define DRIVER_VERSION "v1.0b2" | |
78 | #define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>" | |
79 | #define DRIVER_DESC "AIRcable USB Driver" | |
80 | ||
81 | /* ID table that will be registered with USB core */ | |
7d40d7e8 | 82 | static const struct usb_device_id id_table[] = { |
3fe70ba2 MFN |
83 | { USB_DEVICE(AIRCABLE_VID, AIRCABLE_USB_PID) }, |
84 | { }, | |
85 | }; | |
86 | MODULE_DEVICE_TABLE(usb, id_table); | |
87 | ||
88 | ||
89 | /* Internal Structure */ | |
90 | struct aircable_private { | |
91 | spinlock_t rx_lock; /* spinlock for the receive lines */ | |
92 | struct circ_buf *tx_buf; /* write buffer */ | |
93 | struct circ_buf *rx_buf; /* read buffer */ | |
94 | int rx_flags; /* for throttilng */ | |
95 | struct work_struct rx_work; /* work cue for the receiving line */ | |
c4028958 | 96 | struct usb_serial_port *port; /* USB port with which associated */ |
3fe70ba2 MFN |
97 | }; |
98 | ||
99 | /* Private methods */ | |
100 | ||
101 | /* Circular Buffer Methods, code from ti_usb_3410_5052 used */ | |
102 | /* | |
103 | * serial_buf_clear | |
104 | * | |
105 | * Clear out all data in the circular buffer. | |
106 | */ | |
107 | static void serial_buf_clear(struct circ_buf *cb) | |
108 | { | |
109 | cb->head = cb->tail = 0; | |
110 | } | |
111 | ||
112 | /* | |
113 | * serial_buf_alloc | |
114 | * | |
115 | * Allocate a circular buffer and all associated memory. | |
116 | */ | |
117 | static struct circ_buf *serial_buf_alloc(void) | |
118 | { | |
119 | struct circ_buf *cb; | |
120 | cb = kmalloc(sizeof(struct circ_buf), GFP_KERNEL); | |
121 | if (cb == NULL) | |
122 | return NULL; | |
123 | cb->buf = kmalloc(AIRCABLE_BUF_SIZE, GFP_KERNEL); | |
124 | if (cb->buf == NULL) { | |
125 | kfree(cb); | |
126 | return NULL; | |
127 | } | |
128 | serial_buf_clear(cb); | |
129 | return cb; | |
130 | } | |
131 | ||
132 | /* | |
133 | * serial_buf_free | |
134 | * | |
135 | * Free the buffer and all associated memory. | |
136 | */ | |
137 | static void serial_buf_free(struct circ_buf *cb) | |
138 | { | |
139 | kfree(cb->buf); | |
140 | kfree(cb); | |
141 | } | |
142 | ||
143 | /* | |
144 | * serial_buf_data_avail | |
145 | * | |
146 | * Return the number of bytes of data available in the circular | |
147 | * buffer. | |
148 | */ | |
149 | static int serial_buf_data_avail(struct circ_buf *cb) | |
150 | { | |
c4d0f8cb | 151 | return CIRC_CNT(cb->head, cb->tail, AIRCABLE_BUF_SIZE); |
3fe70ba2 MFN |
152 | } |
153 | ||
154 | /* | |
155 | * serial_buf_put | |
156 | * | |
157 | * Copy data data from a user buffer and put it into the circular buffer. | |
158 | * Restrict to the amount of space available. | |
159 | * | |
160 | * Return the number of bytes copied. | |
161 | */ | |
162 | static int serial_buf_put(struct circ_buf *cb, const char *buf, int count) | |
163 | { | |
164 | int c, ret = 0; | |
165 | while (1) { | |
166 | c = CIRC_SPACE_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE); | |
167 | if (count < c) | |
168 | c = count; | |
169 | if (c <= 0) | |
170 | break; | |
171 | memcpy(cb->buf + cb->head, buf, c); | |
172 | cb->head = (cb->head + c) & (AIRCABLE_BUF_SIZE-1); | |
173 | buf += c; | |
174 | count -= c; | |
c4d0f8cb | 175 | ret = c; |
3fe70ba2 MFN |
176 | } |
177 | return ret; | |
178 | } | |
179 | ||
180 | /* | |
181 | * serial_buf_get | |
182 | * | |
183 | * Get data from the circular buffer and copy to the given buffer. | |
184 | * Restrict to the amount of data available. | |
185 | * | |
186 | * Return the number of bytes copied. | |
187 | */ | |
188 | static int serial_buf_get(struct circ_buf *cb, char *buf, int count) | |
189 | { | |
190 | int c, ret = 0; | |
191 | while (1) { | |
192 | c = CIRC_CNT_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE); | |
193 | if (count < c) | |
194 | c = count; | |
195 | if (c <= 0) | |
196 | break; | |
197 | memcpy(buf, cb->buf + cb->tail, c); | |
198 | cb->tail = (cb->tail + c) & (AIRCABLE_BUF_SIZE-1); | |
199 | buf += c; | |
200 | count -= c; | |
c4d0f8cb | 201 | ret = c; |
3fe70ba2 MFN |
202 | } |
203 | return ret; | |
204 | } | |
205 | ||
206 | /* End of circula buffer methods */ | |
207 | ||
208 | static void aircable_send(struct usb_serial_port *port) | |
209 | { | |
210 | int count, result; | |
211 | struct aircable_private *priv = usb_get_serial_port_data(port); | |
c4d0f8cb | 212 | unsigned char *buf; |
fd05e720 | 213 | __le16 *dbuf; |
441b62c1 | 214 | dbg("%s - port %d", __func__, port->number); |
3fe70ba2 MFN |
215 | if (port->write_urb_busy) |
216 | return; | |
217 | ||
218 | count = min(serial_buf_data_avail(priv->tx_buf), MAX_HCI_FRAMESIZE); | |
219 | if (count == 0) | |
220 | return; | |
221 | ||
222 | buf = kzalloc(count + HCI_HEADER_LENGTH, GFP_ATOMIC); | |
223 | if (!buf) { | |
194343d9 GKH |
224 | dev_err(&port->dev, "%s- kzalloc(%d) failed.\n", |
225 | __func__, count + HCI_HEADER_LENGTH); | |
3fe70ba2 MFN |
226 | return; |
227 | } | |
228 | ||
229 | buf[0] = TX_HEADER_0; | |
230 | buf[1] = TX_HEADER_1; | |
fd05e720 | 231 | dbuf = (__le16 *)&buf[2]; |
b19d402a | 232 | *dbuf = cpu_to_le16((u16)count); |
c4d0f8cb AC |
233 | serial_buf_get(priv->tx_buf, buf + HCI_HEADER_LENGTH, |
234 | MAX_HCI_FRAMESIZE); | |
3fe70ba2 MFN |
235 | |
236 | memcpy(port->write_urb->transfer_buffer, buf, | |
237 | count + HCI_HEADER_LENGTH); | |
238 | ||
239 | kfree(buf); | |
240 | port->write_urb_busy = 1; | |
441b62c1 | 241 | usb_serial_debug_data(debug, &port->dev, __func__, |
3fe70ba2 MFN |
242 | count + HCI_HEADER_LENGTH, |
243 | port->write_urb->transfer_buffer); | |
244 | port->write_urb->transfer_buffer_length = count + HCI_HEADER_LENGTH; | |
245 | port->write_urb->dev = port->serial->dev; | |
246 | result = usb_submit_urb(port->write_urb, GFP_ATOMIC); | |
247 | ||
248 | if (result) { | |
249 | dev_err(&port->dev, | |
250 | "%s - failed submitting write urb, error %d\n", | |
441b62c1 | 251 | __func__, result); |
3fe70ba2 MFN |
252 | port->write_urb_busy = 0; |
253 | } | |
254 | ||
255 | schedule_work(&port->work); | |
256 | } | |
257 | ||
c4028958 | 258 | static void aircable_read(struct work_struct *work) |
3fe70ba2 | 259 | { |
c4028958 DH |
260 | struct aircable_private *priv = |
261 | container_of(work, struct aircable_private, rx_work); | |
262 | struct usb_serial_port *port = priv->port; | |
3fe70ba2 MFN |
263 | struct tty_struct *tty; |
264 | unsigned char *data; | |
265 | int count; | |
c4d0f8cb | 266 | if (priv->rx_flags & THROTTLED) { |
3fe70ba2 MFN |
267 | if (priv->rx_flags & ACTUALLY_THROTTLED) |
268 | schedule_work(&priv->rx_work); | |
269 | return; | |
270 | } | |
271 | ||
272 | /* By now I will flush data to the tty in packages of no more than | |
273 | * 64 bytes, to ensure I do not get throttled. | |
274 | * Ask USB mailing list for better aproach. | |
275 | */ | |
4a90f09b | 276 | tty = tty_port_tty_get(&port->port); |
3fe70ba2 | 277 | |
7a5c7b42 | 278 | if (!tty) { |
3fe70ba2 | 279 | schedule_work(&priv->rx_work); |
194343d9 | 280 | dev_err(&port->dev, "%s - No tty available\n", __func__); |
7a5c7b42 NMF |
281 | return ; |
282 | } | |
3fe70ba2 MFN |
283 | |
284 | count = min(64, serial_buf_data_avail(priv->rx_buf)); | |
285 | ||
286 | if (count <= 0) | |
4a90f09b | 287 | goto out; /* We have finished sending everything. */ |
3fe70ba2 MFN |
288 | |
289 | tty_prepare_flip_string(tty, &data, count); | |
c4d0f8cb | 290 | if (!data) { |
4a90f09b AC |
291 | dev_err(&port->dev, "%s- kzalloc(%d) failed.", |
292 | __func__, count); | |
293 | goto out; | |
3fe70ba2 MFN |
294 | } |
295 | ||
296 | serial_buf_get(priv->rx_buf, data, count); | |
297 | ||
298 | tty_flip_buffer_push(tty); | |
299 | ||
300 | if (serial_buf_data_avail(priv->rx_buf)) | |
301 | schedule_work(&priv->rx_work); | |
4a90f09b AC |
302 | out: |
303 | tty_kref_put(tty); | |
3fe70ba2 MFN |
304 | return; |
305 | } | |
306 | /* End of private methods */ | |
307 | ||
308 | static int aircable_probe(struct usb_serial *serial, | |
309 | const struct usb_device_id *id) | |
310 | { | |
c4d0f8cb AC |
311 | struct usb_host_interface *iface_desc = serial->interface-> |
312 | cur_altsetting; | |
3fe70ba2 | 313 | struct usb_endpoint_descriptor *endpoint; |
c4d0f8cb | 314 | int num_bulk_out = 0; |
3fe70ba2 MFN |
315 | int i; |
316 | ||
317 | for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { | |
318 | endpoint = &iface_desc->endpoint[i].desc; | |
377f13bf | 319 | if (usb_endpoint_is_bulk_out(endpoint)) { |
3fe70ba2 MFN |
320 | dbg("found bulk out on endpoint %d", i); |
321 | ++num_bulk_out; | |
322 | } | |
323 | } | |
324 | ||
325 | if (num_bulk_out == 0) { | |
326 | dbg("Invalid interface, discarding"); | |
327 | return -ENODEV; | |
328 | } | |
329 | ||
330 | return 0; | |
331 | } | |
332 | ||
c4d0f8cb | 333 | static int aircable_attach(struct usb_serial *serial) |
3fe70ba2 MFN |
334 | { |
335 | struct usb_serial_port *port = serial->port[0]; | |
336 | struct aircable_private *priv; | |
337 | ||
338 | priv = kzalloc(sizeof(struct aircable_private), GFP_KERNEL); | |
c4d0f8cb | 339 | if (!priv) { |
194343d9 | 340 | dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__, |
3fe70ba2 MFN |
341 | sizeof(struct aircable_private)); |
342 | return -ENOMEM; | |
343 | } | |
344 | ||
345 | /* Allocation of Circular Buffers */ | |
346 | priv->tx_buf = serial_buf_alloc(); | |
347 | if (priv->tx_buf == NULL) { | |
348 | kfree(priv); | |
349 | return -ENOMEM; | |
350 | } | |
351 | ||
352 | priv->rx_buf = serial_buf_alloc(); | |
353 | if (priv->rx_buf == NULL) { | |
354 | kfree(priv->tx_buf); | |
355 | kfree(priv); | |
356 | return -ENOMEM; | |
357 | } | |
358 | ||
359 | priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); | |
c4028958 DH |
360 | priv->port = port; |
361 | INIT_WORK(&priv->rx_work, aircable_read); | |
3fe70ba2 MFN |
362 | |
363 | usb_set_serial_port_data(serial->port[0], priv); | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
f9c99bb8 | 368 | static void aircable_release(struct usb_serial *serial) |
3fe70ba2 MFN |
369 | { |
370 | ||
371 | struct usb_serial_port *port = serial->port[0]; | |
372 | struct aircable_private *priv = usb_get_serial_port_data(port); | |
373 | ||
441b62c1 | 374 | dbg("%s", __func__); |
3fe70ba2 MFN |
375 | |
376 | if (priv) { | |
377 | serial_buf_free(priv->tx_buf); | |
378 | serial_buf_free(priv->rx_buf); | |
3fe70ba2 MFN |
379 | kfree(priv); |
380 | } | |
381 | } | |
382 | ||
95da310e | 383 | static int aircable_write_room(struct tty_struct *tty) |
3fe70ba2 | 384 | { |
95da310e | 385 | struct usb_serial_port *port = tty->driver_data; |
3fe70ba2 MFN |
386 | struct aircable_private *priv = usb_get_serial_port_data(port); |
387 | return serial_buf_data_avail(priv->tx_buf); | |
388 | } | |
389 | ||
95da310e | 390 | static int aircable_write(struct tty_struct *tty, struct usb_serial_port *port, |
3fe70ba2 MFN |
391 | const unsigned char *source, int count) |
392 | { | |
393 | struct aircable_private *priv = usb_get_serial_port_data(port); | |
394 | int temp; | |
395 | ||
441b62c1 | 396 | dbg("%s - port %d, %d bytes", __func__, port->number, count); |
3fe70ba2 | 397 | |
441b62c1 | 398 | usb_serial_debug_data(debug, &port->dev, __func__, count, source); |
3fe70ba2 | 399 | |
c4d0f8cb | 400 | if (!count) { |
441b62c1 | 401 | dbg("%s - write request of 0 bytes", __func__); |
3fe70ba2 MFN |
402 | return count; |
403 | } | |
404 | ||
405 | temp = serial_buf_put(priv->tx_buf, source, count); | |
406 | ||
407 | aircable_send(port); | |
408 | ||
409 | if (count > AIRCABLE_BUF_SIZE) | |
410 | count = AIRCABLE_BUF_SIZE; | |
411 | ||
412 | return count; | |
413 | ||
414 | } | |
415 | ||
7d12e780 | 416 | static void aircable_write_bulk_callback(struct urb *urb) |
3fe70ba2 MFN |
417 | { |
418 | struct usb_serial_port *port = urb->context; | |
1373dbbc | 419 | int status = urb->status; |
3fe70ba2 MFN |
420 | int result; |
421 | ||
441b62c1 | 422 | dbg("%s - urb status: %d", __func__ , status); |
3fe70ba2 MFN |
423 | |
424 | /* This has been taken from cypress_m8.c cypress_write_int_callback */ | |
1373dbbc | 425 | switch (status) { |
c4d0f8cb AC |
426 | case 0: |
427 | /* success */ | |
428 | break; | |
429 | case -ECONNRESET: | |
430 | case -ENOENT: | |
431 | case -ESHUTDOWN: | |
432 | /* this urb is terminated, clean up */ | |
433 | dbg("%s - urb shutting down with status: %d", | |
434 | __func__, status); | |
435 | port->write_urb_busy = 0; | |
436 | return; | |
437 | default: | |
438 | /* error in the urb, so we have to resubmit it */ | |
439 | dbg("%s - Overflow in write", __func__); | |
440 | dbg("%s - nonzero write bulk status received: %d", | |
441 | __func__, status); | |
442 | port->write_urb->transfer_buffer_length = 1; | |
443 | port->write_urb->dev = port->serial->dev; | |
444 | result = usb_submit_urb(port->write_urb, GFP_ATOMIC); | |
445 | if (result) | |
446 | dev_err(&urb->dev->dev, | |
447 | "%s - failed resubmitting write urb, error %d\n", | |
448 | __func__, result); | |
449 | else | |
3fe70ba2 | 450 | return; |
3fe70ba2 MFN |
451 | } |
452 | ||
453 | port->write_urb_busy = 0; | |
454 | ||
455 | aircable_send(port); | |
456 | } | |
457 | ||
7d12e780 | 458 | static void aircable_read_bulk_callback(struct urb *urb) |
3fe70ba2 MFN |
459 | { |
460 | struct usb_serial_port *port = urb->context; | |
461 | struct aircable_private *priv = usb_get_serial_port_data(port); | |
462 | struct tty_struct *tty; | |
463 | unsigned long no_packages, remaining, package_length, i; | |
464 | int result, shift = 0; | |
465 | unsigned char *temp; | |
1373dbbc | 466 | int status = urb->status; |
3fe70ba2 | 467 | |
441b62c1 | 468 | dbg("%s - port %d", __func__, port->number); |
3fe70ba2 | 469 | |
1373dbbc | 470 | if (status) { |
441b62c1 | 471 | dbg("%s - urb status = %d", __func__, status); |
1373dbbc | 472 | if (status == -EPROTO) { |
3fe70ba2 | 473 | dbg("%s - caught -EPROTO, resubmitting the urb", |
441b62c1 | 474 | __func__); |
3fe70ba2 | 475 | usb_fill_bulk_urb(port->read_urb, port->serial->dev, |
c4d0f8cb AC |
476 | usb_rcvbulkpipe(port->serial->dev, |
477 | port->bulk_in_endpointAddress), | |
478 | port->read_urb->transfer_buffer, | |
479 | port->read_urb->transfer_buffer_length, | |
480 | aircable_read_bulk_callback, port); | |
3fe70ba2 MFN |
481 | |
482 | result = usb_submit_urb(urb, GFP_ATOMIC); | |
483 | if (result) | |
484 | dev_err(&urb->dev->dev, | |
485 | "%s - failed resubmitting read urb, error %d\n", | |
441b62c1 | 486 | __func__, result); |
3fe70ba2 MFN |
487 | return; |
488 | } | |
441b62c1 | 489 | dbg("%s - unable to handle the error, exiting.", __func__); |
3fe70ba2 MFN |
490 | return; |
491 | } | |
492 | ||
441b62c1 | 493 | usb_serial_debug_data(debug, &port->dev, __func__, |
c4d0f8cb | 494 | urb->actual_length, urb->transfer_buffer); |
3fe70ba2 | 495 | |
4a90f09b | 496 | tty = tty_port_tty_get(&port->port); |
3fe70ba2 MFN |
497 | if (tty && urb->actual_length) { |
498 | if (urb->actual_length <= 2) { | |
499 | /* This is an incomplete package */ | |
500 | serial_buf_put(priv->rx_buf, urb->transfer_buffer, | |
501 | urb->actual_length); | |
502 | } else { | |
503 | temp = urb->transfer_buffer; | |
504 | if (temp[0] == RX_HEADER_0) | |
505 | shift = HCI_HEADER_LENGTH; | |
506 | ||
507 | remaining = urb->actual_length; | |
508 | no_packages = urb->actual_length / (HCI_COMPLETE_FRAME); | |
509 | ||
510 | if (urb->actual_length % HCI_COMPLETE_FRAME != 0) | |
c4d0f8cb | 511 | no_packages++; |
3fe70ba2 | 512 | |
c4d0f8cb | 513 | for (i = 0; i < no_packages; i++) { |
3fe70ba2 MFN |
514 | if (remaining > (HCI_COMPLETE_FRAME)) |
515 | package_length = HCI_COMPLETE_FRAME; | |
516 | else | |
517 | package_length = remaining; | |
518 | remaining -= package_length; | |
519 | ||
520 | serial_buf_put(priv->rx_buf, | |
521 | urb->transfer_buffer + shift + | |
522 | (HCI_COMPLETE_FRAME) * (i), | |
523 | package_length - shift); | |
524 | } | |
525 | } | |
c4028958 | 526 | aircable_read(&priv->rx_work); |
3fe70ba2 | 527 | } |
4a90f09b | 528 | tty_kref_put(tty); |
3fe70ba2 | 529 | |
1f87158e AS |
530 | /* Schedule the next read */ |
531 | usb_fill_bulk_urb(port->read_urb, port->serial->dev, | |
532 | usb_rcvbulkpipe(port->serial->dev, | |
533 | port->bulk_in_endpointAddress), | |
534 | port->read_urb->transfer_buffer, | |
535 | port->read_urb->transfer_buffer_length, | |
536 | aircable_read_bulk_callback, port); | |
537 | ||
538 | result = usb_submit_urb(urb, GFP_ATOMIC); | |
539 | if (result && result != -EPERM) | |
540 | dev_err(&urb->dev->dev, | |
541 | "%s - failed resubmitting read urb, error %d\n", | |
542 | __func__, result); | |
3fe70ba2 MFN |
543 | } |
544 | ||
545 | /* Based on ftdi_sio.c throttle */ | |
95da310e | 546 | static void aircable_throttle(struct tty_struct *tty) |
3fe70ba2 | 547 | { |
95da310e | 548 | struct usb_serial_port *port = tty->driver_data; |
3fe70ba2 | 549 | struct aircable_private *priv = usb_get_serial_port_data(port); |
3fe70ba2 | 550 | |
441b62c1 | 551 | dbg("%s - port %d", __func__, port->number); |
3fe70ba2 | 552 | |
63832515 | 553 | spin_lock_irq(&priv->rx_lock); |
3fe70ba2 | 554 | priv->rx_flags |= THROTTLED; |
63832515 | 555 | spin_unlock_irq(&priv->rx_lock); |
3fe70ba2 MFN |
556 | } |
557 | ||
558 | /* Based on ftdi_sio.c unthrottle */ | |
95da310e | 559 | static void aircable_unthrottle(struct tty_struct *tty) |
3fe70ba2 | 560 | { |
95da310e | 561 | struct usb_serial_port *port = tty->driver_data; |
3fe70ba2 MFN |
562 | struct aircable_private *priv = usb_get_serial_port_data(port); |
563 | int actually_throttled; | |
3fe70ba2 | 564 | |
441b62c1 | 565 | dbg("%s - port %d", __func__, port->number); |
3fe70ba2 | 566 | |
63832515 | 567 | spin_lock_irq(&priv->rx_lock); |
3fe70ba2 MFN |
568 | actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED; |
569 | priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED); | |
63832515 | 570 | spin_unlock_irq(&priv->rx_lock); |
3fe70ba2 MFN |
571 | |
572 | if (actually_throttled) | |
573 | schedule_work(&priv->rx_work); | |
574 | } | |
575 | ||
d9b1b787 JH |
576 | static struct usb_driver aircable_driver = { |
577 | .name = "aircable", | |
578 | .probe = usb_serial_probe, | |
579 | .disconnect = usb_serial_disconnect, | |
580 | .id_table = id_table, | |
581 | .no_dynamic_id = 1, | |
582 | }; | |
583 | ||
3fe70ba2 | 584 | static struct usb_serial_driver aircable_device = { |
52d67f0b JH |
585 | .driver = { |
586 | .owner = THIS_MODULE, | |
587 | .name = "aircable", | |
588 | }, | |
d9b1b787 | 589 | .usb_driver = &aircable_driver, |
3fe70ba2 MFN |
590 | .id_table = id_table, |
591 | .num_ports = 1, | |
592 | .attach = aircable_attach, | |
593 | .probe = aircable_probe, | |
f9c99bb8 | 594 | .release = aircable_release, |
3fe70ba2 MFN |
595 | .write = aircable_write, |
596 | .write_room = aircable_write_room, | |
597 | .write_bulk_callback = aircable_write_bulk_callback, | |
598 | .read_bulk_callback = aircable_read_bulk_callback, | |
599 | .throttle = aircable_throttle, | |
600 | .unthrottle = aircable_unthrottle, | |
601 | }; | |
602 | ||
c4d0f8cb | 603 | static int __init aircable_init(void) |
3fe70ba2 MFN |
604 | { |
605 | int retval; | |
606 | retval = usb_serial_register(&aircable_device); | |
607 | if (retval) | |
608 | goto failed_serial_register; | |
609 | retval = usb_register(&aircable_driver); | |
610 | if (retval) | |
611 | goto failed_usb_register; | |
612 | return 0; | |
613 | ||
3fe70ba2 | 614 | failed_usb_register: |
78c8fb37 DY |
615 | usb_serial_deregister(&aircable_device); |
616 | failed_serial_register: | |
3fe70ba2 MFN |
617 | return retval; |
618 | } | |
619 | ||
c4d0f8cb | 620 | static void __exit aircable_exit(void) |
3fe70ba2 MFN |
621 | { |
622 | usb_deregister(&aircable_driver); | |
623 | usb_serial_deregister(&aircable_device); | |
624 | } | |
625 | ||
626 | MODULE_AUTHOR(DRIVER_AUTHOR); | |
627 | MODULE_DESCRIPTION(DRIVER_DESC); | |
628 | MODULE_VERSION(DRIVER_VERSION); | |
629 | MODULE_LICENSE("GPL"); | |
630 | ||
631 | module_init(aircable_init); | |
632 | module_exit(aircable_exit); | |
633 | ||
634 | module_param(debug, bool, S_IRUGO | S_IWUSR); | |
635 | MODULE_PARM_DESC(debug, "Debug enabled or not"); |