diff options
author | Sean Young <sean@mess.org> | 2012-07-15 12:31:00 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-07-30 20:53:49 -0400 |
commit | 26ff63137c45886169ed102bddd6e90d6c27f00d (patch) | |
tree | 7da15018851c84db24bac85489a40923bdba4a3f /drivers/media/rc | |
parent | 6ac454aa98be6b9d5ead482263d37dd92cbcb0eb (diff) |
[media] Add support for the IguanaWorks USB IR Transceiver
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/rc')
-rw-r--r-- | drivers/media/rc/Kconfig | 11 | ||||
-rw-r--r-- | drivers/media/rc/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/rc/iguanair.c | 639 |
3 files changed, 651 insertions, 0 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 908ef70430e9..5180390be7ab 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig | |||
@@ -259,6 +259,17 @@ config IR_WINBOND_CIR | |||
259 | To compile this driver as a module, choose M here: the module will | 259 | To compile this driver as a module, choose M here: the module will |
260 | be called winbond_cir. | 260 | be called winbond_cir. |
261 | 261 | ||
262 | config IR_IGUANA | ||
263 | tristate "IguanaWorks USB IR Transceiver" | ||
264 | depends on RC_CORE | ||
265 | select USB | ||
266 | ---help--- | ||
267 | Say Y here if you want to use the IgaunaWorks USB IR Transceiver. | ||
268 | Both infrared receive and send are supported. | ||
269 | |||
270 | To compile this driver as a module, choose M here: the module will | ||
271 | be called iguanair. | ||
272 | |||
262 | config RC_LOOPBACK | 273 | config RC_LOOPBACK |
263 | tristate "Remote Control Loopback Driver" | 274 | tristate "Remote Control Loopback Driver" |
264 | depends on RC_CORE | 275 | depends on RC_CORE |
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 29f364f88a94..f871d1986c21 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile | |||
@@ -27,3 +27,4 @@ obj-$(CONFIG_IR_STREAMZAP) += streamzap.o | |||
27 | obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o | 27 | obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o |
28 | obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o | 28 | obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o |
29 | obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o | 29 | obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o |
30 | obj-$(CONFIG_IR_IGUANA) += iguanair.o | ||
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c new file mode 100644 index 000000000000..5e2eaf8ba73e --- /dev/null +++ b/drivers/media/rc/iguanair.c | |||
@@ -0,0 +1,639 @@ | |||
1 | /* | ||
2 | * IguanaWorks USB IR Transceiver support | ||
3 | * | ||
4 | * Copyright (C) 2012 Sean Young <sean@mess.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/device.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/usb.h> | ||
25 | #include <linux/usb/input.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/completion.h> | ||
28 | #include <media/rc-core.h> | ||
29 | |||
30 | #define DRIVER_NAME "iguanair" | ||
31 | |||
32 | struct iguanair { | ||
33 | struct rc_dev *rc; | ||
34 | |||
35 | struct device *dev; | ||
36 | struct usb_device *udev; | ||
37 | |||
38 | int pipe_in, pipe_out; | ||
39 | uint8_t bufsize; | ||
40 | uint8_t version[2]; | ||
41 | |||
42 | struct mutex lock; | ||
43 | |||
44 | /* receiver support */ | ||
45 | bool receiver_on; | ||
46 | dma_addr_t dma_in; | ||
47 | uint8_t *buf_in; | ||
48 | struct urb *urb_in; | ||
49 | struct completion completion; | ||
50 | |||
51 | /* transmit support */ | ||
52 | bool tx_overflow; | ||
53 | uint32_t carrier; | ||
54 | uint8_t cycle_overhead; | ||
55 | uint8_t channels; | ||
56 | uint8_t busy4; | ||
57 | uint8_t busy7; | ||
58 | |||
59 | char name[64]; | ||
60 | char phys[64]; | ||
61 | }; | ||
62 | |||
63 | #define CMD_GET_VERSION 0x01 | ||
64 | #define CMD_GET_BUFSIZE 0x11 | ||
65 | #define CMD_GET_FEATURES 0x10 | ||
66 | #define CMD_SEND 0x15 | ||
67 | #define CMD_EXECUTE 0x1f | ||
68 | #define CMD_RX_OVERFLOW 0x31 | ||
69 | #define CMD_TX_OVERFLOW 0x32 | ||
70 | #define CMD_RECEIVER_ON 0x12 | ||
71 | #define CMD_RECEIVER_OFF 0x14 | ||
72 | |||
73 | #define DIR_IN 0xdc | ||
74 | #define DIR_OUT 0xcd | ||
75 | |||
76 | #define MAX_PACKET_SIZE 8u | ||
77 | #define TIMEOUT 1000 | ||
78 | |||
79 | struct packet { | ||
80 | uint16_t start; | ||
81 | uint8_t direction; | ||
82 | uint8_t cmd; | ||
83 | }; | ||
84 | |||
85 | struct response_packet { | ||
86 | struct packet header; | ||
87 | uint8_t data[4]; | ||
88 | }; | ||
89 | |||
90 | struct send_packet { | ||
91 | struct packet header; | ||
92 | uint8_t length; | ||
93 | uint8_t channels; | ||
94 | uint8_t busy7; | ||
95 | uint8_t busy4; | ||
96 | uint8_t payload[0]; | ||
97 | }; | ||
98 | |||
99 | static void process_ir_data(struct iguanair *ir, unsigned len) | ||
100 | { | ||
101 | if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) { | ||
102 | switch (ir->buf_in[3]) { | ||
103 | case CMD_TX_OVERFLOW: | ||
104 | ir->tx_overflow = true; | ||
105 | case CMD_RECEIVER_OFF: | ||
106 | case CMD_RECEIVER_ON: | ||
107 | case CMD_SEND: | ||
108 | complete(&ir->completion); | ||
109 | break; | ||
110 | case CMD_RX_OVERFLOW: | ||
111 | dev_warn(ir->dev, "receive overflow\n"); | ||
112 | break; | ||
113 | default: | ||
114 | dev_warn(ir->dev, "control code %02x received\n", | ||
115 | ir->buf_in[3]); | ||
116 | break; | ||
117 | } | ||
118 | } else if (len >= 7) { | ||
119 | DEFINE_IR_RAW_EVENT(rawir); | ||
120 | unsigned i; | ||
121 | |||
122 | init_ir_raw_event(&rawir); | ||
123 | |||
124 | for (i = 0; i < 7; i++) { | ||
125 | if (ir->buf_in[i] == 0x80) { | ||
126 | rawir.pulse = false; | ||
127 | rawir.duration = US_TO_NS(21845); | ||
128 | } else { | ||
129 | rawir.pulse = (ir->buf_in[i] & 0x80) == 0; | ||
130 | rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) * | ||
131 | 21330; | ||
132 | } | ||
133 | |||
134 | ir_raw_event_store_with_filter(ir->rc, &rawir); | ||
135 | } | ||
136 | |||
137 | ir_raw_event_handle(ir->rc); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static void iguanair_rx(struct urb *urb) | ||
142 | { | ||
143 | struct iguanair *ir; | ||
144 | |||
145 | if (!urb) | ||
146 | return; | ||
147 | |||
148 | ir = urb->context; | ||
149 | if (!ir) { | ||
150 | usb_unlink_urb(urb); | ||
151 | return; | ||
152 | } | ||
153 | |||
154 | switch (urb->status) { | ||
155 | case 0: | ||
156 | process_ir_data(ir, urb->actual_length); | ||
157 | break; | ||
158 | case -ECONNRESET: | ||
159 | case -ENOENT: | ||
160 | case -ESHUTDOWN: | ||
161 | usb_unlink_urb(urb); | ||
162 | return; | ||
163 | case -EPIPE: | ||
164 | default: | ||
165 | dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status); | ||
166 | break; | ||
167 | } | ||
168 | |||
169 | usb_submit_urb(urb, GFP_ATOMIC); | ||
170 | } | ||
171 | |||
172 | static int iguanair_send(struct iguanair *ir, void *data, unsigned size, | ||
173 | struct response_packet *response, unsigned *res_len) | ||
174 | { | ||
175 | unsigned offset, len; | ||
176 | int rc, transferred; | ||
177 | |||
178 | for (offset = 0; offset < size; offset += MAX_PACKET_SIZE) { | ||
179 | len = min(size - offset, MAX_PACKET_SIZE); | ||
180 | |||
181 | if (ir->tx_overflow) | ||
182 | return -EOVERFLOW; | ||
183 | |||
184 | rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data + offset, | ||
185 | len, &transferred, TIMEOUT); | ||
186 | if (rc) | ||
187 | return rc; | ||
188 | |||
189 | if (transferred != len) | ||
190 | return -EIO; | ||
191 | } | ||
192 | |||
193 | if (response) { | ||
194 | rc = usb_interrupt_msg(ir->udev, ir->pipe_in, response, | ||
195 | sizeof(*response), res_len, TIMEOUT); | ||
196 | } | ||
197 | |||
198 | return rc; | ||
199 | } | ||
200 | |||
201 | static int iguanair_get_features(struct iguanair *ir) | ||
202 | { | ||
203 | struct packet packet; | ||
204 | struct response_packet response; | ||
205 | int rc, len; | ||
206 | |||
207 | packet.start = 0; | ||
208 | packet.direction = DIR_OUT; | ||
209 | packet.cmd = CMD_GET_VERSION; | ||
210 | |||
211 | rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len); | ||
212 | if (rc) { | ||
213 | dev_info(ir->dev, "failed to get version\n"); | ||
214 | goto out; | ||
215 | } | ||
216 | |||
217 | if (len != 6) { | ||
218 | dev_info(ir->dev, "failed to get version\n"); | ||
219 | rc = -EIO; | ||
220 | goto out; | ||
221 | } | ||
222 | |||
223 | ir->version[0] = response.data[0]; | ||
224 | ir->version[1] = response.data[1]; | ||
225 | ir->bufsize = 150; | ||
226 | ir->cycle_overhead = 65; | ||
227 | |||
228 | packet.cmd = CMD_GET_BUFSIZE; | ||
229 | |||
230 | rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len); | ||
231 | if (rc) { | ||
232 | dev_info(ir->dev, "failed to get buffer size\n"); | ||
233 | goto out; | ||
234 | } | ||
235 | |||
236 | if (len != 5) { | ||
237 | dev_info(ir->dev, "failed to get buffer size\n"); | ||
238 | rc = -EIO; | ||
239 | goto out; | ||
240 | } | ||
241 | |||
242 | ir->bufsize = response.data[0]; | ||
243 | |||
244 | if (ir->version[0] == 0 || ir->version[1] == 0) | ||
245 | goto out; | ||
246 | |||
247 | packet.cmd = CMD_GET_FEATURES; | ||
248 | |||
249 | rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len); | ||
250 | if (rc) { | ||
251 | dev_info(ir->dev, "failed to get features\n"); | ||
252 | goto out; | ||
253 | } | ||
254 | |||
255 | if (len < 5) { | ||
256 | dev_info(ir->dev, "failed to get features\n"); | ||
257 | rc = -EIO; | ||
258 | goto out; | ||
259 | } | ||
260 | |||
261 | if (len > 5 && ir->version[0] >= 4) | ||
262 | ir->cycle_overhead = response.data[1]; | ||
263 | |||
264 | out: | ||
265 | return rc; | ||
266 | } | ||
267 | |||
268 | static int iguanair_receiver(struct iguanair *ir, bool enable) | ||
269 | { | ||
270 | struct packet packet = { 0, DIR_OUT, enable ? | ||
271 | CMD_RECEIVER_ON : CMD_RECEIVER_OFF }; | ||
272 | int rc; | ||
273 | |||
274 | INIT_COMPLETION(ir->completion); | ||
275 | |||
276 | rc = iguanair_send(ir, &packet, sizeof(packet), NULL, NULL); | ||
277 | if (rc) | ||
278 | return rc; | ||
279 | |||
280 | wait_for_completion_timeout(&ir->completion, TIMEOUT); | ||
281 | |||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | /* | ||
286 | * The iguana ir creates the carrier by busy spinning after each pulse or | ||
287 | * space. This is counted in CPU cycles, with the CPU running at 24MHz. It is | ||
288 | * broken down into 7-cycles and 4-cyles delays, with a preference for | ||
289 | * 4-cycle delays. | ||
290 | */ | ||
291 | static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier) | ||
292 | { | ||
293 | struct iguanair *ir = dev->priv; | ||
294 | |||
295 | if (carrier < 25000 || carrier > 150000) | ||
296 | return -EINVAL; | ||
297 | |||
298 | mutex_lock(&ir->lock); | ||
299 | |||
300 | if (carrier != ir->carrier) { | ||
301 | uint32_t cycles, fours, sevens; | ||
302 | |||
303 | ir->carrier = carrier; | ||
304 | |||
305 | cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) - | ||
306 | ir->cycle_overhead; | ||
307 | |||
308 | /* make up the the remainer of 4-cycle blocks */ | ||
309 | switch (cycles & 3) { | ||
310 | case 0: | ||
311 | sevens = 0; | ||
312 | break; | ||
313 | case 1: | ||
314 | sevens = 3; | ||
315 | break; | ||
316 | case 2: | ||
317 | sevens = 2; | ||
318 | break; | ||
319 | case 3: | ||
320 | sevens = 1; | ||
321 | break; | ||
322 | } | ||
323 | |||
324 | fours = (cycles - sevens * 7) / 4; | ||
325 | |||
326 | /* magic happens here */ | ||
327 | ir->busy7 = (4 - sevens) * 2; | ||
328 | ir->busy4 = 110 - fours; | ||
329 | } | ||
330 | |||
331 | mutex_unlock(&ir->lock); | ||
332 | |||
333 | return carrier; | ||
334 | } | ||
335 | |||
336 | static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask) | ||
337 | { | ||
338 | struct iguanair *ir = dev->priv; | ||
339 | |||
340 | if (mask > 15) | ||
341 | return 4; | ||
342 | |||
343 | mutex_lock(&ir->lock); | ||
344 | ir->channels = mask; | ||
345 | mutex_unlock(&ir->lock); | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count) | ||
351 | { | ||
352 | struct iguanair *ir = dev->priv; | ||
353 | uint8_t space, *payload; | ||
354 | unsigned i, size, rc; | ||
355 | struct send_packet *packet; | ||
356 | |||
357 | mutex_lock(&ir->lock); | ||
358 | |||
359 | /* convert from us to carrier periods */ | ||
360 | for (i = size = 0; i < count; i++) { | ||
361 | txbuf[i] = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000); | ||
362 | size += (txbuf[i] + 126) / 127; | ||
363 | } | ||
364 | |||
365 | packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL); | ||
366 | if (!packet) { | ||
367 | rc = -ENOMEM; | ||
368 | goto out; | ||
369 | } | ||
370 | |||
371 | if (size > ir->bufsize) { | ||
372 | rc = -E2BIG; | ||
373 | goto out; | ||
374 | } | ||
375 | |||
376 | packet->header.start = 0; | ||
377 | packet->header.direction = DIR_OUT; | ||
378 | packet->header.cmd = CMD_SEND; | ||
379 | packet->length = size; | ||
380 | packet->channels = ir->channels << 4; | ||
381 | packet->busy7 = ir->busy7; | ||
382 | packet->busy4 = ir->busy4; | ||
383 | |||
384 | space = 0; | ||
385 | payload = packet->payload; | ||
386 | |||
387 | for (i = 0; i < count; i++) { | ||
388 | unsigned periods = txbuf[i]; | ||
389 | |||
390 | while (periods > 127) { | ||
391 | *payload++ = 127 | space; | ||
392 | periods -= 127; | ||
393 | } | ||
394 | |||
395 | *payload++ = periods | space; | ||
396 | space ^= 0x80; | ||
397 | } | ||
398 | |||
399 | if (ir->receiver_on) { | ||
400 | rc = iguanair_receiver(ir, false); | ||
401 | if (rc) { | ||
402 | dev_warn(ir->dev, "disable receiver before transmit failed\n"); | ||
403 | goto out; | ||
404 | } | ||
405 | } | ||
406 | |||
407 | ir->tx_overflow = false; | ||
408 | |||
409 | INIT_COMPLETION(ir->completion); | ||
410 | |||
411 | rc = iguanair_send(ir, packet, size + 8, NULL, NULL); | ||
412 | |||
413 | if (rc == 0) { | ||
414 | wait_for_completion_timeout(&ir->completion, TIMEOUT); | ||
415 | if (ir->tx_overflow) | ||
416 | rc = -EOVERFLOW; | ||
417 | } | ||
418 | |||
419 | ir->tx_overflow = false; | ||
420 | |||
421 | if (ir->receiver_on) { | ||
422 | if (iguanair_receiver(ir, true)) | ||
423 | dev_warn(ir->dev, "re-enable receiver after transmit failed\n"); | ||
424 | } | ||
425 | |||
426 | out: | ||
427 | mutex_unlock(&ir->lock); | ||
428 | kfree(packet); | ||
429 | |||
430 | return rc; | ||
431 | } | ||
432 | |||
433 | static int iguanair_open(struct rc_dev *rdev) | ||
434 | { | ||
435 | struct iguanair *ir = rdev->priv; | ||
436 | int rc; | ||
437 | |||
438 | mutex_lock(&ir->lock); | ||
439 | |||
440 | usb_submit_urb(ir->urb_in, GFP_KERNEL); | ||
441 | |||
442 | BUG_ON(ir->receiver_on); | ||
443 | |||
444 | rc = iguanair_receiver(ir, true); | ||
445 | if (rc == 0) | ||
446 | ir->receiver_on = true; | ||
447 | |||
448 | mutex_unlock(&ir->lock); | ||
449 | |||
450 | return rc; | ||
451 | } | ||
452 | |||
453 | static void iguanair_close(struct rc_dev *rdev) | ||
454 | { | ||
455 | struct iguanair *ir = rdev->priv; | ||
456 | int rc; | ||
457 | |||
458 | mutex_lock(&ir->lock); | ||
459 | |||
460 | rc = iguanair_receiver(ir, false); | ||
461 | ir->receiver_on = false; | ||
462 | if (rc) | ||
463 | dev_warn(ir->dev, "failed to disable receiver: %d\n", rc); | ||
464 | |||
465 | usb_kill_urb(ir->urb_in); | ||
466 | |||
467 | mutex_unlock(&ir->lock); | ||
468 | } | ||
469 | |||
470 | static int __devinit iguanair_probe(struct usb_interface *intf, | ||
471 | const struct usb_device_id *id) | ||
472 | { | ||
473 | struct usb_device *udev = interface_to_usbdev(intf); | ||
474 | struct iguanair *ir; | ||
475 | struct rc_dev *rc; | ||
476 | int ret; | ||
477 | struct usb_host_interface *idesc; | ||
478 | |||
479 | ir = kzalloc(sizeof(*ir), GFP_KERNEL); | ||
480 | rc = rc_allocate_device(); | ||
481 | if (!ir || !rc) { | ||
482 | ret = ENOMEM; | ||
483 | goto out; | ||
484 | } | ||
485 | |||
486 | ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_ATOMIC, | ||
487 | &ir->dma_in); | ||
488 | ir->urb_in = usb_alloc_urb(0, GFP_KERNEL); | ||
489 | |||
490 | if (!ir->buf_in || !ir->urb_in) { | ||
491 | ret = ENOMEM; | ||
492 | goto out; | ||
493 | } | ||
494 | |||
495 | idesc = intf->altsetting; | ||
496 | |||
497 | if (idesc->desc.bNumEndpoints < 2) { | ||
498 | ret = -ENODEV; | ||
499 | goto out; | ||
500 | } | ||
501 | |||
502 | ir->rc = rc; | ||
503 | ir->dev = &intf->dev; | ||
504 | ir->udev = udev; | ||
505 | ir->pipe_in = usb_rcvintpipe(udev, | ||
506 | idesc->endpoint[0].desc.bEndpointAddress); | ||
507 | ir->pipe_out = usb_sndintpipe(udev, | ||
508 | idesc->endpoint[1].desc.bEndpointAddress); | ||
509 | mutex_init(&ir->lock); | ||
510 | init_completion(&ir->completion); | ||
511 | |||
512 | ret = iguanair_get_features(ir); | ||
513 | if (ret) { | ||
514 | dev_warn(&intf->dev, "failed to get device features"); | ||
515 | goto out; | ||
516 | } | ||
517 | |||
518 | usb_fill_int_urb(ir->urb_in, ir->udev, ir->pipe_in, ir->buf_in, | ||
519 | MAX_PACKET_SIZE, iguanair_rx, ir, | ||
520 | idesc->endpoint[0].desc.bInterval); | ||
521 | ir->urb_in->transfer_dma = ir->dma_in; | ||
522 | ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | ||
523 | |||
524 | snprintf(ir->name, sizeof(ir->name), | ||
525 | "IguanaWorks USB IR Transceiver version %d.%d", | ||
526 | ir->version[0], ir->version[1]); | ||
527 | |||
528 | usb_make_path(ir->udev, ir->phys, sizeof(ir->phys)); | ||
529 | |||
530 | rc->input_name = ir->name; | ||
531 | rc->input_phys = ir->phys; | ||
532 | usb_to_input_id(ir->udev, &rc->input_id); | ||
533 | rc->dev.parent = &intf->dev; | ||
534 | rc->driver_type = RC_DRIVER_IR_RAW; | ||
535 | rc->allowed_protos = RC_TYPE_ALL; | ||
536 | rc->priv = ir; | ||
537 | rc->open = iguanair_open; | ||
538 | rc->close = iguanair_close; | ||
539 | rc->s_tx_mask = iguanair_set_tx_mask; | ||
540 | rc->s_tx_carrier = iguanair_set_tx_carrier; | ||
541 | rc->tx_ir = iguanair_tx; | ||
542 | rc->driver_name = DRIVER_NAME; | ||
543 | rc->map_name = RC_MAP_EMPTY; | ||
544 | |||
545 | iguanair_set_tx_carrier(rc, 38000); | ||
546 | |||
547 | ret = rc_register_device(rc); | ||
548 | if (ret < 0) { | ||
549 | dev_err(&intf->dev, "failed to register rc device %d", ret); | ||
550 | goto out; | ||
551 | } | ||
552 | |||
553 | usb_set_intfdata(intf, ir); | ||
554 | |||
555 | dev_info(&intf->dev, "Registered %s", ir->name); | ||
556 | |||
557 | return 0; | ||
558 | out: | ||
559 | if (ir) { | ||
560 | usb_free_urb(ir->urb_in); | ||
561 | usb_free_coherent(udev, MAX_PACKET_SIZE, ir->buf_in, | ||
562 | ir->dma_in); | ||
563 | } | ||
564 | rc_free_device(rc); | ||
565 | kfree(ir); | ||
566 | return ret; | ||
567 | } | ||
568 | |||
569 | static void __devexit iguanair_disconnect(struct usb_interface *intf) | ||
570 | { | ||
571 | struct iguanair *ir = usb_get_intfdata(intf); | ||
572 | |||
573 | usb_set_intfdata(intf, NULL); | ||
574 | |||
575 | usb_kill_urb(ir->urb_in); | ||
576 | usb_free_urb(ir->urb_in); | ||
577 | usb_free_coherent(ir->udev, MAX_PACKET_SIZE, ir->buf_in, ir->dma_in); | ||
578 | rc_unregister_device(ir->rc); | ||
579 | kfree(ir); | ||
580 | } | ||
581 | |||
582 | static int iguanair_suspend(struct usb_interface *intf, pm_message_t message) | ||
583 | { | ||
584 | struct iguanair *ir = usb_get_intfdata(intf); | ||
585 | int rc = 0; | ||
586 | |||
587 | mutex_lock(&ir->lock); | ||
588 | |||
589 | if (ir->receiver_on) { | ||
590 | rc = iguanair_receiver(ir, false); | ||
591 | if (rc) | ||
592 | dev_warn(ir->dev, "failed to disable receiver for suspend\n"); | ||
593 | } | ||
594 | |||
595 | mutex_unlock(&ir->lock); | ||
596 | |||
597 | return rc; | ||
598 | } | ||
599 | |||
600 | static int iguanair_resume(struct usb_interface *intf) | ||
601 | { | ||
602 | struct iguanair *ir = usb_get_intfdata(intf); | ||
603 | int rc = 0; | ||
604 | |||
605 | mutex_lock(&ir->lock); | ||
606 | |||
607 | if (ir->receiver_on) { | ||
608 | rc = iguanair_receiver(ir, true); | ||
609 | if (rc) | ||
610 | dev_warn(ir->dev, "failed to enable receiver after resume\n"); | ||
611 | } | ||
612 | |||
613 | mutex_unlock(&ir->lock); | ||
614 | |||
615 | return rc; | ||
616 | } | ||
617 | |||
618 | static const struct usb_device_id iguanair_table[] = { | ||
619 | { USB_DEVICE(0x1781, 0x0938) }, | ||
620 | { } | ||
621 | }; | ||
622 | |||
623 | static struct usb_driver iguanair_driver = { | ||
624 | .name = DRIVER_NAME, | ||
625 | .probe = iguanair_probe, | ||
626 | .disconnect = __devexit_p(iguanair_disconnect), | ||
627 | .suspend = iguanair_suspend, | ||
628 | .resume = iguanair_resume, | ||
629 | .reset_resume = iguanair_resume, | ||
630 | .id_table = iguanair_table | ||
631 | }; | ||
632 | |||
633 | module_usb_driver(iguanair_driver); | ||
634 | |||
635 | MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver"); | ||
636 | MODULE_AUTHOR("Sean Young <sean@mess.org>"); | ||
637 | MODULE_LICENSE("GPL"); | ||
638 | MODULE_DEVICE_TABLE(usb, iguanair_table); | ||
639 | |||