aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/IR
diff options
context:
space:
mode:
authorJarod Wilson <jarod@redhat.com>2010-07-03 00:07:53 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-08-02 14:16:20 -0400
commitca4146985db7cbb97816e9b961b8db79e63d9e86 (patch)
treedd44b6e835a4f381acc949226277fe4de91c4219 /drivers/media/IR
parent4a62a5ab59742331a4e17ccaa894968d40ed9b16 (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/Kconfig10
-rw-r--r--drivers/media/IR/Makefile1
-rw-r--r--drivers/media/IR/ir-core-priv.h13
-rw-r--r--drivers/media/IR/ir-lirc-codec.c284
-rw-r--r--drivers/media/IR/ir-raw-event.c1
-rw-r--r--drivers/media/IR/ir-sysfs.c1
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
72config 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
72config IR_IMON 82config 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
11obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o 11obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
12obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o 12obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o
13obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o 13obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o
14obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
14 15
15# stand-alone IR receivers/transmitters 16# stand-alone IR receivers/transmitters
16obj-$(CONFIG_IR_IMON) += imon.o 17obj-$(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 */
32static 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
58static 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
95out:
96 kfree(txbuf);
97 return ret;
98}
99
100static 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
163static int ir_lirc_open(void *data)
164{
165 return 0;
166}
167
168static void ir_lirc_close(void *data)
169{
170 return;
171}
172
173static 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
183static 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
237lirc_register_failed:
238rbuf_init_failed:
239 kfree(rbuf);
240rbuf_alloc_failed:
241 kfree(drv);
242
243 return rc;
244}
245
246static 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
258static 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
265static 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
273static void __exit ir_lirc_codec_exit(void)
274{
275 ir_raw_handler_unregister(&lirc_handler);
276}
277
278module_init(ir_lirc_codec_init);
279module_exit(ir_lirc_codec_exit);
280
281MODULE_LICENSE("GPL");
282MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
283MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
284MODULE_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"