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