]>
Commit | Line | Data |
---|---|---|
9a4a5574 BT |
1 | /* |
2 | * HID driver for the apple ir device | |
3 | * | |
4 | * Original driver written by James McKenzie | |
5 | * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <[email protected]> | |
6 | * Updated to support newer remotes by Bastien Nocera <[email protected]> | |
7 | * Ported to HID subsystem by Benjamin Tissoires <[email protected]> | |
8 | * | |
9 | * Copyright (C) 2006 James McKenzie | |
10 | * Copyright (C) 2008 Greg Kroah-Hartman <[email protected]> | |
11 | * Copyright (C) 2008 Novell Inc. | |
12 | * Copyright (C) 2010, 2012 Bastien Nocera <[email protected]> | |
13 | * Copyright (C) 2013 Benjamin Tissoires <[email protected]> | |
14 | * Copyright (C) 2013 Red Hat Inc. All Rights Reserved | |
15 | * | |
16 | * This software is licensed under the terms of the GNU General Public | |
17 | * License version 2, as published by the Free Software Foundation, and | |
18 | * may be copied, distributed, and modified under those terms. | |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, | |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | * GNU General Public License for more details. | |
24 | */ | |
25 | ||
26 | #include <linux/device.h> | |
27 | #include <linux/hid.h> | |
28 | #include <linux/module.h> | |
29 | #include "hid-ids.h" | |
30 | ||
31 | MODULE_AUTHOR("James McKenzie"); | |
32 | MODULE_AUTHOR("Benjamin Tissoires <[email protected]>"); | |
33 | MODULE_DESCRIPTION("HID Apple IR remote controls"); | |
34 | MODULE_LICENSE("GPL"); | |
35 | ||
36 | #define KEY_MASK 0x0F | |
37 | #define TWO_PACKETS_MASK 0x40 | |
38 | ||
39 | /* | |
40 | * James McKenzie has two devices both of which report the following | |
41 | * 25 87 ee 83 0a + | |
42 | * 25 87 ee 83 0c - | |
43 | * 25 87 ee 83 09 << | |
44 | * 25 87 ee 83 06 >> | |
45 | * 25 87 ee 83 05 >" | |
46 | * 25 87 ee 83 03 menu | |
47 | * 26 00 00 00 00 for key repeat | |
48 | */ | |
49 | ||
50 | /* | |
51 | * Thomas Glanzmann reports the following responses | |
52 | * 25 87 ee ca 0b + | |
53 | * 25 87 ee ca 0d - | |
54 | * 25 87 ee ca 08 << | |
55 | * 25 87 ee ca 07 >> | |
56 | * 25 87 ee ca 04 >" | |
57 | * 25 87 ee ca 02 menu | |
58 | * 26 00 00 00 00 for key repeat | |
59 | * | |
60 | * He also observes the following event sometimes | |
61 | * sent after a key is release, which I interpret | |
62 | * as a flat battery message | |
63 | * 25 87 e0 ca 06 flat battery | |
64 | */ | |
65 | ||
66 | /* | |
67 | * Alexandre Karpenko reports the following responses for Device ID 0x8242 | |
68 | * 25 87 ee 47 0b + | |
69 | * 25 87 ee 47 0d - | |
70 | * 25 87 ee 47 08 << | |
71 | * 25 87 ee 47 07 >> | |
72 | * 25 87 ee 47 04 >" | |
73 | * 25 87 ee 47 02 menu | |
74 | * 26 87 ee 47 ** for key repeat (** is the code of the key being held) | |
75 | */ | |
76 | ||
77 | /* | |
78 | * Bastien Nocera's remote | |
79 | * 25 87 ee 91 5f followed by | |
80 | * 25 87 ee 91 05 gives you >" | |
81 | * | |
82 | * 25 87 ee 91 5c followed by | |
83 | * 25 87 ee 91 05 gives you the middle button | |
84 | */ | |
85 | ||
86 | /* | |
87 | * Fabien Andre's remote | |
88 | * 25 87 ee a3 5e followed by | |
89 | * 25 87 ee a3 04 gives you >" | |
90 | * | |
91 | * 25 87 ee a3 5d followed by | |
92 | * 25 87 ee a3 04 gives you the middle button | |
93 | */ | |
94 | ||
95 | static const unsigned short appleir_key_table[] = { | |
96 | KEY_RESERVED, | |
97 | KEY_MENU, | |
98 | KEY_PLAYPAUSE, | |
99 | KEY_FORWARD, | |
100 | KEY_BACK, | |
101 | KEY_VOLUMEUP, | |
102 | KEY_VOLUMEDOWN, | |
103 | KEY_RESERVED, | |
104 | KEY_RESERVED, | |
105 | KEY_RESERVED, | |
106 | KEY_RESERVED, | |
107 | KEY_RESERVED, | |
108 | KEY_RESERVED, | |
109 | KEY_RESERVED, | |
110 | KEY_ENTER, | |
111 | KEY_PLAYPAUSE, | |
112 | KEY_RESERVED, | |
113 | }; | |
114 | ||
115 | struct appleir { | |
116 | struct input_dev *input_dev; | |
117 | struct hid_device *hid; | |
118 | unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; | |
119 | struct timer_list key_up_timer; /* timer for key up */ | |
120 | spinlock_t lock; /* protects .current_key */ | |
121 | int current_key; /* the currently pressed key */ | |
122 | int prev_key_idx; /* key index in a 2 packets message */ | |
123 | }; | |
124 | ||
125 | static int get_key(int data) | |
126 | { | |
127 | /* | |
128 | * The key is coded accross bits 2..9: | |
129 | * | |
130 | * 0x00 or 0x01 ( ) key: 0 -> KEY_RESERVED | |
131 | * 0x02 or 0x03 ( menu ) key: 1 -> KEY_MENU | |
132 | * 0x04 or 0x05 ( >" ) key: 2 -> KEY_PLAYPAUSE | |
133 | * 0x06 or 0x07 ( >> ) key: 3 -> KEY_FORWARD | |
134 | * 0x08 or 0x09 ( << ) key: 4 -> KEY_BACK | |
135 | * 0x0a or 0x0b ( + ) key: 5 -> KEY_VOLUMEUP | |
136 | * 0x0c or 0x0d ( - ) key: 6 -> KEY_VOLUMEDOWN | |
137 | * 0x0e or 0x0f ( ) key: 7 -> KEY_RESERVED | |
138 | * 0x50 or 0x51 ( ) key: 8 -> KEY_RESERVED | |
139 | * 0x52 or 0x53 ( ) key: 9 -> KEY_RESERVED | |
140 | * 0x54 or 0x55 ( ) key: 10 -> KEY_RESERVED | |
141 | * 0x56 or 0x57 ( ) key: 11 -> KEY_RESERVED | |
142 | * 0x58 or 0x59 ( ) key: 12 -> KEY_RESERVED | |
143 | * 0x5a or 0x5b ( ) key: 13 -> KEY_RESERVED | |
144 | * 0x5c or 0x5d ( middle ) key: 14 -> KEY_ENTER | |
145 | * 0x5e or 0x5f ( >" ) key: 15 -> KEY_PLAYPAUSE | |
146 | * | |
147 | * Packets starting with 0x5 are part of a two-packets message, | |
148 | * we notify the caller by sending a negative value. | |
149 | */ | |
150 | int key = (data >> 1) & KEY_MASK; | |
151 | ||
152 | if ((data & TWO_PACKETS_MASK)) | |
153 | /* Part of a 2 packets-command */ | |
154 | key = -key; | |
155 | ||
156 | return key; | |
157 | } | |
158 | ||
159 | static void key_up(struct hid_device *hid, struct appleir *appleir, int key) | |
160 | { | |
161 | input_report_key(appleir->input_dev, key, 0); | |
162 | input_sync(appleir->input_dev); | |
163 | } | |
164 | ||
165 | static void key_down(struct hid_device *hid, struct appleir *appleir, int key) | |
166 | { | |
167 | input_report_key(appleir->input_dev, key, 1); | |
168 | input_sync(appleir->input_dev); | |
169 | } | |
170 | ||
171 | static void battery_flat(struct appleir *appleir) | |
172 | { | |
173 | dev_err(&appleir->input_dev->dev, "possible flat battery?\n"); | |
174 | } | |
175 | ||
e99e88a9 | 176 | static void key_up_tick(struct timer_list *t) |
9a4a5574 | 177 | { |
e99e88a9 | 178 | struct appleir *appleir = from_timer(appleir, t, key_up_timer); |
9a4a5574 BT |
179 | struct hid_device *hid = appleir->hid; |
180 | unsigned long flags; | |
181 | ||
182 | spin_lock_irqsave(&appleir->lock, flags); | |
183 | if (appleir->current_key) { | |
184 | key_up(hid, appleir, appleir->current_key); | |
185 | appleir->current_key = 0; | |
186 | } | |
187 | spin_unlock_irqrestore(&appleir->lock, flags); | |
188 | } | |
189 | ||
190 | static int appleir_raw_event(struct hid_device *hid, struct hid_report *report, | |
191 | u8 *data, int len) | |
192 | { | |
193 | struct appleir *appleir = hid_get_drvdata(hid); | |
194 | static const u8 keydown[] = { 0x25, 0x87, 0xee }; | |
195 | static const u8 keyrepeat[] = { 0x26, }; | |
196 | static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 }; | |
197 | unsigned long flags; | |
198 | ||
199 | if (len != 5) | |
200 | goto out; | |
201 | ||
202 | if (!memcmp(data, keydown, sizeof(keydown))) { | |
203 | int index; | |
204 | ||
205 | spin_lock_irqsave(&appleir->lock, flags); | |
206 | /* | |
207 | * If we already have a key down, take it up before marking | |
208 | * this one down | |
209 | */ | |
210 | if (appleir->current_key) | |
211 | key_up(hid, appleir, appleir->current_key); | |
212 | ||
213 | /* Handle dual packet commands */ | |
214 | if (appleir->prev_key_idx > 0) | |
215 | index = appleir->prev_key_idx; | |
216 | else | |
217 | index = get_key(data[4]); | |
218 | ||
219 | if (index >= 0) { | |
220 | appleir->current_key = appleir->keymap[index]; | |
221 | ||
222 | key_down(hid, appleir, appleir->current_key); | |
223 | /* | |
224 | * Remote doesn't do key up, either pull them up, in | |
225 | * the test above, or here set a timer which pulls | |
226 | * them up after 1/8 s | |
227 | */ | |
228 | mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); | |
229 | appleir->prev_key_idx = 0; | |
230 | } else | |
231 | /* Remember key for next packet */ | |
232 | appleir->prev_key_idx = -index; | |
233 | spin_unlock_irqrestore(&appleir->lock, flags); | |
234 | goto out; | |
235 | } | |
236 | ||
237 | appleir->prev_key_idx = 0; | |
238 | ||
239 | if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { | |
240 | key_down(hid, appleir, appleir->current_key); | |
241 | /* | |
242 | * Remote doesn't do key up, either pull them up, in the test | |
243 | * above, or here set a timer which pulls them up after 1/8 s | |
244 | */ | |
245 | mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); | |
246 | goto out; | |
247 | } | |
248 | ||
249 | if (!memcmp(data, flatbattery, sizeof(flatbattery))) { | |
250 | battery_flat(appleir); | |
251 | /* Fall through */ | |
252 | } | |
253 | ||
254 | out: | |
255 | /* let hidraw and hiddev handle the report */ | |
256 | return 0; | |
257 | } | |
258 | ||
9154301a | 259 | static int appleir_input_configured(struct hid_device *hid, |
9a4a5574 BT |
260 | struct hid_input *hidinput) |
261 | { | |
262 | struct input_dev *input_dev = hidinput->input; | |
263 | struct appleir *appleir = hid_get_drvdata(hid); | |
264 | int i; | |
265 | ||
266 | appleir->input_dev = input_dev; | |
267 | ||
268 | input_dev->keycode = appleir->keymap; | |
269 | input_dev->keycodesize = sizeof(unsigned short); | |
270 | input_dev->keycodemax = ARRAY_SIZE(appleir->keymap); | |
271 | ||
272 | input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); | |
273 | ||
274 | memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap)); | |
275 | for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++) | |
276 | set_bit(appleir->keymap[i], input_dev->keybit); | |
277 | clear_bit(KEY_RESERVED, input_dev->keybit); | |
9154301a DT |
278 | |
279 | return 0; | |
9a4a5574 BT |
280 | } |
281 | ||
282 | static int appleir_input_mapping(struct hid_device *hid, | |
283 | struct hid_input *hi, struct hid_field *field, | |
284 | struct hid_usage *usage, unsigned long **bit, int *max) | |
285 | { | |
286 | return -1; | |
287 | } | |
288 | ||
289 | static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id) | |
290 | { | |
291 | int ret; | |
292 | struct appleir *appleir; | |
293 | ||
294 | appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL); | |
295 | if (!appleir) { | |
296 | ret = -ENOMEM; | |
297 | goto allocfail; | |
298 | } | |
299 | ||
300 | appleir->hid = hid; | |
301 | ||
3d18bd41 BT |
302 | /* force input as some remotes bypass the input registration */ |
303 | hid->quirks |= HID_QUIRK_HIDINPUT_FORCE; | |
304 | ||
9a4a5574 | 305 | spin_lock_init(&appleir->lock); |
e99e88a9 | 306 | timer_setup(&appleir->key_up_timer, key_up_tick, 0); |
9a4a5574 BT |
307 | |
308 | hid_set_drvdata(hid, appleir); | |
309 | ||
310 | ret = hid_parse(hid); | |
311 | if (ret) { | |
312 | hid_err(hid, "parse failed\n"); | |
313 | goto fail; | |
314 | } | |
315 | ||
316 | ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE); | |
317 | if (ret) { | |
318 | hid_err(hid, "hw start failed\n"); | |
319 | goto fail; | |
320 | } | |
321 | ||
322 | return 0; | |
323 | fail: | |
324 | kfree(appleir); | |
325 | allocfail: | |
326 | return ret; | |
327 | } | |
328 | ||
329 | static void appleir_remove(struct hid_device *hid) | |
330 | { | |
331 | struct appleir *appleir = hid_get_drvdata(hid); | |
332 | hid_hw_stop(hid); | |
333 | del_timer_sync(&appleir->key_up_timer); | |
334 | kfree(appleir); | |
335 | } | |
336 | ||
337 | static const struct hid_device_id appleir_devices[] = { | |
338 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, | |
339 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, | |
340 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, | |
341 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, | |
342 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, | |
343 | { } | |
344 | }; | |
345 | MODULE_DEVICE_TABLE(hid, appleir_devices); | |
346 | ||
347 | static struct hid_driver appleir_driver = { | |
348 | .name = "appleir", | |
349 | .id_table = appleir_devices, | |
350 | .raw_event = appleir_raw_event, | |
351 | .input_configured = appleir_input_configured, | |
352 | .probe = appleir_probe, | |
353 | .remove = appleir_remove, | |
354 | .input_mapping = appleir_input_mapping, | |
355 | }; | |
356 | module_hid_driver(appleir_driver); |