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:
Andrew Thompson 2009-05-27 19:27:29 +00:00
parent 9d3086c72c
commit fa64b9442b
2 changed files with 154 additions and 43 deletions

View File

@ -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);

View File

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