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-all mailing list