aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2010-04-15 17:46:05 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-05-19 11:57:43 -0400
commitbf670f641d478fa5a2dd60ed41bab3156cc780c0 (patch)
treefe3c9ef6531956d5904addb9e825254938b13810 /drivers
parent384f23e8c9b189888d6d8c84ae5ebd23b074a0b6 (diff)
V4L/DVB: ir-core: Add JVC support to ir-core
This patch adds a JVC decoder to ir-core. Signed-off-by: David Härdeman <david@hardeman.nu> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/IR/Kconfig9
-rw-r--r--drivers/media/IR/Makefile1
-rw-r--r--drivers/media/IR/ir-core-priv.h7
-rw-r--r--drivers/media/IR/ir-jvc-decoder.c320
-rw-r--r--drivers/media/IR/ir-raw-event.c1
-rw-r--r--drivers/media/IR/ir-sysfs.c4
6 files changed, 342 insertions, 0 deletions
diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig
index dae5ea113867..e87242af9b35 100644
--- a/drivers/media/IR/Kconfig
+++ b/drivers/media/IR/Kconfig
@@ -37,6 +37,15 @@ config IR_RC6_DECODER
37 Enable this option if you have an infrared remote control which 37 Enable this option if you have an infrared remote control which
38 uses the RC6 protocol, and you need software decoding support. 38 uses the RC6 protocol, and you need software decoding support.
39 39
40config IR_JVC_DECODER
41 tristate "Enable IR raw decoder for the JVC protocol"
42 depends on IR_CORE
43 default y
44
45 ---help---
46 Enable this option if you have an infrared remote control which
47 uses the JVC protocol, and you need software decoding support.
48
40config IR_IMON 49config IR_IMON
41 tristate "SoundGraph iMON Receiver and Display" 50 tristate "SoundGraph iMON Receiver and Display"
42 depends on USB_ARCH_HAS_HCD 51 depends on USB_ARCH_HAS_HCD
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile
index 57b145515d4f..937cbdedc7c1 100644
--- a/drivers/media/IR/Makefile
+++ b/drivers/media/IR/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_VIDEO_IR) += ir-common.o
8obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o 8obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
9obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o 9obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
10obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o 10obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
11obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o
11 12
12# stand-alone IR receivers/transmitters 13# stand-alone IR receivers/transmitters
13obj-$(CONFIG_IR_IMON) += imon.o 14obj-$(CONFIG_IR_IMON) += imon.o
diff --git a/drivers/media/IR/ir-core-priv.h b/drivers/media/IR/ir-core-priv.h
index f965024385d8..464e392f24cf 100644
--- a/drivers/media/IR/ir-core-priv.h
+++ b/drivers/media/IR/ir-core-priv.h
@@ -109,4 +109,11 @@ void ir_raw_init(void);
109#define load_rc6_decode() 0 109#define load_rc6_decode() 0
110#endif 110#endif
111 111
112/* from ir-jvc-decoder.c */
113#ifdef CONFIG_IR_JVC_DECODER_MODULE
114#define load_jvc_decode() request_module("ir-jvc-decoder")
115#else
116#define load_jvc_decode() 0
117#endif
118
112#endif /* _IR_RAW_EVENT */ 119#endif /* _IR_RAW_EVENT */
diff --git a/drivers/media/IR/ir-jvc-decoder.c b/drivers/media/IR/ir-jvc-decoder.c
new file mode 100644
index 000000000000..0b804944cbb0
--- /dev/null
+++ b/drivers/media/IR/ir-jvc-decoder.c
@@ -0,0 +1,320 @@
1/* ir-jvc-decoder.c - handle JVC IR Pulse/Space protocol
2 *
3 * Copyright (C) 2010 by David Härdeman <david@hardeman.nu>
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/bitrev.h>
16#include "ir-core-priv.h"
17
18#define JVC_NBITS 16 /* dev(8) + func(8) */
19#define JVC_UNIT 525000 /* ns */
20#define JVC_HEADER_PULSE (16 * JVC_UNIT) /* lack of header -> repeat */
21#define JVC_HEADER_SPACE (8 * JVC_UNIT)
22#define JVC_BIT_PULSE (1 * JVC_UNIT)
23#define JVC_BIT_0_SPACE (1 * JVC_UNIT)
24#define JVC_BIT_1_SPACE (3 * JVC_UNIT)
25#define JVC_TRAILER_PULSE (1 * JVC_UNIT)
26#define JVC_TRAILER_SPACE (35 * JVC_UNIT)
27
28/* Used to register jvc_decoder clients */
29static LIST_HEAD(decoder_list);
30DEFINE_SPINLOCK(decoder_lock);
31
32enum jvc_state {
33 STATE_INACTIVE,
34 STATE_HEADER_SPACE,
35 STATE_BIT_PULSE,
36 STATE_BIT_SPACE,
37 STATE_TRAILER_PULSE,
38 STATE_TRAILER_SPACE,
39};
40
41struct decoder_data {
42 struct list_head list;
43 struct ir_input_dev *ir_dev;
44 int enabled:1;
45
46 /* State machine control */
47 enum jvc_state state;
48 u16 jvc_bits;
49 u16 jvc_old_bits;
50 unsigned count;
51 bool first;
52 bool toggle;
53};
54
55
56/**
57 * get_decoder_data() - gets decoder data
58 * @input_dev: input device
59 *
60 * Returns the struct decoder_data that corresponds to a device
61 */
62static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev)
63{
64 struct decoder_data *data = NULL;
65
66 spin_lock(&decoder_lock);
67 list_for_each_entry(data, &decoder_list, list) {
68 if (data->ir_dev == ir_dev)
69 break;
70 }
71 spin_unlock(&decoder_lock);
72 return data;
73}
74
75static ssize_t store_enabled(struct device *d,
76 struct device_attribute *mattr,
77 const char *buf,
78 size_t len)
79{
80 unsigned long value;
81 struct ir_input_dev *ir_dev = dev_get_drvdata(d);
82 struct decoder_data *data = get_decoder_data(ir_dev);
83
84 if (!data)
85 return -EINVAL;
86
87 if (strict_strtoul(buf, 10, &value) || value > 1)
88 return -EINVAL;
89
90 data->enabled = value;
91
92 return len;
93}
94
95static ssize_t show_enabled(struct device *d,
96 struct device_attribute *mattr, char *buf)
97{
98 struct ir_input_dev *ir_dev = dev_get_drvdata(d);
99 struct decoder_data *data = get_decoder_data(ir_dev);
100
101 if (!data)
102 return -EINVAL;
103
104 if (data->enabled)
105 return sprintf(buf, "1\n");
106 else
107 return sprintf(buf, "0\n");
108}
109
110static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled);
111
112static struct attribute *decoder_attributes[] = {
113 &dev_attr_enabled.attr,
114 NULL
115};
116
117static struct attribute_group decoder_attribute_group = {
118 .name = "jvc_decoder",
119 .attrs = decoder_attributes,
120};
121
122/**
123 * ir_jvc_decode() - Decode one JVC pulse or space
124 * @input_dev: the struct input_dev descriptor of the device
125 * @duration: the struct ir_raw_event descriptor of the pulse/space
126 *
127 * This function returns -EINVAL if the pulse violates the state machine
128 */
129static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
130{
131 struct decoder_data *data;
132 struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
133
134 data = get_decoder_data(ir_dev);
135 if (!data)
136 return -EINVAL;
137
138 if (!data->enabled)
139 return 0;
140
141 if (IS_RESET(ev)) {
142 data->state = STATE_INACTIVE;
143 return 0;
144 }
145
146 if (!geq_margin(ev.duration, JVC_UNIT, JVC_UNIT / 2))
147 goto out;
148
149 IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n",
150 data->state, TO_US(ev.duration), TO_STR(ev.pulse));
151
152 switch (data->state) {
153
154 case STATE_INACTIVE:
155 if (!ev.pulse)
156 break;
157
158 if (!eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2))
159 break;
160
161 data->count = 0;
162 data->first = true;
163 data->toggle = !data->toggle;
164 data->state = STATE_HEADER_SPACE;
165 return 0;
166
167 case STATE_HEADER_SPACE:
168 if (ev.pulse)
169 break;
170
171 if (!eq_margin(ev.duration, JVC_HEADER_SPACE, JVC_UNIT / 2))
172 break;
173
174 data->state = STATE_BIT_PULSE;
175 return 0;
176
177 case STATE_BIT_PULSE:
178 if (!ev.pulse)
179 break;
180
181 if (!eq_margin(ev.duration, JVC_BIT_PULSE, JVC_UNIT / 2))
182 break;
183
184 data->state = STATE_BIT_SPACE;
185 return 0;
186
187 case STATE_BIT_SPACE:
188 if (ev.pulse)
189 break;
190
191 data->jvc_bits <<= 1;
192 if (eq_margin(ev.duration, JVC_BIT_1_SPACE, JVC_UNIT / 2)) {
193 data->jvc_bits |= 1;
194 decrease_duration(&ev, JVC_BIT_1_SPACE);
195 } else if (eq_margin(ev.duration, JVC_BIT_0_SPACE, JVC_UNIT / 2))
196 decrease_duration(&ev, JVC_BIT_0_SPACE);
197 else
198 break;
199 data->count++;
200
201 if (data->count == JVC_NBITS)
202 data->state = STATE_TRAILER_PULSE;
203 else
204 data->state = STATE_BIT_PULSE;
205 return 0;
206
207 case STATE_TRAILER_PULSE:
208 if (!ev.pulse)
209 break;
210
211 if (!eq_margin(ev.duration, JVC_TRAILER_PULSE, JVC_UNIT / 2))
212 break;
213
214 data->state = STATE_TRAILER_SPACE;
215 return 0;
216
217 case STATE_TRAILER_SPACE:
218 if (ev.pulse)
219 break;
220
221 if (!geq_margin(ev.duration, JVC_TRAILER_SPACE, JVC_UNIT / 2))
222 break;
223
224 if (data->first) {
225 u32 scancode;
226 scancode = (bitrev8((data->jvc_bits >> 8) & 0xff) << 8) |
227 (bitrev8((data->jvc_bits >> 0) & 0xff) << 0);
228 IR_dprintk(1, "JVC scancode 0x%04x\n", scancode);
229 ir_keydown(input_dev, scancode, data->toggle);
230 data->first = false;
231 data->jvc_old_bits = data->jvc_bits;
232 } else if (data->jvc_bits == data->jvc_old_bits) {
233 IR_dprintk(1, "JVC repeat\n");
234 ir_repeat(input_dev);
235 } else {
236 IR_dprintk(1, "JVC invalid repeat msg\n");
237 break;
238 }
239
240 data->count = 0;
241 data->state = STATE_BIT_PULSE;
242 return 0;
243 }
244
245out:
246 IR_dprintk(1, "JVC decode failed at state %d (%uus %s)\n",
247 data->state, TO_US(ev.duration), TO_STR(ev.pulse));
248 data->state = STATE_INACTIVE;
249 return -EINVAL;
250}
251
252static int ir_jvc_register(struct input_dev *input_dev)
253{
254 struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
255 struct decoder_data *data;
256 int rc;
257
258 rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group);
259 if (rc < 0)
260 return rc;
261
262 data = kzalloc(sizeof(*data), GFP_KERNEL);
263 if (!data) {
264 sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
265 return -ENOMEM;
266 }
267
268 data->ir_dev = ir_dev;
269 data->enabled = 1;
270
271 spin_lock(&decoder_lock);
272 list_add_tail(&data->list, &decoder_list);
273 spin_unlock(&decoder_lock);
274
275 return 0;
276}
277
278static int ir_jvc_unregister(struct input_dev *input_dev)
279{
280 struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
281 static struct decoder_data *data;
282
283 data = get_decoder_data(ir_dev);
284 if (!data)
285 return 0;
286
287 sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
288
289 spin_lock(&decoder_lock);
290 list_del(&data->list);
291 spin_unlock(&decoder_lock);
292
293 return 0;
294}
295
296static struct ir_raw_handler jvc_handler = {
297 .decode = ir_jvc_decode,
298 .raw_register = ir_jvc_register,
299 .raw_unregister = ir_jvc_unregister,
300};
301
302static int __init ir_jvc_decode_init(void)
303{
304 ir_raw_handler_register(&jvc_handler);
305
306 printk(KERN_INFO "IR JVC protocol handler initialized\n");
307 return 0;
308}
309
310static void __exit ir_jvc_decode_exit(void)
311{
312 ir_raw_handler_unregister(&jvc_handler);
313}
314
315module_init(ir_jvc_decode_init);
316module_exit(ir_jvc_decode_exit);
317
318MODULE_LICENSE("GPL");
319MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
320MODULE_DESCRIPTION("JVC IR protocol decoder");
diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c
index 59f173c45b9d..7eef6bf4404f 100644
--- a/drivers/media/IR/ir-raw-event.c
+++ b/drivers/media/IR/ir-raw-event.c
@@ -233,6 +233,7 @@ static void init_decoders(struct work_struct *work)
233 load_nec_decode(); 233 load_nec_decode();
234 load_rc5_decode(); 234 load_rc5_decode();
235 load_rc6_decode(); 235 load_rc6_decode();
236 load_jvc_decode();
236 237
237 /* If needed, we may later add some init code. In this case, 238 /* If needed, we may later add some init code. In this case,
238 it is needed to change the CONFIG_MODULE test at ir-core.h 239 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 ceec980552a4..3ec760a98061 100644
--- a/drivers/media/IR/ir-sysfs.c
+++ b/drivers/media/IR/ir-sysfs.c
@@ -63,6 +63,8 @@ static ssize_t show_protocol(struct device *d,
63 s = "nec"; 63 s = "nec";
64 else if (ir_type == IR_TYPE_RC6) 64 else if (ir_type == IR_TYPE_RC6)
65 s = "rc6"; 65 s = "rc6";
66 else if (ir_type == IR_TYPE_JVC)
67 s = "jvc";
66 else 68 else
67 s = "other"; 69 s = "other";
68 70
@@ -100,6 +102,8 @@ static ssize_t store_protocol(struct device *d,
100 ir_type |= IR_TYPE_PD; 102 ir_type |= IR_TYPE_PD;
101 if (!strcasecmp(buf, "nec")) 103 if (!strcasecmp(buf, "nec"))
102 ir_type |= IR_TYPE_NEC; 104 ir_type |= IR_TYPE_NEC;
105 if (!strcasecmp(buf, "jvc"))
106 ir_type |= IR_TYPE_JVC;
103 } 107 }
104 108
105 if (!ir_type) { 109 if (!ir_type) {