Date: Wed, 27 May 2009 19:27:30 +0000 (UTC) From: Andrew Thompson <thompsa@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r192925 - in head/sys/dev/usb: . input Message-ID: <200905271927.n4RJRUH8009289@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200905271927.n4RJRUH8009289>