aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb/dibusb/dvb-dibusb-remote.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/dvb/dibusb/dvb-dibusb-remote.c')
-rw-r--r--drivers/media/dvb/dibusb/dvb-dibusb-remote.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-remote.c b/drivers/media/dvb/dibusb/dvb-dibusb-remote.c
new file mode 100644
index 000000000000..9dc8b15517b7
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-remote.c
@@ -0,0 +1,316 @@
1/*
2 * dvb-dibusb-remote.c is part of the driver for mobile USB Budget DVB-T devices
3 * based on reference design made by DiBcom (http://www.dibcom.fr/)
4 *
5 * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
6 *
7 * see dvb-dibusb-core.c for more copyright details.
8 *
9 * This file contains functions for handling the event device on the software
10 * side and the remote control on the hardware side.
11 */
12#include "dvb-dibusb.h"
13
14/* Table to map raw key codes to key events. This should not be hard-wired
15 into the kernel. */
16static const struct { u8 c0, c1, c2; uint32_t key; } nec_rc_keys [] =
17{
18 /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */
19 { 0x00, 0xff, 0x16, KEY_POWER },
20 { 0x00, 0xff, 0x10, KEY_MUTE },
21 { 0x00, 0xff, 0x03, KEY_1 },
22 { 0x00, 0xff, 0x01, KEY_2 },
23 { 0x00, 0xff, 0x06, KEY_3 },
24 { 0x00, 0xff, 0x09, KEY_4 },
25 { 0x00, 0xff, 0x1d, KEY_5 },
26 { 0x00, 0xff, 0x1f, KEY_6 },
27 { 0x00, 0xff, 0x0d, KEY_7 },
28 { 0x00, 0xff, 0x19, KEY_8 },
29 { 0x00, 0xff, 0x1b, KEY_9 },
30 { 0x00, 0xff, 0x15, KEY_0 },
31 { 0x00, 0xff, 0x05, KEY_CHANNELUP },
32 { 0x00, 0xff, 0x02, KEY_CHANNELDOWN },
33 { 0x00, 0xff, 0x1e, KEY_VOLUMEUP },
34 { 0x00, 0xff, 0x0a, KEY_VOLUMEDOWN },
35 { 0x00, 0xff, 0x11, KEY_RECORD },
36 { 0x00, 0xff, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */
37 { 0x00, 0xff, 0x14, KEY_PLAY },
38 { 0x00, 0xff, 0x1a, KEY_STOP },
39 { 0x00, 0xff, 0x40, KEY_REWIND },
40 { 0x00, 0xff, 0x12, KEY_FASTFORWARD },
41 { 0x00, 0xff, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */
42 { 0x00, 0xff, 0x4c, KEY_PAUSE },
43 { 0x00, 0xff, 0x4d, KEY_SCREEN }, /* Full screen mode. */
44 { 0x00, 0xff, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
45 /* additional keys TwinHan VisionPlus, the Artec seemingly not have */
46 { 0x00, 0xff, 0x0c, KEY_CANCEL }, /* Cancel */
47 { 0x00, 0xff, 0x1c, KEY_EPG }, /* EPG */
48 { 0x00, 0xff, 0x00, KEY_TAB }, /* Tab */
49 { 0x00, 0xff, 0x48, KEY_INFO }, /* Preview */
50 { 0x00, 0xff, 0x04, KEY_LIST }, /* RecordList */
51 { 0x00, 0xff, 0x0f, KEY_TEXT }, /* Teletext */
52 /* Key codes for the KWorld/ADSTech/JetWay remote. */
53 { 0x86, 0x6b, 0x12, KEY_POWER },
54 { 0x86, 0x6b, 0x0f, KEY_SELECT }, /* source */
55 { 0x86, 0x6b, 0x0c, KEY_UNKNOWN }, /* scan */
56 { 0x86, 0x6b, 0x0b, KEY_EPG },
57 { 0x86, 0x6b, 0x10, KEY_MUTE },
58 { 0x86, 0x6b, 0x01, KEY_1 },
59 { 0x86, 0x6b, 0x02, KEY_2 },
60 { 0x86, 0x6b, 0x03, KEY_3 },
61 { 0x86, 0x6b, 0x04, KEY_4 },
62 { 0x86, 0x6b, 0x05, KEY_5 },
63 { 0x86, 0x6b, 0x06, KEY_6 },
64 { 0x86, 0x6b, 0x07, KEY_7 },
65 { 0x86, 0x6b, 0x08, KEY_8 },
66 { 0x86, 0x6b, 0x09, KEY_9 },
67 { 0x86, 0x6b, 0x0a, KEY_0 },
68 { 0x86, 0x6b, 0x18, KEY_ZOOM },
69 { 0x86, 0x6b, 0x1c, KEY_UNKNOWN }, /* preview */
70 { 0x86, 0x6b, 0x13, KEY_UNKNOWN }, /* snap */
71 { 0x86, 0x6b, 0x00, KEY_UNDO },
72 { 0x86, 0x6b, 0x1d, KEY_RECORD },
73 { 0x86, 0x6b, 0x0d, KEY_STOP },
74 { 0x86, 0x6b, 0x0e, KEY_PAUSE },
75 { 0x86, 0x6b, 0x16, KEY_PLAY },
76 { 0x86, 0x6b, 0x11, KEY_BACK },
77 { 0x86, 0x6b, 0x19, KEY_FORWARD },
78 { 0x86, 0x6b, 0x14, KEY_UNKNOWN }, /* pip */
79 { 0x86, 0x6b, 0x15, KEY_ESC },
80 { 0x86, 0x6b, 0x1a, KEY_UP },
81 { 0x86, 0x6b, 0x1e, KEY_DOWN },
82 { 0x86, 0x6b, 0x1f, KEY_LEFT },
83 { 0x86, 0x6b, 0x1b, KEY_RIGHT },
84};
85
86/* Hauppauge NOVA-T USB2 keys */
87static const struct { u16 raw; uint32_t key; } haupp_rc_keys [] = {
88 { 0xddf, KEY_GOTO },
89 { 0xdef, KEY_POWER },
90 { 0xce7, KEY_TV },
91 { 0xcc7, KEY_VIDEO },
92 { 0xccf, KEY_AUDIO },
93 { 0xcd7, KEY_MEDIA },
94 { 0xcdf, KEY_EPG },
95 { 0xca7, KEY_UP },
96 { 0xc67, KEY_RADIO },
97 { 0xcb7, KEY_LEFT },
98 { 0xd2f, KEY_OK },
99 { 0xcbf, KEY_RIGHT },
100 { 0xcff, KEY_BACK },
101 { 0xcaf, KEY_DOWN },
102 { 0xc6f, KEY_MENU },
103 { 0xc87, KEY_VOLUMEUP },
104 { 0xc8f, KEY_VOLUMEDOWN },
105 { 0xc97, KEY_CHANNEL },
106 { 0xc7f, KEY_MUTE },
107 { 0xd07, KEY_CHANNELUP },
108 { 0xd0f, KEY_CHANNELDOWN },
109 { 0xdbf, KEY_RECORD },
110 { 0xdb7, KEY_STOP },
111 { 0xd97, KEY_REWIND },
112 { 0xdaf, KEY_PLAY },
113 { 0xda7, KEY_FASTFORWARD },
114 { 0xd27, KEY_LAST }, /* Skip backwards */
115 { 0xd87, KEY_PAUSE },
116 { 0xcf7, KEY_NEXT },
117 { 0xc07, KEY_0 },
118 { 0xc0f, KEY_1 },
119 { 0xc17, KEY_2 },
120 { 0xc1f, KEY_3 },
121 { 0xc27, KEY_4 },
122 { 0xc2f, KEY_5 },
123 { 0xc37, KEY_6 },
124 { 0xc3f, KEY_7 },
125 { 0xc47, KEY_8 },
126 { 0xc4f, KEY_9 },
127 { 0xc57, KEY_KPASTERISK },
128 { 0xc77, KEY_GRAVE }, /* # */
129 { 0xc5f, KEY_RED },
130 { 0xd77, KEY_GREEN },
131 { 0xdc7, KEY_YELLOW },
132 { 0xd4f, KEY_BLUE},
133};
134
135static int dibusb_key2event_nec(struct usb_dibusb *dib,u8 rb[5])
136{
137 int i;
138 switch (rb[0]) {
139 case DIBUSB_RC_NEC_KEY_PRESSED:
140 /* rb[1-3] is the actual key, rb[4] is a checksum */
141 deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
142 rb[1], rb[2], rb[3], rb[4]);
143
144 if ((0xff - rb[3]) != rb[4]) {
145 deb_rc("remote control checksum failed.\n");
146 break;
147 }
148
149 /* See if we can match the raw key code. */
150 for (i = 0; i < sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++) {
151 if (nec_rc_keys[i].c0 == rb[1] &&
152 nec_rc_keys[i].c1 == rb[2] &&
153 nec_rc_keys[i].c2 == rb[3]) {
154
155 dib->last_event = nec_rc_keys[i].key;
156 return 1;
157 }
158 }
159 break;
160 case DIBUSB_RC_NEC_KEY_REPEATED:
161 /* rb[1]..rb[4] are always zero.*/
162 /* Repeats often seem to occur so for the moment just ignore this. */
163 return 0;
164 case DIBUSB_RC_NEC_EMPTY: /* No (more) remote control keys. */
165 default:
166 break;
167 }
168 return -1;
169}
170
171static int dibusb_key2event_hauppauge(struct usb_dibusb *dib,u8 rb[4])
172{
173 u16 raw;
174 int i,state;
175 switch (rb[0]) {
176 case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED:
177 raw = ((rb[1] & 0x0f) << 8) | rb[2];
178
179 state = !!(rb[1] & 0x40);
180
181 deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to %04x state: %d\n",rb[1],rb[2],rb[3],raw,state);
182 for (i = 0; i < sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++) {
183 if (haupp_rc_keys[i].raw == raw) {
184 if (dib->last_event == haupp_rc_keys[i].key &&
185 dib->last_state == state) {
186 deb_rc("key repeat\n");
187 return 0;
188 } else {
189 dib->last_event = haupp_rc_keys[i].key;
190 dib->last_state = state;
191 return 1;
192 }
193 }
194 }
195
196 break;
197 case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY:
198 default:
199 break;
200 }
201 return -1;
202}
203
204/*
205 * Read the remote control and feed the appropriate event.
206 * NEC protocol is used for remote controls
207 */
208static int dibusb_read_remote_control(struct usb_dibusb *dib)
209{
210 u8 b[1] = { DIBUSB_REQ_POLL_REMOTE }, rb[5];
211 int ret,event = 0;
212
213 if ((ret = dibusb_readwrite_usb(dib,b,1,rb,5)))
214 return ret;
215
216 switch (dib->dibdev->dev_cl->remote_type) {
217 case DIBUSB_RC_NEC_PROTOCOL:
218 event = dibusb_key2event_nec(dib,rb);
219 break;
220 case DIBUSB_RC_HAUPPAUGE_PROTO:
221 event = dibusb_key2event_hauppauge(dib,rb);
222 default:
223 break;
224 }
225
226 /* key repeat */
227 if (event == 0)
228 if (++dib->repeat_key_count < dib->rc_key_repeat_count) {
229 deb_rc("key repeat dropped. (%d)\n",dib->repeat_key_count);
230 event = -1; /* skip this key repeat */
231 }
232
233 if (event == 1 || event == 0) {
234 deb_rc("Translated key 0x%04x\n",event);
235
236 /* Signal down and up events for this key. */
237 input_report_key(&dib->rc_input_dev, dib->last_event, 1);
238 input_report_key(&dib->rc_input_dev, dib->last_event, 0);
239 input_sync(&dib->rc_input_dev);
240
241 if (event == 1)
242 dib->repeat_key_count = 0;
243 }
244 return 0;
245}
246
247/* Remote-control poll function - called every dib->rc_query_interval ms to see
248 whether the remote control has received anything. */
249static void dibusb_remote_query(void *data)
250{
251 struct usb_dibusb *dib = (struct usb_dibusb *) data;
252 /* TODO: need a lock here. We can simply skip checking for the remote control
253 if we're busy. */
254 dibusb_read_remote_control(dib);
255 schedule_delayed_work(&dib->rc_query_work,
256 msecs_to_jiffies(dib->rc_query_interval));
257}
258
259int dibusb_remote_init(struct usb_dibusb *dib)
260{
261 int i;
262
263 if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO)
264 return 0;
265
266 /* Initialise the remote-control structures.*/
267 init_input_dev(&dib->rc_input_dev);
268
269 dib->rc_input_dev.evbit[0] = BIT(EV_KEY);
270 dib->rc_input_dev.keycodesize = sizeof(unsigned char);
271 dib->rc_input_dev.keycodemax = KEY_MAX;
272 dib->rc_input_dev.name = DRIVER_DESC " remote control";
273
274 switch (dib->dibdev->dev_cl->remote_type) {
275 case DIBUSB_RC_NEC_PROTOCOL:
276 for (i=0; i<sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++)
277 set_bit(nec_rc_keys[i].key, dib->rc_input_dev.keybit);
278 break;
279 case DIBUSB_RC_HAUPPAUGE_PROTO:
280 for (i=0; i<sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++)
281 set_bit(haupp_rc_keys[i].key, dib->rc_input_dev.keybit);
282 break;
283 default:
284 break;
285 }
286
287
288 input_register_device(&dib->rc_input_dev);
289
290 INIT_WORK(&dib->rc_query_work, dibusb_remote_query, dib);
291
292 /* Start the remote-control polling. */
293 if (dib->rc_query_interval < 40)
294 dib->rc_query_interval = 100; /* default */
295
296 info("schedule remote query interval to %d msecs.",dib->rc_query_interval);
297 schedule_delayed_work(&dib->rc_query_work,msecs_to_jiffies(dib->rc_query_interval));
298
299 dib->init_state |= DIBUSB_STATE_REMOTE;
300
301 return 0;
302}
303
304int dibusb_remote_exit(struct usb_dibusb *dib)
305{
306 if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO)
307 return 0;
308
309 if (dib->init_state & DIBUSB_STATE_REMOTE) {
310 cancel_delayed_work(&dib->rc_query_work);
311 flush_scheduled_work();
312 input_unregister_device(&dib->rc_input_dev);
313 }
314 dib->init_state &= ~DIBUSB_STATE_REMOTE;
315 return 0;
316}