svn commit: r192925 - in head/sys/dev/usb: . input
Andrew Thompson
thompsa at FreeBSD.org
Wed May 27 19:27:31 UTC 2009
Author: thompsa
Date: Wed May 27 19:27:29 2009
New Revision: 192925
URL: http://svn.freebsd.org/changeset/base/192925
Log:
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
Modified:
head/sys/dev/usb/input/ukbd.c
head/sys/dev/usb/usbhid.h
Modified: head/sys/dev/usb/input/ukbd.c
==============================================================================
--- head/sys/dev/usb/input/ukbd.c Wed May 27 19:21:29 2009 (r192924)
+++ head/sys/dev/usb/input/ukbd.c Wed May 27 19:27:29 2009 (r192925)
@@ -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 *xfe
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 *xfe
}
}
#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
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_con
.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);
Modified: head/sys/dev/usb/usbhid.h
==============================================================================
--- head/sys/dev/usb/usbhid.h Wed May 27 19:21:29 2009 (r192924)
+++ head/sys/dev/usb/usbhid.h Wed May 27 19:27:29 2009 (r192925)
@@ -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
More information about the svn-src-head
mailing list