diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/video/ir-kbd-i2c.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/video/ir-kbd-i2c.c')
-rw-r--r-- | drivers/media/video/ir-kbd-i2c.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c new file mode 100644 index 000000000000..92664f75d327 --- /dev/null +++ b/drivers/media/video/ir-kbd-i2c.c | |||
@@ -0,0 +1,492 @@ | |||
1 | /* | ||
2 | * $Id: ir-kbd-i2c.c,v 1.10 2004/12/09 12:51:35 kraxel Exp $ | ||
3 | * | ||
4 | * keyboard input driver for i2c IR remote controls | ||
5 | * | ||
6 | * Copyright (c) 2000-2003 Gerd Knorr <kraxel@bytesex.org> | ||
7 | * modified for PixelView (BT878P+W/FM) by | ||
8 | * Michal Kochanowicz <mkochano@pld.org.pl> | ||
9 | * Christoph Bartelmus <lirc@bartelmus.de> | ||
10 | * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by | ||
11 | * Ulrich Mueller <ulrich.mueller42@web.de> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, write to the Free Software | ||
25 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include <linux/module.h> | ||
30 | #include <linux/moduleparam.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/sched.h> | ||
34 | #include <linux/string.h> | ||
35 | #include <linux/timer.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include <linux/errno.h> | ||
38 | #include <linux/slab.h> | ||
39 | #include <linux/i2c.h> | ||
40 | #include <linux/workqueue.h> | ||
41 | |||
42 | #include <asm/semaphore.h> | ||
43 | |||
44 | #include <media/ir-common.h> | ||
45 | |||
46 | /* Mark Phalan <phalanm@o2.ie> */ | ||
47 | static IR_KEYTAB_TYPE ir_codes_pv951[IR_KEYTAB_SIZE] = { | ||
48 | [ 0 ] = KEY_KP0, | ||
49 | [ 1 ] = KEY_KP1, | ||
50 | [ 2 ] = KEY_KP2, | ||
51 | [ 3 ] = KEY_KP3, | ||
52 | [ 4 ] = KEY_KP4, | ||
53 | [ 5 ] = KEY_KP5, | ||
54 | [ 6 ] = KEY_KP6, | ||
55 | [ 7 ] = KEY_KP7, | ||
56 | [ 8 ] = KEY_KP8, | ||
57 | [ 9 ] = KEY_KP9, | ||
58 | |||
59 | [ 18 ] = KEY_POWER, | ||
60 | [ 16 ] = KEY_MUTE, | ||
61 | [ 31 ] = KEY_VOLUMEDOWN, | ||
62 | [ 27 ] = KEY_VOLUMEUP, | ||
63 | [ 26 ] = KEY_CHANNELUP, | ||
64 | [ 30 ] = KEY_CHANNELDOWN, | ||
65 | [ 14 ] = KEY_PAGEUP, | ||
66 | [ 29 ] = KEY_PAGEDOWN, | ||
67 | [ 19 ] = KEY_SOUND, | ||
68 | |||
69 | [ 24 ] = KEY_KPPLUSMINUS, // CH +/- | ||
70 | [ 22 ] = KEY_SUBTITLE, // CC | ||
71 | [ 13 ] = KEY_TEXT, // TTX | ||
72 | [ 11 ] = KEY_TV, // AIR/CBL | ||
73 | [ 17 ] = KEY_PC, // PC/TV | ||
74 | [ 23 ] = KEY_OK, // CH RTN | ||
75 | [ 25 ] = KEY_MODE, // FUNC | ||
76 | [ 12 ] = KEY_SEARCH, // AUTOSCAN | ||
77 | |||
78 | /* Not sure what to do with these ones! */ | ||
79 | [ 15 ] = KEY_SELECT, // SOURCE | ||
80 | [ 10 ] = KEY_KPPLUS, // +100 | ||
81 | [ 20 ] = KEY_KPEQUAL, // SYNC | ||
82 | [ 28 ] = KEY_MEDIA, // PC/TV | ||
83 | }; | ||
84 | |||
85 | static IR_KEYTAB_TYPE ir_codes_purpletv[IR_KEYTAB_SIZE] = { | ||
86 | [ 0x3 ] = KEY_POWER, | ||
87 | [ 0x6f ] = KEY_MUTE, | ||
88 | [ 0x10 ] = KEY_BACKSPACE, // Recall | ||
89 | |||
90 | [ 0x11 ] = KEY_KP0, | ||
91 | [ 0x4 ] = KEY_KP1, | ||
92 | [ 0x5 ] = KEY_KP2, | ||
93 | [ 0x6 ] = KEY_KP3, | ||
94 | [ 0x8 ] = KEY_KP4, | ||
95 | [ 0x9 ] = KEY_KP5, | ||
96 | [ 0xa ] = KEY_KP6, | ||
97 | [ 0xc ] = KEY_KP7, | ||
98 | [ 0xd ] = KEY_KP8, | ||
99 | [ 0xe ] = KEY_KP9, | ||
100 | [ 0x12 ] = KEY_KPDOT, // 100+ | ||
101 | |||
102 | [ 0x7 ] = KEY_VOLUMEUP, | ||
103 | [ 0xb ] = KEY_VOLUMEDOWN, | ||
104 | [ 0x1a ] = KEY_KPPLUS, | ||
105 | [ 0x18 ] = KEY_KPMINUS, | ||
106 | [ 0x15 ] = KEY_UP, | ||
107 | [ 0x1d ] = KEY_DOWN, | ||
108 | [ 0xf ] = KEY_CHANNELUP, | ||
109 | [ 0x13 ] = KEY_CHANNELDOWN, | ||
110 | [ 0x48 ] = KEY_ZOOM, | ||
111 | |||
112 | [ 0x1b ] = KEY_VIDEO, // Video source | ||
113 | #if 0 | ||
114 | [ 0x1f ] = KEY_S, // Snapshot | ||
115 | #endif | ||
116 | [ 0x49 ] = KEY_LANGUAGE, // MTS Select | ||
117 | [ 0x19 ] = KEY_SEARCH, // Auto Scan | ||
118 | |||
119 | [ 0x4b ] = KEY_RECORD, | ||
120 | [ 0x46 ] = KEY_PLAY, | ||
121 | [ 0x45 ] = KEY_PAUSE, // Pause | ||
122 | [ 0x44 ] = KEY_STOP, | ||
123 | #if 0 | ||
124 | [ 0x43 ] = KEY_T, // Time Shift | ||
125 | [ 0x47 ] = KEY_Y, // Time Shift OFF | ||
126 | [ 0x4a ] = KEY_O, // TOP | ||
127 | [ 0x17 ] = KEY_F, // SURF CH | ||
128 | #endif | ||
129 | [ 0x40 ] = KEY_FORWARD, // Forward ? | ||
130 | [ 0x42 ] = KEY_REWIND, // Backward ? | ||
131 | |||
132 | }; | ||
133 | |||
134 | struct IR; | ||
135 | struct IR { | ||
136 | struct i2c_client c; | ||
137 | struct input_dev input; | ||
138 | struct ir_input_state ir; | ||
139 | |||
140 | struct work_struct work; | ||
141 | struct timer_list timer; | ||
142 | char phys[32]; | ||
143 | int (*get_key)(struct IR*, u32*, u32*); | ||
144 | }; | ||
145 | |||
146 | /* ----------------------------------------------------------------------- */ | ||
147 | /* insmod parameters */ | ||
148 | |||
149 | static int debug; | ||
150 | module_param(debug, int, 0644); /* debug level (0,1,2) */ | ||
151 | |||
152 | #define DEVNAME "ir-kbd-i2c" | ||
153 | #define dprintk(level, fmt, arg...) if (debug >= level) \ | ||
154 | printk(KERN_DEBUG DEVNAME ": " fmt , ## arg) | ||
155 | |||
156 | /* ----------------------------------------------------------------------- */ | ||
157 | |||
158 | static int get_key_haup(struct IR *ir, u32 *ir_key, u32 *ir_raw) | ||
159 | { | ||
160 | unsigned char buf[3]; | ||
161 | int start, toggle, dev, code; | ||
162 | |||
163 | /* poll IR chip */ | ||
164 | if (3 != i2c_master_recv(&ir->c,buf,3)) | ||
165 | return -EIO; | ||
166 | |||
167 | /* split rc5 data block ... */ | ||
168 | start = (buf[0] >> 6) & 3; | ||
169 | toggle = (buf[0] >> 5) & 1; | ||
170 | dev = buf[0] & 0x1f; | ||
171 | code = (buf[1] >> 2) & 0x3f; | ||
172 | |||
173 | if (3 != start) | ||
174 | /* no key pressed */ | ||
175 | return 0; | ||
176 | dprintk(1,"ir hauppauge (rc5): s%d t%d dev=%d code=%d\n", | ||
177 | start, toggle, dev, code); | ||
178 | |||
179 | /* return key */ | ||
180 | *ir_key = code; | ||
181 | *ir_raw = (start << 12) | (toggle << 11) | (dev << 6) | code; | ||
182 | return 1; | ||
183 | } | ||
184 | |||
185 | static int get_key_pixelview(struct IR *ir, u32 *ir_key, u32 *ir_raw) | ||
186 | { | ||
187 | unsigned char b; | ||
188 | |||
189 | /* poll IR chip */ | ||
190 | if (1 != i2c_master_recv(&ir->c,&b,1)) { | ||
191 | dprintk(1,"read error\n"); | ||
192 | return -EIO; | ||
193 | } | ||
194 | *ir_key = b; | ||
195 | *ir_raw = b; | ||
196 | return 1; | ||
197 | } | ||
198 | |||
199 | static int get_key_pv951(struct IR *ir, u32 *ir_key, u32 *ir_raw) | ||
200 | { | ||
201 | unsigned char b; | ||
202 | |||
203 | /* poll IR chip */ | ||
204 | if (1 != i2c_master_recv(&ir->c,&b,1)) { | ||
205 | dprintk(1,"read error\n"); | ||
206 | return -EIO; | ||
207 | } | ||
208 | |||
209 | /* ignore 0xaa */ | ||
210 | if (b==0xaa) | ||
211 | return 0; | ||
212 | dprintk(2,"key %02x\n", b); | ||
213 | |||
214 | *ir_key = b; | ||
215 | *ir_raw = b; | ||
216 | return 1; | ||
217 | } | ||
218 | |||
219 | static int get_key_knc1(struct IR *ir, u32 *ir_key, u32 *ir_raw) | ||
220 | { | ||
221 | unsigned char b; | ||
222 | |||
223 | /* poll IR chip */ | ||
224 | if (1 != i2c_master_recv(&ir->c,&b,1)) { | ||
225 | dprintk(1,"read error\n"); | ||
226 | return -EIO; | ||
227 | } | ||
228 | |||
229 | /* it seems that 0xFE indicates that a button is still hold | ||
230 | down, while 0xFF indicates that no button is hold | ||
231 | down. 0xFE sequences are sometimes interrupted by 0xFF */ | ||
232 | |||
233 | dprintk(2,"key %02x\n", b); | ||
234 | |||
235 | if (b == 0xFF) | ||
236 | return 0; | ||
237 | |||
238 | if (b == 0xFE) | ||
239 | /* keep old data */ | ||
240 | return 1; | ||
241 | |||
242 | *ir_key = b; | ||
243 | *ir_raw = b; | ||
244 | return 1; | ||
245 | } | ||
246 | |||
247 | static int get_key_purpletv(struct IR *ir, u32 *ir_key, u32 *ir_raw) | ||
248 | { | ||
249 | unsigned char b; | ||
250 | |||
251 | /* poll IR chip */ | ||
252 | if (1 != i2c_master_recv(&ir->c,&b,1)) { | ||
253 | dprintk(1,"read error\n"); | ||
254 | return -EIO; | ||
255 | } | ||
256 | |||
257 | /* no button press */ | ||
258 | if (b==0) | ||
259 | return 0; | ||
260 | |||
261 | /* repeating */ | ||
262 | if (b & 0x80) | ||
263 | return 1; | ||
264 | |||
265 | *ir_key = b; | ||
266 | *ir_raw = b; | ||
267 | return 1; | ||
268 | } | ||
269 | /* ----------------------------------------------------------------------- */ | ||
270 | |||
271 | static void ir_key_poll(struct IR *ir) | ||
272 | { | ||
273 | static u32 ir_key, ir_raw; | ||
274 | int rc; | ||
275 | |||
276 | dprintk(2,"ir_poll_key\n"); | ||
277 | rc = ir->get_key(ir, &ir_key, &ir_raw); | ||
278 | if (rc < 0) { | ||
279 | dprintk(2,"error\n"); | ||
280 | return; | ||
281 | } | ||
282 | |||
283 | if (0 == rc) { | ||
284 | ir_input_nokey(&ir->input,&ir->ir); | ||
285 | } else { | ||
286 | ir_input_keydown(&ir->input,&ir->ir, ir_key, ir_raw); | ||
287 | } | ||
288 | } | ||
289 | |||
290 | static void ir_timer(unsigned long data) | ||
291 | { | ||
292 | struct IR *ir = (struct IR*)data; | ||
293 | schedule_work(&ir->work); | ||
294 | } | ||
295 | |||
296 | static void ir_work(void *data) | ||
297 | { | ||
298 | struct IR *ir = data; | ||
299 | ir_key_poll(ir); | ||
300 | mod_timer(&ir->timer, jiffies+HZ/10); | ||
301 | } | ||
302 | |||
303 | /* ----------------------------------------------------------------------- */ | ||
304 | |||
305 | static int ir_attach(struct i2c_adapter *adap, int addr, | ||
306 | unsigned short flags, int kind); | ||
307 | static int ir_detach(struct i2c_client *client); | ||
308 | static int ir_probe(struct i2c_adapter *adap); | ||
309 | |||
310 | static struct i2c_driver driver = { | ||
311 | .name = "ir remote kbd driver", | ||
312 | .id = I2C_DRIVERID_EXP3, /* FIXME */ | ||
313 | .flags = I2C_DF_NOTIFY, | ||
314 | .attach_adapter = ir_probe, | ||
315 | .detach_client = ir_detach, | ||
316 | }; | ||
317 | |||
318 | static struct i2c_client client_template = | ||
319 | { | ||
320 | I2C_DEVNAME("unset"), | ||
321 | .driver = &driver | ||
322 | }; | ||
323 | |||
324 | static int ir_attach(struct i2c_adapter *adap, int addr, | ||
325 | unsigned short flags, int kind) | ||
326 | { | ||
327 | IR_KEYTAB_TYPE *ir_codes = NULL; | ||
328 | char *name; | ||
329 | int ir_type; | ||
330 | struct IR *ir; | ||
331 | |||
332 | if (NULL == (ir = kmalloc(sizeof(struct IR),GFP_KERNEL))) | ||
333 | return -ENOMEM; | ||
334 | memset(ir,0,sizeof(*ir)); | ||
335 | ir->c = client_template; | ||
336 | |||
337 | i2c_set_clientdata(&ir->c, ir); | ||
338 | ir->c.adapter = adap; | ||
339 | ir->c.addr = addr; | ||
340 | |||
341 | switch(addr) { | ||
342 | case 0x64: | ||
343 | name = "Pixelview"; | ||
344 | ir->get_key = get_key_pixelview; | ||
345 | ir_type = IR_TYPE_OTHER; | ||
346 | ir_codes = ir_codes_empty; | ||
347 | break; | ||
348 | case 0x4b: | ||
349 | name = "PV951"; | ||
350 | ir->get_key = get_key_pv951; | ||
351 | ir_type = IR_TYPE_OTHER; | ||
352 | ir_codes = ir_codes_pv951; | ||
353 | break; | ||
354 | case 0x18: | ||
355 | case 0x1a: | ||
356 | name = "Hauppauge"; | ||
357 | ir->get_key = get_key_haup; | ||
358 | ir_type = IR_TYPE_RC5; | ||
359 | ir_codes = ir_codes_rc5_tv; | ||
360 | break; | ||
361 | case 0x30: | ||
362 | name = "KNC One"; | ||
363 | ir->get_key = get_key_knc1; | ||
364 | ir_type = IR_TYPE_OTHER; | ||
365 | ir_codes = ir_codes_empty; | ||
366 | break; | ||
367 | case 0x7a: | ||
368 | name = "Purple TV"; | ||
369 | ir->get_key = get_key_purpletv; | ||
370 | ir_type = IR_TYPE_OTHER; | ||
371 | ir_codes = ir_codes_purpletv; | ||
372 | break; | ||
373 | default: | ||
374 | /* shouldn't happen */ | ||
375 | printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n",addr); | ||
376 | kfree(ir); | ||
377 | return -1; | ||
378 | } | ||
379 | |||
380 | /* register i2c device */ | ||
381 | i2c_attach_client(&ir->c); | ||
382 | snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", name); | ||
383 | snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0", | ||
384 | ir->c.adapter->dev.bus_id, | ||
385 | ir->c.dev.bus_id); | ||
386 | |||
387 | /* init + register input device */ | ||
388 | ir_input_init(&ir->input,&ir->ir,ir_type,ir_codes); | ||
389 | ir->input.id.bustype = BUS_I2C; | ||
390 | ir->input.name = ir->c.name; | ||
391 | ir->input.phys = ir->phys; | ||
392 | input_register_device(&ir->input); | ||
393 | printk(DEVNAME ": %s detected at %s [%s]\n", | ||
394 | ir->input.name,ir->input.phys,adap->name); | ||
395 | |||
396 | /* start polling via eventd */ | ||
397 | INIT_WORK(&ir->work, ir_work, ir); | ||
398 | init_timer(&ir->timer); | ||
399 | ir->timer.function = ir_timer; | ||
400 | ir->timer.data = (unsigned long)ir; | ||
401 | schedule_work(&ir->work); | ||
402 | |||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | static int ir_detach(struct i2c_client *client) | ||
407 | { | ||
408 | struct IR *ir = i2c_get_clientdata(client); | ||
409 | |||
410 | /* kill outstanding polls */ | ||
411 | del_timer(&ir->timer); | ||
412 | flush_scheduled_work(); | ||
413 | |||
414 | /* unregister devices */ | ||
415 | input_unregister_device(&ir->input); | ||
416 | i2c_detach_client(&ir->c); | ||
417 | |||
418 | /* free memory */ | ||
419 | kfree(ir); | ||
420 | return 0; | ||
421 | } | ||
422 | |||
423 | static int ir_probe(struct i2c_adapter *adap) | ||
424 | { | ||
425 | |||
426 | /* The external IR receiver is at i2c address 0x34 (0x35 for | ||
427 | reads). Future Hauppauge cards will have an internal | ||
428 | receiver at 0x30 (0x31 for reads). In theory, both can be | ||
429 | fitted, and Hauppauge suggest an external overrides an | ||
430 | internal. | ||
431 | |||
432 | That's why we probe 0x1a (~0x34) first. CB | ||
433 | */ | ||
434 | |||
435 | static const int probe_bttv[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1}; | ||
436 | static const int probe_saa7134[] = { 0x7a, -1 }; | ||
437 | const int *probe = NULL; | ||
438 | struct i2c_client c; char buf; int i,rc; | ||
439 | |||
440 | switch (adap->id) { | ||
441 | case I2C_ALGO_BIT | I2C_HW_B_BT848: | ||
442 | probe = probe_bttv; | ||
443 | break; | ||
444 | case I2C_ALGO_SAA7134: | ||
445 | probe = probe_saa7134; | ||
446 | break; | ||
447 | } | ||
448 | if (NULL == probe) | ||
449 | return 0; | ||
450 | |||
451 | memset(&c,0,sizeof(c)); | ||
452 | c.adapter = adap; | ||
453 | for (i = 0; -1 != probe[i]; i++) { | ||
454 | c.addr = probe[i]; | ||
455 | rc = i2c_master_recv(&c,&buf,1); | ||
456 | dprintk(1,"probe 0x%02x @ %s: %s\n", | ||
457 | probe[i], adap->name, | ||
458 | (1 == rc) ? "yes" : "no"); | ||
459 | if (1 == rc) { | ||
460 | ir_attach(adap,probe[i],0,0); | ||
461 | break; | ||
462 | } | ||
463 | } | ||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | /* ----------------------------------------------------------------------- */ | ||
468 | |||
469 | MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller"); | ||
470 | MODULE_DESCRIPTION("input driver for i2c IR remote controls"); | ||
471 | MODULE_LICENSE("GPL"); | ||
472 | |||
473 | static int __init ir_init(void) | ||
474 | { | ||
475 | return i2c_add_driver(&driver); | ||
476 | } | ||
477 | |||
478 | static void __exit ir_fini(void) | ||
479 | { | ||
480 | i2c_del_driver(&driver); | ||
481 | } | ||
482 | |||
483 | module_init(ir_init); | ||
484 | module_exit(ir_fini); | ||
485 | |||
486 | /* | ||
487 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
488 | * --------------------------------------------------------------------------- | ||
489 | * Local variables: | ||
490 | * c-basic-offset: 8 | ||
491 | * End: | ||
492 | */ | ||