xref: /freebsd/sys/dev/hid/appleir.c (revision a85c4ab626ef52530400ace1957daaa35dd07534)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
5  */
6 
7 /*
8  * Apple IR Remote Control Driver
9  *
10  * HID driver for Apple IR receivers (USB HID, vendor 0x05ac).
11  * Supports Apple Remote and generic IR remotes using NEC protocol.
12  *
13  * The Apple Remote protocol was reverse-engineered by James McKenzie and
14  * others; key codes and packet format constants are derived from that work
15  * and are factual descriptions of the hardware protocol, not copied code.
16  * Linux reference (GPL-2.0, no code copied): drivers/hid/hid-appleir.c
17  *
18  * Apple Remote Protocol (proprietary):
19  *   Key down:    [0x25][0x87][0xee][remote_id][key_code]
20  *   Key repeat:  [0x26][0x87][0xee][remote_id][key_code]
21  *   Battery low: [0x25][0x87][0xe0][remote_id][0x00]
22  *   Key decode:  (byte4 >> 1) & 0x0F -> keymap[index]
23  *   Two-packet:  bit 6 of key_code (0x40) set -> store index, use on next keydown
24  *
25  * Generic IR Protocol (NEC-style):
26  *   Format:     [0x26][0x7f][0x80][code][~code]
27  *   Checksum:   code + ~code = 0xFF
28  *
29  * NO hardware key-up events -- synthesize via 125ms callout timer.
30  */
31 
32 #include <sys/cdefs.h>
33 
34 #include "opt_hid.h"
35 
36 #include <sys/param.h>
37 #include <sys/bus.h>
38 #include <sys/callout.h>
39 #include <sys/kernel.h>
40 #include <sys/lock.h>
41 #include <sys/malloc.h>
42 #include <sys/module.h>
43 #include <sys/mutex.h>
44 #include <sys/sysctl.h>
45 
46 #include <dev/evdev/input.h>
47 #include <dev/evdev/evdev.h>
48 
49 #define HID_DEBUG_VAR	appleir_debug
50 #include <dev/hid/hid.h>
51 #include <dev/hid/hidbus.h>
52 #include "usbdevs.h"
53 
54 #ifdef HID_DEBUG
55 static int appleir_debug = 0;
56 
57 static SYSCTL_NODE(_hw_hid, OID_AUTO, appleir, CTLFLAG_RW, 0,
58     "Apple IR Remote Control");
59 SYSCTL_INT(_hw_hid_appleir, OID_AUTO, debug, CTLFLAG_RWTUN,
60     &appleir_debug, 0, "Debug level");
61 #endif
62 
63 /* Protocol constants */
64 #define	APPLEIR_REPORT_LEN	5
65 #define	APPLEIR_KEY_MASK	0x0F
66 #define	APPLEIR_TWO_PKT_FLAG	0x40	/* bit 6: two-packet command */
67 #define	APPLEIR_KEYUP_TICKS	MAX(1, hz / 8)	/* 125ms */
68 #define	APPLEIR_TWOPKT_TICKS	MAX(1, hz / 4)	/* 250ms */
69 
70 /* Report type markers (byte 0) */
71 #define	APPLEIR_PKT_KEYDOWN	0x25	/* key down / battery low */
72 #define	APPLEIR_PKT_REPEAT	0x26	/* key repeat / NEC generic */
73 
74 /* Apple Remote signature (bytes 1-2) */
75 #define	APPLEIR_SIG_HI		0x87
76 #define	APPLEIR_SIG_KEYLO	0xee	/* normal key event */
77 #define	APPLEIR_SIG_BATTLO	0xe0	/* battery low event */
78 
79 /* Generic IR NEC signature (bytes 1-2) */
80 #define	APPLEIR_NEC_HI		0x7f
81 #define	APPLEIR_NEC_LO		0x80
82 #define	APPLEIR_NEC_CHECKSUM	0xFF	/* code + ~code must equal this */
83 
84 /*
85  * Apple IR keymap: 17 entries, index = (key_code >> 1) & 0x0F
86  * Based on Linux driver (hid-appleir.c) keymap.
87  */
88 static const uint16_t appleir_keymap[] = {
89 	KEY_RESERVED,		/* 0x00 */
90 	KEY_MENU,		/* 0x01 - menu */
91 	KEY_PLAYPAUSE,		/* 0x02 - play/pause */
92 	KEY_FORWARD,		/* 0x03 - >> */
93 	KEY_BACK,		/* 0x04 - << */
94 	KEY_VOLUMEUP,		/* 0x05 - + */
95 	KEY_VOLUMEDOWN,		/* 0x06 - - */
96 	KEY_RESERVED,		/* 0x07 */
97 	KEY_RESERVED,		/* 0x08 */
98 	KEY_RESERVED,		/* 0x09 */
99 	KEY_RESERVED,		/* 0x0A */
100 	KEY_RESERVED,		/* 0x0B */
101 	KEY_RESERVED,		/* 0x0C */
102 	KEY_RESERVED,		/* 0x0D */
103 	KEY_ENTER,		/* 0x0E - middle button (two-packet) */
104 	KEY_PLAYPAUSE,		/* 0x0F - play/pause (two-packet) */
105 	KEY_RESERVED,		/* 0x10 - out of range guard */
106 };
107 #define APPLEIR_NKEYS	(nitems(appleir_keymap))
108 
109 /*
110  * Generic IR keymap (NEC protocol codes).
111  * Maps raw NEC codes to evdev KEY_* codes.
112  */
113 struct generic_ir_map {
114 	uint8_t		code;		/* NEC IR code */
115 	uint16_t	key;		/* evdev KEY_* */
116 };
117 
118 static const struct generic_ir_map generic_keymap[] = {
119 	{ 0xe1, KEY_VOLUMEUP },
120 	{ 0xe9, KEY_VOLUMEDOWN },
121 	{ 0xed, KEY_CHANNELUP },
122 	{ 0xf3, KEY_CHANNELDOWN },
123 	{ 0xf5, KEY_PLAYPAUSE },
124 	{ 0xf9, KEY_POWER },
125 	{ 0xfb, KEY_MUTE },
126 	{ 0xfe, KEY_OK },
127 };
128 #define GENERIC_NKEYS	(nitems(generic_keymap))
129 
130 static uint16_t
generic_ir_lookup(uint8_t code)131 generic_ir_lookup(uint8_t code)
132 {
133 	int i;
134 
135 	for (i = 0; i < GENERIC_NKEYS; i++) {
136 		if (generic_keymap[i].code == code)
137 			return (generic_keymap[i].key);
138 	}
139 	return (KEY_RESERVED);
140 }
141 
142 struct appleir_softc {
143 	device_t		sc_dev;
144 	struct mtx		sc_mtx;		/* protects below + callout */
145 	struct evdev_dev	*sc_evdev;
146 	struct callout		sc_co;		/* key-up timer */
147 	struct callout		sc_twoco;	/* two-packet timeout */
148 	uint16_t		sc_current_key;	/* evdev keycode (0=none) */
149 	int			sc_prev_key_idx;/* two-packet state (0=none) */
150 	bool			sc_batt_warned;
151 };
152 
153 
154 /*
155  * Callout: synthesize key-up event (no hardware key-up from remote).
156  * Runs with sc_mtx held (callout_init_mtx).
157  */
158 static void
appleir_keyup(void * arg)159 appleir_keyup(void *arg)
160 {
161 	struct appleir_softc *sc = arg;
162 
163 	mtx_assert(&sc->sc_mtx, MA_OWNED);
164 
165 	if (sc->sc_current_key != 0) {
166 		evdev_push_key(sc->sc_evdev, sc->sc_current_key, 0);
167 		evdev_sync(sc->sc_evdev);
168 		sc->sc_current_key = 0;
169 		sc->sc_prev_key_idx = 0;
170 	}
171 }
172 
173 static void
appleir_twopacket_timeout(void * arg)174 appleir_twopacket_timeout(void *arg)
175 {
176 	struct appleir_softc *sc = arg;
177 
178 	mtx_assert(&sc->sc_mtx, MA_OWNED);
179 	sc->sc_prev_key_idx = 0;
180 }
181 
182 /*
183  * Process 5-byte HID interrupt report.
184  * Called from hidbus interrupt context.
185  */
186 static void
appleir_intr(void * context,void * data,hid_size_t len)187 appleir_intr(void *context, void *data, hid_size_t len)
188 {
189 	struct appleir_softc *sc = context;
190 	uint8_t *buf = data;
191 	uint8_t report[APPLEIR_REPORT_LEN];
192 	int index;
193 	uint16_t new_key;
194 
195 	if (len != APPLEIR_REPORT_LEN) {
196 		DPRINTFN(1, "bad report len: %zu\n", (size_t)len);
197 		return;
198 	}
199 
200 	memcpy(report, buf, APPLEIR_REPORT_LEN);
201 
202 	mtx_lock(&sc->sc_mtx);
203 
204 	/* Battery low: [KEYDOWN][SIG_HI][SIG_BATTLO] -- log and ignore */
205 	if (report[0] == APPLEIR_PKT_KEYDOWN &&
206 	    report[1] == APPLEIR_SIG_HI && report[2] == APPLEIR_SIG_BATTLO) {
207 		if (!sc->sc_batt_warned) {
208 			device_printf(sc->sc_dev,
209 			    "remote battery may be low\n");
210 			sc->sc_batt_warned = true;
211 		}
212 		goto done;
213 	}
214 
215 	/* Key down: [KEYDOWN][SIG_HI][SIG_KEYLO][remote_id][key_code] */
216 	if (report[0] == APPLEIR_PKT_KEYDOWN &&
217 	    report[1] == APPLEIR_SIG_HI && report[2] == APPLEIR_SIG_KEYLO) {
218 		/* Release previous key if held */
219 		if (sc->sc_current_key != 0) {
220 			evdev_push_key(sc->sc_evdev, sc->sc_current_key, 0);
221 			evdev_sync(sc->sc_evdev);
222 			sc->sc_current_key = 0;
223 		}
224 
225 		if (sc->sc_prev_key_idx > 0) {
226 			/* Second packet of a two-packet command */
227 			index = sc->sc_prev_key_idx;
228 			sc->sc_prev_key_idx = 0;
229 			callout_stop(&sc->sc_twoco);
230 		} else if (report[4] & APPLEIR_TWO_PKT_FLAG) {
231 			/* First packet of a two-packet command -- wait for next */
232 			sc->sc_prev_key_idx = (report[4] >> 1) & APPLEIR_KEY_MASK;
233 			callout_reset(&sc->sc_twoco, APPLEIR_TWOPKT_TICKS,
234 			    appleir_twopacket_timeout, sc);
235 			goto done;
236 		} else {
237 			index = (report[4] >> 1) & APPLEIR_KEY_MASK;
238 		}
239 
240 		new_key = (index < APPLEIR_NKEYS) ?
241 		    appleir_keymap[index] : KEY_RESERVED;
242 		if (new_key != KEY_RESERVED) {
243 			sc->sc_current_key = new_key;
244 			evdev_push_key(sc->sc_evdev, new_key, 1);
245 			evdev_sync(sc->sc_evdev);
246 			callout_reset(&sc->sc_co, APPLEIR_KEYUP_TICKS,
247 			    appleir_keyup, sc);
248 		}
249 		goto done;
250 	}
251 
252 	/* Key repeat: [REPEAT][SIG_HI][SIG_KEYLO][remote_id][key_code] */
253 	if (report[0] == APPLEIR_PKT_REPEAT &&
254 	    report[1] == APPLEIR_SIG_HI && report[2] == APPLEIR_SIG_KEYLO) {
255 		uint16_t repeat_key;
256 		int repeat_idx;
257 
258 		if (sc->sc_prev_key_idx > 0)
259 			goto done;
260 		if (report[4] & APPLEIR_TWO_PKT_FLAG)
261 			goto done;
262 
263 		repeat_idx = (report[4] >> 1) & APPLEIR_KEY_MASK;
264 		repeat_key = (repeat_idx < APPLEIR_NKEYS) ?
265 		    appleir_keymap[repeat_idx] : KEY_RESERVED;
266 		if (repeat_key == KEY_RESERVED ||
267 		    repeat_key != sc->sc_current_key)
268 			goto done;
269 
270 		evdev_push_key(sc->sc_evdev, repeat_key, 1);
271 		evdev_sync(sc->sc_evdev);
272 		callout_reset(&sc->sc_co, APPLEIR_KEYUP_TICKS,
273 		    appleir_keyup, sc);
274 		goto done;
275 	}
276 
277 	/* Generic IR (NEC protocol): [REPEAT][NEC_HI][NEC_LO][code][~code] */
278 	if (report[0] == APPLEIR_PKT_REPEAT &&
279 	    report[1] == APPLEIR_NEC_HI && report[2] == APPLEIR_NEC_LO) {
280 		uint8_t code = report[3];
281 		uint8_t checksum = report[4];
282 
283 		sc->sc_prev_key_idx = 0;
284 		callout_stop(&sc->sc_twoco);
285 
286 		if ((uint8_t)(code + checksum) != APPLEIR_NEC_CHECKSUM) {
287 			DPRINTFN(1, "generic IR: bad checksum %02x+%02x\n",
288 			    code, checksum);
289 			goto done;
290 		}
291 
292 		new_key = generic_ir_lookup(code);
293 		if (new_key == KEY_RESERVED)
294 			goto done;
295 
296 		if (sc->sc_current_key != new_key) {
297 			if (sc->sc_current_key != 0)
298 				evdev_push_key(sc->sc_evdev,
299 				    sc->sc_current_key, 0);
300 			sc->sc_current_key = new_key;
301 			evdev_push_key(sc->sc_evdev, new_key, 1);
302 			evdev_sync(sc->sc_evdev);
303 		} else {
304 			evdev_push_key(sc->sc_evdev, new_key, 1);
305 			evdev_sync(sc->sc_evdev);
306 		}
307 		callout_reset(&sc->sc_co, APPLEIR_KEYUP_TICKS,
308 		    appleir_keyup, sc);
309 		goto done;
310 	}
311 
312 	DPRINTFN(1, "unknown report: %02x %02x %02x\n",
313 	    report[0], report[1], report[2]);
314 
315 done:
316 	mtx_unlock(&sc->sc_mtx);
317 }
318 
319 /* Apple IR receiver device IDs */
320 static const struct hid_device_id appleir_devs[] = {
321 	{ HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8240) },
322 	{ HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8241) },
323 	{ HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8242) },
324 	{ HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8243) },
325 	{ HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x1440) },
326 };
327 
328 static int
appleir_probe(device_t dev)329 appleir_probe(device_t dev)
330 {
331 	int error;
332 
333 	error = HIDBUS_LOOKUP_DRIVER_INFO(dev, appleir_devs);
334 	if (error != 0)
335 		return (error);
336 
337 	/* Only attach to first top-level collection (TLC index 0) */
338 	if (hidbus_get_index(dev) != 0)
339 		return (ENXIO);
340 
341 	hidbus_set_desc(dev, "Apple IR Receiver");
342 	return (BUS_PROBE_DEFAULT);
343 }
344 
345 static int
appleir_attach(device_t dev)346 appleir_attach(device_t dev)
347 {
348 	struct appleir_softc *sc = device_get_softc(dev);
349 	const struct hid_device_info *hw;
350 	int i, error;
351 
352 	sc->sc_dev = dev;
353 	hw = hid_get_device_info(dev);
354 	sc->sc_current_key = 0;
355 	sc->sc_prev_key_idx = 0;
356 	sc->sc_batt_warned = false;
357 	mtx_init(&sc->sc_mtx, "appleir", NULL, MTX_DEF);
358 	callout_init_mtx(&sc->sc_co, &sc->sc_mtx, 0);
359 	callout_init_mtx(&sc->sc_twoco, &sc->sc_mtx, 0);
360 
361 	sc->sc_evdev = evdev_alloc();
362 	evdev_set_name(sc->sc_evdev, device_get_desc(dev));
363 	evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev));
364 	evdev_set_id(sc->sc_evdev, hw->idBus, hw->idVendor, hw->idProduct,
365 	    hw->idVersion);
366 	evdev_set_serial(sc->sc_evdev, hw->serial);
367 	evdev_support_event(sc->sc_evdev, EV_SYN);
368 	evdev_support_event(sc->sc_evdev, EV_KEY);
369 	evdev_support_event(sc->sc_evdev, EV_REP);
370 
371 	for (i = 0; i < APPLEIR_NKEYS; i++) {
372 		if (appleir_keymap[i] != KEY_RESERVED)
373 			evdev_support_key(sc->sc_evdev, appleir_keymap[i]);
374 	}
375 	for (i = 0; i < GENERIC_NKEYS; i++)
376 		evdev_support_key(sc->sc_evdev, generic_keymap[i].key);
377 
378 	error = evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx);
379 	if (error != 0) {
380 		device_printf(dev, "evdev_register_mtx failed: %d\n", error);
381 		goto fail;
382 	}
383 
384 	hidbus_set_intr(dev, appleir_intr, sc);
385 
386 	error = hid_intr_start(dev);
387 	if (error != 0) {
388 		device_printf(dev, "hid_intr_start failed: %d\n", error);
389 		goto fail;
390 	}
391 
392 	return (0);
393 
394 fail:
395 	if (sc->sc_evdev != NULL)
396 		evdev_free(sc->sc_evdev);
397 	callout_drain(&sc->sc_co);
398 	callout_drain(&sc->sc_twoco);
399 	mtx_destroy(&sc->sc_mtx);
400 	return (error);
401 }
402 
403 static int
appleir_detach(device_t dev)404 appleir_detach(device_t dev)
405 {
406 	struct appleir_softc *sc = device_get_softc(dev);
407 	int error;
408 
409 	error = hid_intr_stop(dev);
410 	if (error != 0) {
411 		device_printf(dev, "hid_intr_stop failed: %d\n", error);
412 		return (error);
413 	}
414 	callout_drain(&sc->sc_co);
415 	callout_drain(&sc->sc_twoco);
416 	evdev_free(sc->sc_evdev);
417 	mtx_destroy(&sc->sc_mtx);
418 
419 	return (0);
420 }
421 
422 static device_method_t appleir_methods[] = {
423 	DEVMETHOD(device_probe,		appleir_probe),
424 	DEVMETHOD(device_attach,	appleir_attach),
425 	DEVMETHOD(device_detach,	appleir_detach),
426 	DEVMETHOD_END
427 };
428 
429 static driver_t appleir_driver = {
430 	"appleir",
431 	appleir_methods,
432 	sizeof(struct appleir_softc)
433 };
434 
435 DRIVER_MODULE(appleir, hidbus, appleir_driver, NULL, NULL);
436 MODULE_DEPEND(appleir, hid, 1, 1, 1);
437 MODULE_DEPEND(appleir, hidbus, 1, 1, 1);
438 MODULE_DEPEND(appleir, evdev, 1, 1, 1);
439 MODULE_VERSION(appleir, 1);
440 HID_PNP_INFO(appleir_devs);
441