Add support for the Apple MacBook Pro keyboard
- add key mappings for fn keys - byte swapping for certain models - Fix leds for keyboards which require an ID byte for the HID output structures Submitted by: Hans Petter Selasky
This commit is contained in:
parent
9d3086c72c
commit
fa64b9442b
@ -113,13 +113,13 @@ struct ukbd_data {
|
||||
#define MOD_WIN_R 0x80
|
||||
uint8_t reserved;
|
||||
uint8_t keycode[UKBD_NKEYCODE];
|
||||
} __packed;
|
||||
uint8_t exten[8];
|
||||
};
|
||||
|
||||
enum {
|
||||
UKBD_INTR_DT,
|
||||
UKBD_INTR_CS,
|
||||
UKBD_CTRL_LED,
|
||||
UKBD_N_TRANSFER = 3,
|
||||
UKBD_N_TRANSFER,
|
||||
};
|
||||
|
||||
struct ukbd_softc {
|
||||
@ -127,6 +127,8 @@ struct ukbd_softc {
|
||||
keymap_t sc_keymap;
|
||||
accentmap_t sc_accmap;
|
||||
fkeytab_t sc_fkeymap[UKBD_NFKEY];
|
||||
struct hid_location sc_loc_apple_eject;
|
||||
struct hid_location sc_loc_apple_fn;
|
||||
struct usb2_callout sc_callout;
|
||||
struct ukbd_data sc_ndata;
|
||||
struct ukbd_data sc_odata;
|
||||
@ -144,12 +146,14 @@ struct ukbd_softc {
|
||||
uint32_t sc_buffered_char[2];
|
||||
#endif
|
||||
uint32_t sc_flags; /* flags */
|
||||
#define UKBD_FLAG_COMPOSE 0x0001
|
||||
#define UKBD_FLAG_POLLING 0x0002
|
||||
#define UKBD_FLAG_SET_LEDS 0x0004
|
||||
#define UKBD_FLAG_INTR_STALL 0x0008
|
||||
#define UKBD_FLAG_ATTACHED 0x0010
|
||||
#define UKBD_FLAG_GONE 0x0020
|
||||
#define UKBD_FLAG_COMPOSE 0x0001
|
||||
#define UKBD_FLAG_POLLING 0x0002
|
||||
#define UKBD_FLAG_SET_LEDS 0x0004
|
||||
#define UKBD_FLAG_ATTACHED 0x0010
|
||||
#define UKBD_FLAG_GONE 0x0020
|
||||
#define UKBD_FLAG_APPLE_EJECT 0x0040
|
||||
#define UKBD_FLAG_APPLE_FN 0x0080
|
||||
#define UKBD_FLAG_APPLE_SWAP 0x0100
|
||||
|
||||
int32_t sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
|
||||
int32_t sc_state; /* shift/lock key state */
|
||||
@ -162,6 +166,8 @@ struct ukbd_softc {
|
||||
uint8_t sc_leds; /* store for async led requests */
|
||||
uint8_t sc_iface_index;
|
||||
uint8_t sc_iface_no;
|
||||
uint8_t sc_kbd_id;
|
||||
uint8_t sc_led_id;
|
||||
};
|
||||
|
||||
#define KEY_ERROR 0x01
|
||||
@ -451,16 +457,25 @@ ukbd_timeout(void *arg)
|
||||
usb2_callout_reset(&sc->sc_callout, hz / 40, &ukbd_timeout, sc);
|
||||
}
|
||||
|
||||
static void
|
||||
ukbd_clear_stall_callback(struct usb2_xfer *xfer)
|
||||
{
|
||||
struct ukbd_softc *sc = xfer->priv_sc;
|
||||
struct usb2_xfer *xfer_other = sc->sc_xfer[UKBD_INTR_DT];
|
||||
static uint8_t
|
||||
ukbd_apple_fn(uint8_t keycode) {
|
||||
switch (keycode) {
|
||||
case 0x28: return 0x49; /* RETURN -> INSERT */
|
||||
case 0x2a: return 0x4c; /* BACKSPACE -> DEL */
|
||||
case 0x50: return 0x4a; /* LEFT ARROW -> HOME */
|
||||
case 0x4f: return 0x4d; /* RIGHT ARROW -> END */
|
||||
case 0x52: return 0x4b; /* UP ARROW -> PGUP */
|
||||
case 0x51: return 0x4e; /* DOWN ARROW -> PGDN */
|
||||
default: return keycode;
|
||||
}
|
||||
}
|
||||
|
||||
if (usb2_clear_stall_callback(xfer, xfer_other)) {
|
||||
DPRINTF("stall cleared\n");
|
||||
sc->sc_flags &= ~UKBD_FLAG_INTR_STALL;
|
||||
usb2_transfer_start(xfer_other);
|
||||
static uint8_t
|
||||
ukbd_apple_swap(uint8_t keycode) {
|
||||
switch (keycode) {
|
||||
case 0x35: return 0x64;
|
||||
case 0x64: return 0x35;
|
||||
default: return keycode;
|
||||
}
|
||||
}
|
||||
|
||||
@ -470,18 +485,59 @@ ukbd_intr_callback(struct usb2_xfer *xfer)
|
||||
struct ukbd_softc *sc = xfer->priv_sc;
|
||||
uint16_t len = xfer->actlen;
|
||||
uint8_t i;
|
||||
uint8_t offset;
|
||||
uint8_t id;
|
||||
uint8_t apple_fn;
|
||||
uint8_t apple_eject;
|
||||
|
||||
switch (USB_GET_STATE(xfer)) {
|
||||
case USB_ST_TRANSFERRED:
|
||||
DPRINTF("actlen=%d bytes\n", len);
|
||||
|
||||
if (len == 0) {
|
||||
DPRINTF("zero length data\n");
|
||||
goto tr_setup;
|
||||
}
|
||||
|
||||
if (sc->sc_kbd_id != 0) {
|
||||
/* check and remove HID ID byte */
|
||||
usb2_copy_out(xfer->frbuffers, 0, &id, 1);
|
||||
if (id != sc->sc_kbd_id) {
|
||||
DPRINTF("wrong HID ID\n");
|
||||
goto tr_setup;
|
||||
}
|
||||
offset = 1;
|
||||
len--;
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (len > sizeof(sc->sc_ndata)) {
|
||||
len = sizeof(sc->sc_ndata);
|
||||
}
|
||||
|
||||
if (len) {
|
||||
bzero(&sc->sc_ndata, sizeof(sc->sc_ndata));
|
||||
usb2_copy_out(xfer->frbuffers, 0, &sc->sc_ndata, len);
|
||||
memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
|
||||
usb2_copy_out(xfer->frbuffers, offset,
|
||||
&sc->sc_ndata, len);
|
||||
|
||||
if ((sc->sc_flags & UKBD_FLAG_APPLE_EJECT) &&
|
||||
hid_get_data((uint8_t *)&sc->sc_ndata,
|
||||
len, &sc->sc_loc_apple_eject))
|
||||
apple_eject = 1;
|
||||
else
|
||||
apple_eject = 0;
|
||||
|
||||
if ((sc->sc_flags & UKBD_FLAG_APPLE_FN) &&
|
||||
hid_get_data((uint8_t *)&sc->sc_ndata,
|
||||
len, &sc->sc_loc_apple_fn))
|
||||
apple_fn = 1;
|
||||
else
|
||||
apple_fn = 0;
|
||||
#if USB_DEBUG
|
||||
DPRINTF("apple_eject=%u apple_fn=%u\n",
|
||||
apple_eject, apple_fn);
|
||||
|
||||
if (sc->sc_ndata.modifiers) {
|
||||
DPRINTF("mod: 0x%04x\n", sc->sc_ndata.modifiers);
|
||||
}
|
||||
@ -491,30 +547,42 @@ ukbd_intr_callback(struct usb2_xfer *xfer)
|
||||
}
|
||||
}
|
||||
#endif /* USB_DEBUG */
|
||||
|
||||
if (apple_fn) {
|
||||
for (i = 0; i < UKBD_NKEYCODE; i++) {
|
||||
sc->sc_ndata.keycode[i] =
|
||||
ukbd_apple_fn(sc->sc_ndata.keycode[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (sc->sc_flags & UKBD_FLAG_APPLE_SWAP) {
|
||||
for (i = 0; i < UKBD_NKEYCODE; i++) {
|
||||
sc->sc_ndata.keycode[i] =
|
||||
ukbd_apple_swap(sc->sc_ndata.keycode[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ukbd_interrupt(sc);
|
||||
}
|
||||
case USB_ST_SETUP:
|
||||
if (sc->sc_flags & UKBD_FLAG_INTR_STALL) {
|
||||
usb2_transfer_start(sc->sc_xfer[UKBD_INTR_CS]);
|
||||
return;
|
||||
}
|
||||
tr_setup:
|
||||
if (sc->sc_inputs < UKBD_IN_BUF_FULL) {
|
||||
xfer->frlengths[0] = xfer->max_data_length;
|
||||
usb2_start_hardware(xfer);
|
||||
} else {
|
||||
DPRINTF("input queue is full!\n");
|
||||
}
|
||||
return;
|
||||
break;
|
||||
|
||||
default: /* Error */
|
||||
DPRINTF("error=%s\n", usb2_errstr(xfer->error));
|
||||
|
||||
if (xfer->error != USB_ERR_CANCELLED) {
|
||||
/* try to clear stall first */
|
||||
sc->sc_flags |= UKBD_FLAG_INTR_STALL;
|
||||
usb2_transfer_start(sc->sc_xfer[UKBD_INTR_CS]);
|
||||
xfer->flags.stall_pipe = 1;
|
||||
goto tr_setup;
|
||||
}
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -522,7 +590,7 @@ static void
|
||||
ukbd_set_leds_callback(struct usb2_xfer *xfer)
|
||||
{
|
||||
struct usb2_device_request req;
|
||||
uint8_t buf[1];
|
||||
uint8_t buf[2];
|
||||
struct ukbd_softc *sc = xfer->priv_sc;
|
||||
|
||||
switch (USB_GET_STATE(xfer)) {
|
||||
@ -536,15 +604,24 @@ ukbd_set_leds_callback(struct usb2_xfer *xfer)
|
||||
USETW2(req.wValue, UHID_OUTPUT_REPORT, 0);
|
||||
req.wIndex[0] = sc->sc_iface_no;
|
||||
req.wIndex[1] = 0;
|
||||
USETW(req.wLength, 1);
|
||||
req.wLength[1] = 0;
|
||||
|
||||
buf[0] = sc->sc_leds;
|
||||
/* check if we need to prefix an ID byte */
|
||||
if (sc->sc_led_id != 0) {
|
||||
req.wLength[0] = 2;
|
||||
buf[0] = sc->sc_led_id;
|
||||
buf[1] = sc->sc_leds;
|
||||
} else {
|
||||
req.wLength[0] = 1;
|
||||
buf[0] = sc->sc_leds;
|
||||
buf[1] = 0;
|
||||
}
|
||||
|
||||
usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
|
||||
usb2_copy_in(xfer->frbuffers + 1, 0, buf, sizeof(buf));
|
||||
|
||||
xfer->frlengths[0] = sizeof(req);
|
||||
xfer->frlengths[1] = sizeof(buf);
|
||||
xfer->frlengths[1] = req.wLength[0];
|
||||
xfer->nframes = 2;
|
||||
usb2_start_hardware(xfer);
|
||||
}
|
||||
@ -567,21 +644,11 @@ static const struct usb2_config ukbd_config[UKBD_N_TRANSFER] = {
|
||||
.callback = &ukbd_intr_callback,
|
||||
},
|
||||
|
||||
[UKBD_INTR_CS] = {
|
||||
.type = UE_CONTROL,
|
||||
.endpoint = 0x00, /* Control pipe */
|
||||
.direction = UE_DIR_ANY,
|
||||
.bufsize = sizeof(struct usb2_device_request),
|
||||
.callback = &ukbd_clear_stall_callback,
|
||||
.timeout = 1000, /* 1 second */
|
||||
.interval = 50, /* 50ms */
|
||||
},
|
||||
|
||||
[UKBD_CTRL_LED] = {
|
||||
.type = UE_CONTROL,
|
||||
.endpoint = 0x00, /* Control pipe */
|
||||
.direction = UE_DIR_ANY,
|
||||
.bufsize = sizeof(struct usb2_device_request) + 1,
|
||||
.bufsize = sizeof(struct usb2_device_request) + 8,
|
||||
.callback = &ukbd_set_leds_callback,
|
||||
.timeout = 1000, /* 1 second */
|
||||
},
|
||||
@ -620,8 +687,11 @@ ukbd_attach(device_t dev)
|
||||
struct usb2_attach_arg *uaa = device_get_ivars(dev);
|
||||
int32_t unit = device_get_unit(dev);
|
||||
keyboard_t *kbd = &sc->sc_kbd;
|
||||
void *hid_ptr = NULL;
|
||||
usb2_error_t err;
|
||||
uint32_t flags;
|
||||
uint16_t n;
|
||||
uint16_t hid_len;
|
||||
|
||||
mtx_assert(&Giant, MA_OWNED);
|
||||
|
||||
@ -669,6 +739,46 @@ ukbd_attach(device_t dev)
|
||||
*/
|
||||
KBD_PROBE_DONE(kbd);
|
||||
|
||||
/* figure out if there is an ID byte in the data */
|
||||
err = usb2_req_get_hid_desc(uaa->device, NULL, &hid_ptr,
|
||||
&hid_len, M_TEMP, uaa->info.bIfaceIndex);
|
||||
if (err == 0) {
|
||||
uint8_t temp_id;
|
||||
|
||||
/* investigate if this is an Apple Keyboard */
|
||||
if (hid_locate(hid_ptr, hid_len,
|
||||
HID_USAGE2(HUP_CONSUMER, HUG_APPLE_EJECT),
|
||||
hid_input, 0, &sc->sc_loc_apple_eject, &flags,
|
||||
&sc->sc_kbd_id)) {
|
||||
if (flags & HIO_VARIABLE)
|
||||
sc->sc_flags |= UKBD_FLAG_APPLE_EJECT |
|
||||
UKBD_FLAG_APPLE_SWAP;
|
||||
if (hid_locate(hid_ptr, hid_len,
|
||||
HID_USAGE2(0xFFFF, 0x0003),
|
||||
hid_input, 0, &sc->sc_loc_apple_fn, &flags,
|
||||
&temp_id)) {
|
||||
if (flags & HIO_VARIABLE)
|
||||
sc->sc_flags |= UKBD_FLAG_APPLE_FN |
|
||||
UKBD_FLAG_APPLE_SWAP;
|
||||
if (temp_id != sc->sc_kbd_id) {
|
||||
DPRINTF("HID IDs mismatch\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Assume the first HID ID contains the
|
||||
* keyboard data
|
||||
*/
|
||||
hid_report_size(hid_ptr, hid_len,
|
||||
hid_input, &sc->sc_kbd_id);
|
||||
}
|
||||
|
||||
/* investigate if we need an ID-byte for the leds */
|
||||
hid_report_size(hid_ptr, hid_len, hid_output, &sc->sc_led_id);
|
||||
|
||||
free(hid_ptr, M_TEMP);
|
||||
}
|
||||
|
||||
/* ignore if SETIDLE fails, hence it is not crucial */
|
||||
err = usb2_req_set_idle(sc->sc_udev, &Giant, sc->sc_iface_index, 0, 0);
|
||||
|
||||
|
@ -128,6 +128,7 @@ struct usb2_hid_descriptor {
|
||||
#define HUG_SYSTEM_MENU_LEFT 0x008b
|
||||
#define HUG_SYSTEM_MENU_UP 0x008c
|
||||
#define HUG_SYSTEM_MENU_DOWN 0x008d
|
||||
#define HUG_APPLE_EJECT 0x00b8
|
||||
|
||||
/* Usages Digitizers */
|
||||
#define HUD_UNDEFINED 0x0000
|
||||
|
Loading…
x
Reference in New Issue
Block a user