appleir: Add Apple IR receiver driver
HID driver for Apple IR receivers (USB HID, vendor 0x05ac). Supports Apple Remote and generic IR remotes using NEC protocol. Supported hardware: - Apple IR Receiver (0x8240, 0x8241, 0x8242, 0x8243, 0x1440) Apple Remote protocol (proprietary 5-byte HID reports): - Key down/repeat/battery-low detection - 17-key mapping with two-packet command support - Synthesized key-up via 125ms callout timer Generic IR remotes (NEC protocol): - Format: [0x26][0x7f][0x80][code][~code] - Checksum: code + ~code = 0xFF - Default keymap with 8 common codes - See: https://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol Output via evdev with standard KEY_* codes. Raw HID access available at /dev/hidraw0 for custom remapping. Based on protocol reverse-engineering by James McKenzie et al. Reference: drivers/hid/hid-appleir.c (Linux) Tested on Mac Mini 2011 (0x05ac:0x8242). Differential Revision: https://reviews.freebsd.org/D55472
This commit is contained in:
committed by
Enji Cooper
parent
2d13620c6e
commit
a85c4ab626
@@ -44,6 +44,7 @@ MAN= aac.4 \
|
|||||||
alc.4 \
|
alc.4 \
|
||||||
ale.4 \
|
ale.4 \
|
||||||
alpm.4 \
|
alpm.4 \
|
||||||
|
appleir.4 \
|
||||||
altq.4 \
|
altq.4 \
|
||||||
amdpm.4 \
|
amdpm.4 \
|
||||||
${_amdsbwd.4} \
|
${_amdsbwd.4} \
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
.\" Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
|
||||||
|
.\"
|
||||||
|
.\" SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
.\"
|
||||||
|
.Dd February 13, 2026
|
||||||
|
.Dt APPLEIR 4
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm appleir
|
||||||
|
.Nd Apple IR receiver driver
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
To compile this driver into the kernel,
|
||||||
|
place the following lines in your
|
||||||
|
kernel configuration file:
|
||||||
|
.Bd -ragged -offset indent
|
||||||
|
.Cd "device appleir"
|
||||||
|
.Cd "device hidbus"
|
||||||
|
.Cd "device hid"
|
||||||
|
.Cd "device evdev"
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Alternatively, to load the driver as a
|
||||||
|
module at boot time, place the following line in
|
||||||
|
.Xr loader.conf 5 Ns :
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
appleir_load="YES"
|
||||||
|
.Ed
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
driver provides support for Apple IR receivers found in Mac computers
|
||||||
|
(2006-2011 era).
|
||||||
|
It supports both Apple Remote controls and generic IR remotes using the
|
||||||
|
NEC infrared protocol.
|
||||||
|
.Pp
|
||||||
|
Supported devices include:
|
||||||
|
.Bl -bullet -compact
|
||||||
|
.It
|
||||||
|
Apple IR Receiver (USB product IDs 0x8240, 0x8241, 0x8242, 0x8243, 0x1440)
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
The driver decodes proprietary Apple Remote button presses and provides
|
||||||
|
a default keymap for common NEC protocol codes used by generic IR remotes.
|
||||||
|
Unmapped button codes can be accessed via the raw HID device at
|
||||||
|
.Pa /dev/hidrawX
|
||||||
|
for custom userland remapping.
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Pa /dev/input/eventX
|
||||||
|
device presents the remote control as an
|
||||||
|
evdev
|
||||||
|
input device with standard KEY_* codes suitable for media applications.
|
||||||
|
.Sh HARDWARE
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
driver supports Apple IR receivers with USB vendor ID 0x05ac and the
|
||||||
|
following product IDs:
|
||||||
|
.Pp
|
||||||
|
.Bl -tag -width "0x8242" -compact
|
||||||
|
.It 0x8240
|
||||||
|
Apple IR Receiver (first generation)
|
||||||
|
.It 0x8241
|
||||||
|
Apple IR Receiver
|
||||||
|
.It 0x8242
|
||||||
|
Apple IR Receiver (Mac Mini 2011, MacBook Pro 3,1)
|
||||||
|
.It 0x8243
|
||||||
|
Apple IR Receiver
|
||||||
|
.It 0x1440
|
||||||
|
Apple IR Receiver (slim)
|
||||||
|
.El
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width ".Pa /dev/input/eventX" -compact
|
||||||
|
.It Pa /dev/input/eventX
|
||||||
|
evdev input device
|
||||||
|
.It Pa /dev/hidrawX
|
||||||
|
raw HID device for custom button mapping
|
||||||
|
.El
|
||||||
|
.Sh SEE ALSO
|
||||||
|
evdev ,
|
||||||
|
.Xr hidbus 4 ,
|
||||||
|
.Xr usbhid 4
|
||||||
|
.Pp
|
||||||
|
NEC Infrared Transmission Protocol:
|
||||||
|
.Lk https://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol
|
||||||
|
.Sh HISTORY
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
driver first appeared in
|
||||||
|
.Fx 16.0 .
|
||||||
|
.Sh AUTHORS
|
||||||
|
.An Abdelkader Boudih Aq Mt freebsd@seuros.com
|
||||||
|
.Pp
|
||||||
|
Based on protocol reverse-engineering by James McKenzie and others.
|
||||||
@@ -1775,6 +1775,7 @@ dev/gpio/gpio_if.m optional gpio
|
|||||||
dev/gpio/gpiobus_if.m optional gpio
|
dev/gpio/gpiobus_if.m optional gpio
|
||||||
dev/gpio/gpiopps.c optional gpiopps fdt
|
dev/gpio/gpiopps.c optional gpiopps fdt
|
||||||
dev/gpio/ofw_gpiobus.c optional fdt gpio
|
dev/gpio/ofw_gpiobus.c optional fdt gpio
|
||||||
|
dev/hid/appleir.c optional appleir
|
||||||
dev/hid/bcm5974.c optional bcm5974
|
dev/hid/bcm5974.c optional bcm5974
|
||||||
dev/hid/hconf.c optional hconf
|
dev/hid/hconf.c optional hconf
|
||||||
dev/hid/hcons.c optional hcons
|
dev/hid/hcons.c optional hcons
|
||||||
|
|||||||
@@ -0,0 +1,440 @@
|
|||||||
|
/*-
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apple IR Remote Control Driver
|
||||||
|
*
|
||||||
|
* HID driver for Apple IR receivers (USB HID, vendor 0x05ac).
|
||||||
|
* Supports Apple Remote and generic IR remotes using NEC protocol.
|
||||||
|
*
|
||||||
|
* The Apple Remote protocol was reverse-engineered by James McKenzie and
|
||||||
|
* others; key codes and packet format constants are derived from that work
|
||||||
|
* and are factual descriptions of the hardware protocol, not copied code.
|
||||||
|
* Linux reference (GPL-2.0, no code copied): drivers/hid/hid-appleir.c
|
||||||
|
*
|
||||||
|
* Apple Remote Protocol (proprietary):
|
||||||
|
* Key down: [0x25][0x87][0xee][remote_id][key_code]
|
||||||
|
* Key repeat: [0x26][0x87][0xee][remote_id][key_code]
|
||||||
|
* Battery low: [0x25][0x87][0xe0][remote_id][0x00]
|
||||||
|
* Key decode: (byte4 >> 1) & 0x0F -> keymap[index]
|
||||||
|
* Two-packet: bit 6 of key_code (0x40) set -> store index, use on next keydown
|
||||||
|
*
|
||||||
|
* Generic IR Protocol (NEC-style):
|
||||||
|
* Format: [0x26][0x7f][0x80][code][~code]
|
||||||
|
* Checksum: code + ~code = 0xFF
|
||||||
|
*
|
||||||
|
* NO hardware key-up events -- synthesize via 125ms callout timer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
|
||||||
|
#include "opt_hid.h"
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/callout.h>
|
||||||
|
#include <sys/kernel.h>
|
||||||
|
#include <sys/lock.h>
|
||||||
|
#include <sys/malloc.h>
|
||||||
|
#include <sys/module.h>
|
||||||
|
#include <sys/mutex.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
|
#include <dev/evdev/input.h>
|
||||||
|
#include <dev/evdev/evdev.h>
|
||||||
|
|
||||||
|
#define HID_DEBUG_VAR appleir_debug
|
||||||
|
#include <dev/hid/hid.h>
|
||||||
|
#include <dev/hid/hidbus.h>
|
||||||
|
#include "usbdevs.h"
|
||||||
|
|
||||||
|
#ifdef HID_DEBUG
|
||||||
|
static int appleir_debug = 0;
|
||||||
|
|
||||||
|
static SYSCTL_NODE(_hw_hid, OID_AUTO, appleir, CTLFLAG_RW, 0,
|
||||||
|
"Apple IR Remote Control");
|
||||||
|
SYSCTL_INT(_hw_hid_appleir, OID_AUTO, debug, CTLFLAG_RWTUN,
|
||||||
|
&appleir_debug, 0, "Debug level");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Protocol constants */
|
||||||
|
#define APPLEIR_REPORT_LEN 5
|
||||||
|
#define APPLEIR_KEY_MASK 0x0F
|
||||||
|
#define APPLEIR_TWO_PKT_FLAG 0x40 /* bit 6: two-packet command */
|
||||||
|
#define APPLEIR_KEYUP_TICKS MAX(1, hz / 8) /* 125ms */
|
||||||
|
#define APPLEIR_TWOPKT_TICKS MAX(1, hz / 4) /* 250ms */
|
||||||
|
|
||||||
|
/* Report type markers (byte 0) */
|
||||||
|
#define APPLEIR_PKT_KEYDOWN 0x25 /* key down / battery low */
|
||||||
|
#define APPLEIR_PKT_REPEAT 0x26 /* key repeat / NEC generic */
|
||||||
|
|
||||||
|
/* Apple Remote signature (bytes 1-2) */
|
||||||
|
#define APPLEIR_SIG_HI 0x87
|
||||||
|
#define APPLEIR_SIG_KEYLO 0xee /* normal key event */
|
||||||
|
#define APPLEIR_SIG_BATTLO 0xe0 /* battery low event */
|
||||||
|
|
||||||
|
/* Generic IR NEC signature (bytes 1-2) */
|
||||||
|
#define APPLEIR_NEC_HI 0x7f
|
||||||
|
#define APPLEIR_NEC_LO 0x80
|
||||||
|
#define APPLEIR_NEC_CHECKSUM 0xFF /* code + ~code must equal this */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apple IR keymap: 17 entries, index = (key_code >> 1) & 0x0F
|
||||||
|
* Based on Linux driver (hid-appleir.c) keymap.
|
||||||
|
*/
|
||||||
|
static const uint16_t appleir_keymap[] = {
|
||||||
|
KEY_RESERVED, /* 0x00 */
|
||||||
|
KEY_MENU, /* 0x01 - menu */
|
||||||
|
KEY_PLAYPAUSE, /* 0x02 - play/pause */
|
||||||
|
KEY_FORWARD, /* 0x03 - >> */
|
||||||
|
KEY_BACK, /* 0x04 - << */
|
||||||
|
KEY_VOLUMEUP, /* 0x05 - + */
|
||||||
|
KEY_VOLUMEDOWN, /* 0x06 - - */
|
||||||
|
KEY_RESERVED, /* 0x07 */
|
||||||
|
KEY_RESERVED, /* 0x08 */
|
||||||
|
KEY_RESERVED, /* 0x09 */
|
||||||
|
KEY_RESERVED, /* 0x0A */
|
||||||
|
KEY_RESERVED, /* 0x0B */
|
||||||
|
KEY_RESERVED, /* 0x0C */
|
||||||
|
KEY_RESERVED, /* 0x0D */
|
||||||
|
KEY_ENTER, /* 0x0E - middle button (two-packet) */
|
||||||
|
KEY_PLAYPAUSE, /* 0x0F - play/pause (two-packet) */
|
||||||
|
KEY_RESERVED, /* 0x10 - out of range guard */
|
||||||
|
};
|
||||||
|
#define APPLEIR_NKEYS (nitems(appleir_keymap))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic IR keymap (NEC protocol codes).
|
||||||
|
* Maps raw NEC codes to evdev KEY_* codes.
|
||||||
|
*/
|
||||||
|
struct generic_ir_map {
|
||||||
|
uint8_t code; /* NEC IR code */
|
||||||
|
uint16_t key; /* evdev KEY_* */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct generic_ir_map generic_keymap[] = {
|
||||||
|
{ 0xe1, KEY_VOLUMEUP },
|
||||||
|
{ 0xe9, KEY_VOLUMEDOWN },
|
||||||
|
{ 0xed, KEY_CHANNELUP },
|
||||||
|
{ 0xf3, KEY_CHANNELDOWN },
|
||||||
|
{ 0xf5, KEY_PLAYPAUSE },
|
||||||
|
{ 0xf9, KEY_POWER },
|
||||||
|
{ 0xfb, KEY_MUTE },
|
||||||
|
{ 0xfe, KEY_OK },
|
||||||
|
};
|
||||||
|
#define GENERIC_NKEYS (nitems(generic_keymap))
|
||||||
|
|
||||||
|
static uint16_t
|
||||||
|
generic_ir_lookup(uint8_t code)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < GENERIC_NKEYS; i++) {
|
||||||
|
if (generic_keymap[i].code == code)
|
||||||
|
return (generic_keymap[i].key);
|
||||||
|
}
|
||||||
|
return (KEY_RESERVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct appleir_softc {
|
||||||
|
device_t sc_dev;
|
||||||
|
struct mtx sc_mtx; /* protects below + callout */
|
||||||
|
struct evdev_dev *sc_evdev;
|
||||||
|
struct callout sc_co; /* key-up timer */
|
||||||
|
struct callout sc_twoco; /* two-packet timeout */
|
||||||
|
uint16_t sc_current_key; /* evdev keycode (0=none) */
|
||||||
|
int sc_prev_key_idx;/* two-packet state (0=none) */
|
||||||
|
bool sc_batt_warned;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callout: synthesize key-up event (no hardware key-up from remote).
|
||||||
|
* Runs with sc_mtx held (callout_init_mtx).
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
appleir_keyup(void *arg)
|
||||||
|
{
|
||||||
|
struct appleir_softc *sc = arg;
|
||||||
|
|
||||||
|
mtx_assert(&sc->sc_mtx, MA_OWNED);
|
||||||
|
|
||||||
|
if (sc->sc_current_key != 0) {
|
||||||
|
evdev_push_key(sc->sc_evdev, sc->sc_current_key, 0);
|
||||||
|
evdev_sync(sc->sc_evdev);
|
||||||
|
sc->sc_current_key = 0;
|
||||||
|
sc->sc_prev_key_idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
appleir_twopacket_timeout(void *arg)
|
||||||
|
{
|
||||||
|
struct appleir_softc *sc = arg;
|
||||||
|
|
||||||
|
mtx_assert(&sc->sc_mtx, MA_OWNED);
|
||||||
|
sc->sc_prev_key_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process 5-byte HID interrupt report.
|
||||||
|
* Called from hidbus interrupt context.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
appleir_intr(void *context, void *data, hid_size_t len)
|
||||||
|
{
|
||||||
|
struct appleir_softc *sc = context;
|
||||||
|
uint8_t *buf = data;
|
||||||
|
uint8_t report[APPLEIR_REPORT_LEN];
|
||||||
|
int index;
|
||||||
|
uint16_t new_key;
|
||||||
|
|
||||||
|
if (len != APPLEIR_REPORT_LEN) {
|
||||||
|
DPRINTFN(1, "bad report len: %zu\n", (size_t)len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(report, buf, APPLEIR_REPORT_LEN);
|
||||||
|
|
||||||
|
mtx_lock(&sc->sc_mtx);
|
||||||
|
|
||||||
|
/* Battery low: [KEYDOWN][SIG_HI][SIG_BATTLO] -- log and ignore */
|
||||||
|
if (report[0] == APPLEIR_PKT_KEYDOWN &&
|
||||||
|
report[1] == APPLEIR_SIG_HI && report[2] == APPLEIR_SIG_BATTLO) {
|
||||||
|
if (!sc->sc_batt_warned) {
|
||||||
|
device_printf(sc->sc_dev,
|
||||||
|
"remote battery may be low\n");
|
||||||
|
sc->sc_batt_warned = true;
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Key down: [KEYDOWN][SIG_HI][SIG_KEYLO][remote_id][key_code] */
|
||||||
|
if (report[0] == APPLEIR_PKT_KEYDOWN &&
|
||||||
|
report[1] == APPLEIR_SIG_HI && report[2] == APPLEIR_SIG_KEYLO) {
|
||||||
|
/* Release previous key if held */
|
||||||
|
if (sc->sc_current_key != 0) {
|
||||||
|
evdev_push_key(sc->sc_evdev, sc->sc_current_key, 0);
|
||||||
|
evdev_sync(sc->sc_evdev);
|
||||||
|
sc->sc_current_key = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sc->sc_prev_key_idx > 0) {
|
||||||
|
/* Second packet of a two-packet command */
|
||||||
|
index = sc->sc_prev_key_idx;
|
||||||
|
sc->sc_prev_key_idx = 0;
|
||||||
|
callout_stop(&sc->sc_twoco);
|
||||||
|
} else if (report[4] & APPLEIR_TWO_PKT_FLAG) {
|
||||||
|
/* First packet of a two-packet command -- wait for next */
|
||||||
|
sc->sc_prev_key_idx = (report[4] >> 1) & APPLEIR_KEY_MASK;
|
||||||
|
callout_reset(&sc->sc_twoco, APPLEIR_TWOPKT_TICKS,
|
||||||
|
appleir_twopacket_timeout, sc);
|
||||||
|
goto done;
|
||||||
|
} else {
|
||||||
|
index = (report[4] >> 1) & APPLEIR_KEY_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_key = (index < APPLEIR_NKEYS) ?
|
||||||
|
appleir_keymap[index] : KEY_RESERVED;
|
||||||
|
if (new_key != KEY_RESERVED) {
|
||||||
|
sc->sc_current_key = new_key;
|
||||||
|
evdev_push_key(sc->sc_evdev, new_key, 1);
|
||||||
|
evdev_sync(sc->sc_evdev);
|
||||||
|
callout_reset(&sc->sc_co, APPLEIR_KEYUP_TICKS,
|
||||||
|
appleir_keyup, sc);
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Key repeat: [REPEAT][SIG_HI][SIG_KEYLO][remote_id][key_code] */
|
||||||
|
if (report[0] == APPLEIR_PKT_REPEAT &&
|
||||||
|
report[1] == APPLEIR_SIG_HI && report[2] == APPLEIR_SIG_KEYLO) {
|
||||||
|
uint16_t repeat_key;
|
||||||
|
int repeat_idx;
|
||||||
|
|
||||||
|
if (sc->sc_prev_key_idx > 0)
|
||||||
|
goto done;
|
||||||
|
if (report[4] & APPLEIR_TWO_PKT_FLAG)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
repeat_idx = (report[4] >> 1) & APPLEIR_KEY_MASK;
|
||||||
|
repeat_key = (repeat_idx < APPLEIR_NKEYS) ?
|
||||||
|
appleir_keymap[repeat_idx] : KEY_RESERVED;
|
||||||
|
if (repeat_key == KEY_RESERVED ||
|
||||||
|
repeat_key != sc->sc_current_key)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
evdev_push_key(sc->sc_evdev, repeat_key, 1);
|
||||||
|
evdev_sync(sc->sc_evdev);
|
||||||
|
callout_reset(&sc->sc_co, APPLEIR_KEYUP_TICKS,
|
||||||
|
appleir_keyup, sc);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic IR (NEC protocol): [REPEAT][NEC_HI][NEC_LO][code][~code] */
|
||||||
|
if (report[0] == APPLEIR_PKT_REPEAT &&
|
||||||
|
report[1] == APPLEIR_NEC_HI && report[2] == APPLEIR_NEC_LO) {
|
||||||
|
uint8_t code = report[3];
|
||||||
|
uint8_t checksum = report[4];
|
||||||
|
|
||||||
|
sc->sc_prev_key_idx = 0;
|
||||||
|
callout_stop(&sc->sc_twoco);
|
||||||
|
|
||||||
|
if ((uint8_t)(code + checksum) != APPLEIR_NEC_CHECKSUM) {
|
||||||
|
DPRINTFN(1, "generic IR: bad checksum %02x+%02x\n",
|
||||||
|
code, checksum);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_key = generic_ir_lookup(code);
|
||||||
|
if (new_key == KEY_RESERVED)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (sc->sc_current_key != new_key) {
|
||||||
|
if (sc->sc_current_key != 0)
|
||||||
|
evdev_push_key(sc->sc_evdev,
|
||||||
|
sc->sc_current_key, 0);
|
||||||
|
sc->sc_current_key = new_key;
|
||||||
|
evdev_push_key(sc->sc_evdev, new_key, 1);
|
||||||
|
evdev_sync(sc->sc_evdev);
|
||||||
|
} else {
|
||||||
|
evdev_push_key(sc->sc_evdev, new_key, 1);
|
||||||
|
evdev_sync(sc->sc_evdev);
|
||||||
|
}
|
||||||
|
callout_reset(&sc->sc_co, APPLEIR_KEYUP_TICKS,
|
||||||
|
appleir_keyup, sc);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTFN(1, "unknown report: %02x %02x %02x\n",
|
||||||
|
report[0], report[1], report[2]);
|
||||||
|
|
||||||
|
done:
|
||||||
|
mtx_unlock(&sc->sc_mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apple IR receiver device IDs */
|
||||||
|
static const struct hid_device_id appleir_devs[] = {
|
||||||
|
{ HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8240) },
|
||||||
|
{ HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8241) },
|
||||||
|
{ HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8242) },
|
||||||
|
{ HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x8243) },
|
||||||
|
{ HID_BVP(BUS_USB, USB_VENDOR_APPLE, 0x1440) },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
appleir_probe(device_t dev)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = HIDBUS_LOOKUP_DRIVER_INFO(dev, appleir_devs);
|
||||||
|
if (error != 0)
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
/* Only attach to first top-level collection (TLC index 0) */
|
||||||
|
if (hidbus_get_index(dev) != 0)
|
||||||
|
return (ENXIO);
|
||||||
|
|
||||||
|
hidbus_set_desc(dev, "Apple IR Receiver");
|
||||||
|
return (BUS_PROBE_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
appleir_attach(device_t dev)
|
||||||
|
{
|
||||||
|
struct appleir_softc *sc = device_get_softc(dev);
|
||||||
|
const struct hid_device_info *hw;
|
||||||
|
int i, error;
|
||||||
|
|
||||||
|
sc->sc_dev = dev;
|
||||||
|
hw = hid_get_device_info(dev);
|
||||||
|
sc->sc_current_key = 0;
|
||||||
|
sc->sc_prev_key_idx = 0;
|
||||||
|
sc->sc_batt_warned = false;
|
||||||
|
mtx_init(&sc->sc_mtx, "appleir", NULL, MTX_DEF);
|
||||||
|
callout_init_mtx(&sc->sc_co, &sc->sc_mtx, 0);
|
||||||
|
callout_init_mtx(&sc->sc_twoco, &sc->sc_mtx, 0);
|
||||||
|
|
||||||
|
sc->sc_evdev = evdev_alloc();
|
||||||
|
evdev_set_name(sc->sc_evdev, device_get_desc(dev));
|
||||||
|
evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev));
|
||||||
|
evdev_set_id(sc->sc_evdev, hw->idBus, hw->idVendor, hw->idProduct,
|
||||||
|
hw->idVersion);
|
||||||
|
evdev_set_serial(sc->sc_evdev, hw->serial);
|
||||||
|
evdev_support_event(sc->sc_evdev, EV_SYN);
|
||||||
|
evdev_support_event(sc->sc_evdev, EV_KEY);
|
||||||
|
evdev_support_event(sc->sc_evdev, EV_REP);
|
||||||
|
|
||||||
|
for (i = 0; i < APPLEIR_NKEYS; i++) {
|
||||||
|
if (appleir_keymap[i] != KEY_RESERVED)
|
||||||
|
evdev_support_key(sc->sc_evdev, appleir_keymap[i]);
|
||||||
|
}
|
||||||
|
for (i = 0; i < GENERIC_NKEYS; i++)
|
||||||
|
evdev_support_key(sc->sc_evdev, generic_keymap[i].key);
|
||||||
|
|
||||||
|
error = evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx);
|
||||||
|
if (error != 0) {
|
||||||
|
device_printf(dev, "evdev_register_mtx failed: %d\n", error);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hidbus_set_intr(dev, appleir_intr, sc);
|
||||||
|
|
||||||
|
error = hid_intr_start(dev);
|
||||||
|
if (error != 0) {
|
||||||
|
device_printf(dev, "hid_intr_start failed: %d\n", error);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (sc->sc_evdev != NULL)
|
||||||
|
evdev_free(sc->sc_evdev);
|
||||||
|
callout_drain(&sc->sc_co);
|
||||||
|
callout_drain(&sc->sc_twoco);
|
||||||
|
mtx_destroy(&sc->sc_mtx);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
appleir_detach(device_t dev)
|
||||||
|
{
|
||||||
|
struct appleir_softc *sc = device_get_softc(dev);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = hid_intr_stop(dev);
|
||||||
|
if (error != 0) {
|
||||||
|
device_printf(dev, "hid_intr_stop failed: %d\n", error);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
callout_drain(&sc->sc_co);
|
||||||
|
callout_drain(&sc->sc_twoco);
|
||||||
|
evdev_free(sc->sc_evdev);
|
||||||
|
mtx_destroy(&sc->sc_mtx);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static device_method_t appleir_methods[] = {
|
||||||
|
DEVMETHOD(device_probe, appleir_probe),
|
||||||
|
DEVMETHOD(device_attach, appleir_attach),
|
||||||
|
DEVMETHOD(device_detach, appleir_detach),
|
||||||
|
DEVMETHOD_END
|
||||||
|
};
|
||||||
|
|
||||||
|
static driver_t appleir_driver = {
|
||||||
|
"appleir",
|
||||||
|
appleir_methods,
|
||||||
|
sizeof(struct appleir_softc)
|
||||||
|
};
|
||||||
|
|
||||||
|
DRIVER_MODULE(appleir, hidbus, appleir_driver, NULL, NULL);
|
||||||
|
MODULE_DEPEND(appleir, hid, 1, 1, 1);
|
||||||
|
MODULE_DEPEND(appleir, hidbus, 1, 1, 1);
|
||||||
|
MODULE_DEPEND(appleir, evdev, 1, 1, 1);
|
||||||
|
MODULE_VERSION(appleir, 1);
|
||||||
|
HID_PNP_INFO(appleir_devs);
|
||||||
@@ -6,6 +6,7 @@ SUBDIR = \
|
|||||||
hidraw
|
hidraw
|
||||||
|
|
||||||
SUBDIR += \
|
SUBDIR += \
|
||||||
|
appleir \
|
||||||
bcm5974 \
|
bcm5974 \
|
||||||
hconf \
|
hconf \
|
||||||
hcons \
|
hcons \
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.PATH: ${SRCTOP}/sys/dev/hid
|
||||||
|
|
||||||
|
KMOD= appleir
|
||||||
|
SRCS= appleir.c
|
||||||
|
SRCS+= opt_hid.h
|
||||||
|
SRCS+= bus_if.h device_if.h usbdevs.h
|
||||||
|
|
||||||
|
.include <bsd.kmod.mk>
|
||||||
Reference in New Issue
Block a user