diff options
author | Jarod Wilson <jarod@redhat.com> | 2010-07-03 00:07:53 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-08-02 14:16:20 -0400 |
commit | ca4146985db7cbb97816e9b961b8db79e63d9e86 (patch) | |
tree | dd44b6e835a4f381acc949226277fe4de91c4219 /drivers/media/IR | |
parent | 4a62a5ab59742331a4e17ccaa894968d40ed9b16 (diff) |
V4L/DVB: IR: add ir-core to lirc userspace decoder bridge driver
v2: copy of buffer data from userspace done inside this plugin/driver,
keeping the actual drivers minimal, and more flexible in what we can
deliver to them later on (they may be fed from within kernelspace later
on, by an in-kernel IR encoder).
Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/IR')
-rw-r--r-- | drivers/media/IR/Kconfig | 10 | ||||
-rw-r--r-- | drivers/media/IR/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/IR/ir-core-priv.h | 13 | ||||
-rw-r--r-- | drivers/media/IR/ir-lirc-codec.c | 284 | ||||
-rw-r--r-- | drivers/media/IR/ir-raw-event.c | 1 | ||||
-rw-r--r-- | drivers/media/IR/ir-sysfs.c | 1 |
6 files changed, 310 insertions, 0 deletions
diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig index 5d2c37dcf11a..40094c007acf 100644 --- a/drivers/media/IR/Kconfig +++ b/drivers/media/IR/Kconfig | |||
@@ -69,6 +69,16 @@ config IR_SONY_DECODER | |||
69 | Enable this option if you have an infrared remote control which | 69 | Enable this option if you have an infrared remote control which |
70 | uses the Sony protocol, and you need software decoding support. | 70 | uses the Sony protocol, and you need software decoding support. |
71 | 71 | ||
72 | config IR_LIRC_CODEC | ||
73 | tristate "Enable IR to LIRC bridge" | ||
74 | depends on IR_CORE | ||
75 | depends on LIRC | ||
76 | default y | ||
77 | |||
78 | ---help--- | ||
79 | Enable this option to pass raw IR to and from userspace via | ||
80 | the LIRC interface. | ||
81 | |||
72 | config IR_IMON | 82 | config IR_IMON |
73 | tristate "SoundGraph iMON Receiver and Display" | 83 | tristate "SoundGraph iMON Receiver and Display" |
74 | depends on USB_ARCH_HAS_HCD | 84 | depends on USB_ARCH_HAS_HCD |
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile index 3ba00bb8bea0..2ae4f3abfdbd 100644 --- a/drivers/media/IR/Makefile +++ b/drivers/media/IR/Makefile | |||
@@ -11,6 +11,7 @@ obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o | |||
11 | obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o | 11 | obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o |
12 | obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o | 12 | obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o |
13 | obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o | 13 | obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o |
14 | obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o | ||
14 | 15 | ||
15 | # stand-alone IR receivers/transmitters | 16 | # stand-alone IR receivers/transmitters |
16 | obj-$(CONFIG_IR_IMON) += imon.o | 17 | obj-$(CONFIG_IR_IMON) += imon.o |
diff --git a/drivers/media/IR/ir-core-priv.h b/drivers/media/IR/ir-core-priv.h index 0a82b22d3828..babd52061bc3 100644 --- a/drivers/media/IR/ir-core-priv.h +++ b/drivers/media/IR/ir-core-priv.h | |||
@@ -73,6 +73,11 @@ struct ir_raw_event_ctrl { | |||
73 | bool first; | 73 | bool first; |
74 | bool toggle; | 74 | bool toggle; |
75 | } jvc; | 75 | } jvc; |
76 | struct lirc_codec { | ||
77 | struct ir_input_dev *ir_dev; | ||
78 | struct lirc_driver *drv; | ||
79 | int lircdata; | ||
80 | } lirc; | ||
76 | }; | 81 | }; |
77 | 82 | ||
78 | /* macros for IR decoders */ | 83 | /* macros for IR decoders */ |
@@ -164,4 +169,12 @@ void ir_raw_init(void); | |||
164 | #define load_sony_decode() 0 | 169 | #define load_sony_decode() 0 |
165 | #endif | 170 | #endif |
166 | 171 | ||
172 | /* from ir-lirc-codec.c */ | ||
173 | #ifdef CONFIG_IR_LIRC_CODEC_MODULE | ||
174 | #define load_lirc_codec() request_module("ir-lirc-codec") | ||
175 | #else | ||
176 | #define load_lirc_codec() 0 | ||
177 | #endif | ||
178 | |||
179 | |||
167 | #endif /* _IR_RAW_EVENT */ | 180 | #endif /* _IR_RAW_EVENT */ |
diff --git a/drivers/media/IR/ir-lirc-codec.c b/drivers/media/IR/ir-lirc-codec.c new file mode 100644 index 000000000000..aff31d1b13d5 --- /dev/null +++ b/drivers/media/IR/ir-lirc-codec.c | |||
@@ -0,0 +1,284 @@ | |||
1 | /* ir-lirc-codec.c - ir-core to classic lirc interface bridge | ||
2 | * | ||
3 | * Copyright (C) 2010 by Jarod Wilson <jarod@redhat.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation version 2 of the License. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/sched.h> | ||
16 | #include <linux/wait.h> | ||
17 | #include <media/lirc.h> | ||
18 | #include <media/ir-core.h> | ||
19 | #include "ir-core-priv.h" | ||
20 | #include "lirc_dev.h" | ||
21 | |||
22 | #define LIRCBUF_SIZE 256 | ||
23 | |||
24 | /** | ||
25 | * ir_lirc_decode() - Send raw IR data to lirc_dev to be relayed to the | ||
26 | * lircd userspace daemon for decoding. | ||
27 | * @input_dev: the struct input_dev descriptor of the device | ||
28 | * @duration: the struct ir_raw_event descriptor of the pulse/space | ||
29 | * | ||
30 | * This function returns -EINVAL if the lirc interfaces aren't wired up. | ||
31 | */ | ||
32 | static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev) | ||
33 | { | ||
34 | struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); | ||
35 | |||
36 | if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC)) | ||
37 | return 0; | ||
38 | |||
39 | if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf) | ||
40 | return -EINVAL; | ||
41 | |||
42 | IR_dprintk(2, "LIRC data transfer started (%uus %s)\n", | ||
43 | TO_US(ev.duration), TO_STR(ev.pulse)); | ||
44 | |||
45 | ir_dev->raw->lirc.lircdata += ev.duration / 1000; | ||
46 | if (ev.pulse) | ||
47 | ir_dev->raw->lirc.lircdata |= PULSE_BIT; | ||
48 | |||
49 | lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf, | ||
50 | (unsigned char *) &ir_dev->raw->lirc.lircdata); | ||
51 | wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll); | ||
52 | |||
53 | ir_dev->raw->lirc.lircdata = 0; | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf, | ||
59 | size_t n, loff_t *ppos) | ||
60 | { | ||
61 | struct lirc_codec *lirc; | ||
62 | struct ir_input_dev *ir_dev; | ||
63 | int *txbuf; /* buffer with values to transmit */ | ||
64 | int ret = 0, count; | ||
65 | |||
66 | lirc = lirc_get_pdata(file); | ||
67 | if (!lirc) | ||
68 | return -EFAULT; | ||
69 | |||
70 | if (n % sizeof(int)) | ||
71 | return -EINVAL; | ||
72 | |||
73 | count = n / sizeof(int); | ||
74 | if (count > LIRCBUF_SIZE || count % 2 == 0) | ||
75 | return -EINVAL; | ||
76 | |||
77 | txbuf = kzalloc(sizeof(int) * LIRCBUF_SIZE, GFP_KERNEL); | ||
78 | if (!txbuf) | ||
79 | return -ENOMEM; | ||
80 | |||
81 | if (copy_from_user(txbuf, buf, n)) { | ||
82 | ret = -EFAULT; | ||
83 | goto out; | ||
84 | } | ||
85 | |||
86 | ir_dev = lirc->ir_dev; | ||
87 | if (!ir_dev) { | ||
88 | ret = -EFAULT; | ||
89 | goto out; | ||
90 | } | ||
91 | |||
92 | if (ir_dev->props && ir_dev->props->tx_ir) | ||
93 | ret = ir_dev->props->tx_ir(ir_dev->props->priv, txbuf, (u32)n); | ||
94 | |||
95 | out: | ||
96 | kfree(txbuf); | ||
97 | return ret; | ||
98 | } | ||
99 | |||
100 | static int ir_lirc_ioctl(struct inode *node, struct file *filep, | ||
101 | unsigned int cmd, unsigned long arg) | ||
102 | { | ||
103 | struct lirc_codec *lirc; | ||
104 | struct ir_input_dev *ir_dev; | ||
105 | int ret = 0; | ||
106 | void *drv_data; | ||
107 | unsigned long val; | ||
108 | |||
109 | lirc = lirc_get_pdata(filep); | ||
110 | if (!lirc) | ||
111 | return -EFAULT; | ||
112 | |||
113 | ir_dev = lirc->ir_dev; | ||
114 | if (!ir_dev || !ir_dev->props || !ir_dev->props->priv) | ||
115 | return -EFAULT; | ||
116 | |||
117 | drv_data = ir_dev->props->priv; | ||
118 | |||
119 | switch (cmd) { | ||
120 | case LIRC_SET_TRANSMITTER_MASK: | ||
121 | ret = get_user(val, (unsigned long *)arg); | ||
122 | if (ret) | ||
123 | return ret; | ||
124 | |||
125 | if (ir_dev->props && ir_dev->props->s_tx_mask) | ||
126 | ret = ir_dev->props->s_tx_mask(drv_data, (u32)val); | ||
127 | else | ||
128 | return -EINVAL; | ||
129 | break; | ||
130 | |||
131 | case LIRC_SET_SEND_CARRIER: | ||
132 | ret = get_user(val, (unsigned long *)arg); | ||
133 | if (ret) | ||
134 | return ret; | ||
135 | |||
136 | if (ir_dev->props && ir_dev->props->s_tx_carrier) | ||
137 | ir_dev->props->s_tx_carrier(drv_data, (u32)val); | ||
138 | else | ||
139 | return -EINVAL; | ||
140 | break; | ||
141 | |||
142 | case LIRC_GET_SEND_MODE: | ||
143 | val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK; | ||
144 | ret = put_user(val, (unsigned long *)arg); | ||
145 | break; | ||
146 | |||
147 | case LIRC_SET_SEND_MODE: | ||
148 | ret = get_user(val, (unsigned long *)arg); | ||
149 | if (ret) | ||
150 | return ret; | ||
151 | |||
152 | if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK)) | ||
153 | return -EINVAL; | ||
154 | break; | ||
155 | |||
156 | default: | ||
157 | return lirc_dev_fop_ioctl(node, filep, cmd, arg); | ||
158 | } | ||
159 | |||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | static int ir_lirc_open(void *data) | ||
164 | { | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static void ir_lirc_close(void *data) | ||
169 | { | ||
170 | return; | ||
171 | } | ||
172 | |||
173 | static struct file_operations lirc_fops = { | ||
174 | .owner = THIS_MODULE, | ||
175 | .write = ir_lirc_transmit_ir, | ||
176 | .ioctl = ir_lirc_ioctl, | ||
177 | .read = lirc_dev_fop_read, | ||
178 | .poll = lirc_dev_fop_poll, | ||
179 | .open = lirc_dev_fop_open, | ||
180 | .release = lirc_dev_fop_close, | ||
181 | }; | ||
182 | |||
183 | static int ir_lirc_register(struct input_dev *input_dev) | ||
184 | { | ||
185 | struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); | ||
186 | struct lirc_driver *drv; | ||
187 | struct lirc_buffer *rbuf; | ||
188 | int rc = -ENOMEM; | ||
189 | unsigned long features; | ||
190 | |||
191 | drv = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); | ||
192 | if (!drv) | ||
193 | return rc; | ||
194 | |||
195 | rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL); | ||
196 | if (!drv) | ||
197 | goto rbuf_alloc_failed; | ||
198 | |||
199 | rc = lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE); | ||
200 | if (rc) | ||
201 | goto rbuf_init_failed; | ||
202 | |||
203 | features = LIRC_CAN_REC_MODE2; | ||
204 | if (ir_dev->props->tx_ir) { | ||
205 | features |= LIRC_CAN_SEND_PULSE; | ||
206 | if (ir_dev->props->s_tx_mask) | ||
207 | features |= LIRC_CAN_SET_TRANSMITTER_MASK; | ||
208 | if (ir_dev->props->s_tx_carrier) | ||
209 | features |= LIRC_CAN_SET_SEND_CARRIER; | ||
210 | } | ||
211 | |||
212 | snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)", | ||
213 | ir_dev->driver_name); | ||
214 | drv->minor = -1; | ||
215 | drv->features = features; | ||
216 | drv->data = &ir_dev->raw->lirc; | ||
217 | drv->rbuf = rbuf; | ||
218 | drv->set_use_inc = &ir_lirc_open; | ||
219 | drv->set_use_dec = &ir_lirc_close; | ||
220 | drv->code_length = sizeof(struct ir_raw_event) * 8; | ||
221 | drv->fops = &lirc_fops; | ||
222 | drv->dev = &ir_dev->dev; | ||
223 | drv->owner = THIS_MODULE; | ||
224 | |||
225 | drv->minor = lirc_register_driver(drv); | ||
226 | if (drv->minor < 0) { | ||
227 | rc = -ENODEV; | ||
228 | goto lirc_register_failed; | ||
229 | } | ||
230 | |||
231 | ir_dev->raw->lirc.drv = drv; | ||
232 | ir_dev->raw->lirc.ir_dev = ir_dev; | ||
233 | ir_dev->raw->lirc.lircdata = PULSE_MASK; | ||
234 | |||
235 | return 0; | ||
236 | |||
237 | lirc_register_failed: | ||
238 | rbuf_init_failed: | ||
239 | kfree(rbuf); | ||
240 | rbuf_alloc_failed: | ||
241 | kfree(drv); | ||
242 | |||
243 | return rc; | ||
244 | } | ||
245 | |||
246 | static int ir_lirc_unregister(struct input_dev *input_dev) | ||
247 | { | ||
248 | struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); | ||
249 | struct lirc_codec *lirc = &ir_dev->raw->lirc; | ||
250 | |||
251 | lirc_unregister_driver(lirc->drv->minor); | ||
252 | lirc_buffer_free(lirc->drv->rbuf); | ||
253 | kfree(lirc->drv); | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static struct ir_raw_handler lirc_handler = { | ||
259 | .protocols = IR_TYPE_LIRC, | ||
260 | .decode = ir_lirc_decode, | ||
261 | .raw_register = ir_lirc_register, | ||
262 | .raw_unregister = ir_lirc_unregister, | ||
263 | }; | ||
264 | |||
265 | static int __init ir_lirc_codec_init(void) | ||
266 | { | ||
267 | ir_raw_handler_register(&lirc_handler); | ||
268 | |||
269 | printk(KERN_INFO "IR LIRC bridge handler initialized\n"); | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static void __exit ir_lirc_codec_exit(void) | ||
274 | { | ||
275 | ir_raw_handler_unregister(&lirc_handler); | ||
276 | } | ||
277 | |||
278 | module_init(ir_lirc_codec_init); | ||
279 | module_exit(ir_lirc_codec_exit); | ||
280 | |||
281 | MODULE_LICENSE("GPL"); | ||
282 | MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); | ||
283 | MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); | ||
284 | MODULE_DESCRIPTION("LIRC IR handler bridge"); | ||
diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c index 5f98ab823057..6f192ef31db1 100644 --- a/drivers/media/IR/ir-raw-event.c +++ b/drivers/media/IR/ir-raw-event.c | |||
@@ -253,6 +253,7 @@ static void init_decoders(struct work_struct *work) | |||
253 | load_rc6_decode(); | 253 | load_rc6_decode(); |
254 | load_jvc_decode(); | 254 | load_jvc_decode(); |
255 | load_sony_decode(); | 255 | load_sony_decode(); |
256 | load_lirc_codec(); | ||
256 | 257 | ||
257 | /* If needed, we may later add some init code. In this case, | 258 | /* If needed, we may later add some init code. In this case, |
258 | it is needed to change the CONFIG_MODULE test at ir-core.h | 259 | it is needed to change the CONFIG_MODULE test at ir-core.h |
diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index f176fbff9488..6273047e915b 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c | |||
@@ -43,6 +43,7 @@ static struct { | |||
43 | { IR_TYPE_RC6, "rc-6" }, | 43 | { IR_TYPE_RC6, "rc-6" }, |
44 | { IR_TYPE_JVC, "jvc" }, | 44 | { IR_TYPE_JVC, "jvc" }, |
45 | { IR_TYPE_SONY, "sony" }, | 45 | { IR_TYPE_SONY, "sony" }, |
46 | { IR_TYPE_LIRC, "lirc" }, | ||
46 | }; | 47 | }; |
47 | 48 | ||
48 | #define PROTO_NONE "none" | 49 | #define PROTO_NONE "none" |