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 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 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 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 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 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 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 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