PERFORCE change 98329 for review

Hans Petter Selasky hselasky at FreeBSD.org
Fri Jun 2 11:46:32 UTC 2006


http://perforce.freebsd.org/chv.cgi?CH=98329

Change 98329 by hselasky at hselasky_mini_itx on 2006/06/02 11:44:32

	Finished reworking the USB keyboard driver. It still uses the "Giant" lock,
	hence the keyboard system is all under "Giant". Seems like some bugs got
	fixed, hence my USB keyboard works better. That means it does not generate
	an additional "Print Screen" character when I type something with "Num Lock" 
	on. Also the buffering should be better. The interrupt pipe is stopped when
	the keyboard buffer is half full. The size of the keyboard buffer has been 
	doubled. Also repetition of keys works in the kernel-debugger. Please test!

Affected files ...

.. //depot/projects/usb/src/sys/conf/files#4 edit
.. //depot/projects/usb/src/sys/dev/usb/ukbd.c#4 edit

Differences ...

==== //depot/projects/usb/src/sys/conf/files#4 (text+ko) ====

@@ -992,6 +992,7 @@
 dev/usb/ugen.c			optional ugen
 dev/usb/ulpt.c			optional ulpt
 dev/usb/ums.c			optional ums
+dev/usb/ukbd.c			optional ukbd
 #
 # USB support (not merged)
 dev/usb/if_aue.c		optional aue
@@ -1013,7 +1014,6 @@
 dev/usb/ufm.c			optional ufm
 dev/usb/uftdi.c			optional uftdi ucom
 dev/usb/uhid.c			optional uhid
-dev/usb/ukbd.c			optional ukbd
 dev/usb/umass.c			optional umass
 dev/usb/umct.c			optional umct
 dev/usb/umodem.c		optional umodem

==== //depot/projects/usb/src/sys/dev/usb/ukbd.c#4 (text+ko) ====

@@ -1,0 +1,1491 @@
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart at augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Modifications for SUN TYPE 6 USB Keyboard by
+ *  Jörg Peter Schley (jps at scxnet.de)
+ */
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include "opt_kbd.h"
+#include "opt_ukbd.h"
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/tty.h>
+#include <sys/kbio.h>
+
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/kbdtables.h>
+
+#include <dev/usb/usb_port.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_subr.h>
+#include <dev/usb/usb_hid.h>
+#include <dev/usb/usb_quirks.h>
+
+/* the initial key map, accent map and fkey strings */
+#ifdef UKBD_DFLT_KEYMAP
+#define KBD_DFLT_KEYMAP
+#include "ukbdmap.h"
+#endif
+
+__FBSDID("$FreeBSD: src/sys/dev/usb/ukbd.c,v 1.53 2006/02/28 03:34:06 emax Exp $");
+
+#ifdef USB_DEBUG
+#define DPRINTF(n,fmt,...)						\
+  do { if (ukbd_debug > (n)) {						\
+      printf("%s: " fmt, __FUNCTION__,## __VA_ARGS__); } } while (0)
+
+static int ukbd_debug = 0;
+SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB ukbd");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, debug, CTLFLAG_RW,
+	   &ukbd_debug, 0, "ukbd debug level");
+#else
+#define DPRINTF(...)
+#endif
+
+#define UPROTO_BOOT_KEYBOARD 1
+
+#define UKBD_EMULATE_ATSCANCODE	       1
+#define UKBD_DRIVER_NAME          "ukbd"
+#define UKBD_NMOD                     8 /* units */
+#define UKBD_NKEYCODE                 6 /* units */
+#define UKBD_N_TRANSFER               3 /* units */
+#define UKBD_IN_BUF_SIZE  (2*(UKBD_NMOD + (2*UKBD_NKEYCODE))) /* bytes */
+#define UKBD_IN_BUF_FULL  (UKBD_IN_BUF_SIZE / 2) /* bytes */
+#define UKBD_NFKEY        (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
+
+struct ukbd_data {
+	u_int8_t	modifiers;
+#define MOD_CONTROL_L	0x01
+#define MOD_CONTROL_R	0x10
+#define MOD_SHIFT_L	0x02
+#define MOD_SHIFT_R	0x20
+#define MOD_ALT_L	0x04
+#define MOD_ALT_R	0x40
+#define MOD_WIN_L	0x08
+#define MOD_WIN_R	0x80
+	u_int8_t	reserved;
+	u_int8_t	keycode[UKBD_NKEYCODE];
+} __attribute__((__packed__));
+
+struct ukbd_softc {
+	keyboard_t         sc_kbd;
+	keymap_t           sc_keymap;
+	accentmap_t        sc_accmap;
+	fkeytab_t          sc_fkeymap[UKBD_NFKEY];
+	struct __callout   sc_callout;
+	struct ukbd_data   sc_ndata;
+	struct ukbd_data   sc_odata;
+
+	struct usbd_device * sc_udev;
+	struct usbd_interface * sc_iface;
+	struct usbd_xfer * sc_xfer[UKBD_N_TRANSFER];
+
+	u_int32_t          sc_ntime[UKBD_NKEYCODE];
+	u_int32_t          sc_otime[UKBD_NKEYCODE];
+	u_int32_t          sc_input[UKBD_IN_BUF_SIZE]; /* input buffer */
+	u_int32_t          sc_time_ms;
+	u_int32_t          sc_composed_char; /* composed char code, if non-zero */
+#ifdef UKBD_EMULATE_ATSCANCODE
+	u_int32_t          sc_buffered_char[2];
+#endif
+	u_int32_t	   sc_flags; /* flags */
+#define UKBD_FLAG_COMPOSE    0x0001
+#define UKBD_FLAG_POLLING    0x0002
+#define UKBD_FLAG_SET_LEDS   0x0004
+#define UKBD_FLAG_PIPE_ERROR 0x0008
+#define UKBD_FLAG_WAIT_USB   0x0010
+#define UKBD_FLAG_WAIT_CO    0x0020
+#define UKBD_FLAG_ATTACHED   0x0040
+#define UKBD_FLAG_GONE       0x0080
+
+	int32_t            sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
+	int32_t		   sc_state;  /* shift/lock key state */
+	int32_t		   sc_accents; /* accent key index (> 0) */
+
+	u_int16_t          sc_inputs;
+	u_int16_t          sc_inputhead;
+	u_int16_t          sc_inputtail;
+
+	u_int8_t           sc_leds;
+	u_int8_t	   sc_iface_index;
+	u_int8_t           sc_wakeup_detach; /* dummy */
+};
+
+#define KEY_ERROR	  0x01
+
+#define KEY_PRESS	  0
+#define KEY_RELEASE	  0x400
+#define KEY_INDEX(c)	  ((c) & 0xFF)
+
+#define SCAN_PRESS	  0
+#define SCAN_RELEASE	  0x80
+#define SCAN_PREFIX_E0	  0x100
+#define SCAN_PREFIX_E1	  0x200
+#define SCAN_PREFIX_CTL	  0x400
+#define SCAN_PREFIX_SHIFT 0x800
+#define SCAN_PREFIX	(SCAN_PREFIX_E0  | SCAN_PREFIX_E1 | \
+			 SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT)
+#define SCAN_CHAR(c)	((c) & 0x7f)
+
+static const struct {
+	u_int32_t mask, key;
+} ukbd_mods[UKBD_NMOD] = {
+	{ MOD_CONTROL_L, 0xe0 },
+	{ MOD_CONTROL_R, 0xe4 },
+	{ MOD_SHIFT_L,   0xe1 },
+	{ MOD_SHIFT_R,   0xe5 },
+	{ MOD_ALT_L,     0xe2 },
+	{ MOD_ALT_R,     0xe6 },
+	{ MOD_WIN_L,     0xe3 },
+	{ MOD_WIN_R,	 0xe7 },
+};
+
+#define NN 0			/* no translation */
+/*
+ * Translate USB keycodes to AT keyboard scancodes.
+ */
+/*
+ * FIXME: Mac USB keyboard generates:
+ * 0x53: keypad NumLock/Clear
+ * 0x66: Power
+ * 0x67: keypad =
+ * 0x68: F13
+ * 0x69: F14
+ * 0x6a: F15
+ */
+static const u_int8_t ukbd_trtab[256] = {
+	   0,   0,   0,   0,  30,  48,  46,  32, /* 00 - 07 */
+	  18,  33,  34,  35,  23,  36,  37,  38, /* 08 - 0F */
+	  50,  49,  24,  25,  16,  19,  31,  20, /* 10 - 17 */
+	  22,  47,  17,  45,  21,  44,   2,   3, /* 18 - 1F */
+	   4,   5,   6,   7,   8,   9,  10,  11, /* 20 - 27 */
+	  28,   1,  14,  15,  57,  12,  13,  26, /* 28 - 2F */
+	  27,  43,  43,  39,  40,  41,  51,  52, /* 30 - 37 */
+	  53,  58,  59,  60,  61,  62,  63,  64, /* 38 - 3F */
+	  65,  66,  67,  68,  87,  88,  92,  70, /* 40 - 47 */
+	 104, 102,  94,  96, 103,  99, 101,  98, /* 48 - 4F */
+	  97, 100,  95,  69,  91,  55,  74,  78, /* 50 - 57 */
+	  89,  79,  80,  81,  75,  76,  77,  71, /* 58 - 5F */
+          72,  73,  82,  83,  86, 107, 122,  NN, /* 60 - 67 */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* 68 - 6F */
+          NN,  NN,  NN,  NN, 115, 108, 111, 113, /* 70 - 77 */
+         109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */
+         121, 120,  NN,  NN,  NN,  NN,  NN, 115, /* 80 - 87 */
+         112, 125, 121, 123,  NN,  NN,  NN,  NN, /* 88 - 8F */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* 90 - 97 */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* 98 - 9F */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* A0 - A7 */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* A8 - AF */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* B0 - B7 */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* B8 - BF */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* C0 - C7 */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* C8 - CF */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* D0 - D7 */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* D8 - DF */
+          29,  42,  56, 105,  90,  54,  93, 106, /* E0 - E7 */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* E8 - EF */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* F0 - F7 */
+          NN,  NN,  NN,  NN,  NN,  NN,  NN,  NN, /* F8 - FF */
+};
+
+/* prototypes */
+static void		ukbd_timeout(void *arg);
+static void		ukbd_set_leds(struct ukbd_softc *sc, u_int8_t leds);
+static int		ukbd_set_typematic(keyboard_t *kbd, int code);
+#ifdef UKBD_EMULATE_ATSCANCODE
+static int		ukbd_key2scan(struct ukbd_softc *sc, int keycode, 
+				      int shift, int up);
+#endif
+static u_int32_t	ukbd_read_char(keyboard_t *kbd, int wait);
+static void		ukbd_clear_state(keyboard_t *kbd);
+static int		ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg);
+static int		ukbd_enable(keyboard_t *kbd);
+static int		ukbd_disable(keyboard_t *kbd);
+static void		ukbd_interrupt(struct ukbd_softc *sc);
+
+static device_probe_t ukbd_probe;
+static device_attach_t ukbd_attach;
+static device_detach_t ukbd_detach;
+static device_resume_t ukbd_resume;
+
+static void
+ukbd_put_key(struct ukbd_softc *sc, u_int32_t key)
+{
+	mtx_assert(&Giant, MA_OWNED);
+
+	DPRINTF(0, "0x%02x (%d) %s\n", key, key,
+		(key & KEY_RELEASE) ? "released" : "pressed");
+
+	if (sc->sc_inputs < UKBD_IN_BUF_SIZE) {
+	    sc->sc_input[sc->sc_inputtail] = key;
+	    ++(sc->sc_inputs);
+	    ++(sc->sc_inputtail);
+	    if (sc->sc_inputtail >= UKBD_IN_BUF_SIZE) {
+	        sc->sc_inputtail = 0;
+	    }
+	} else {
+	  DPRINTF(0, "input buffer is full\n");
+	}
+	return;
+}
+
+static int32_t
+ukbd_get_key(struct ukbd_softc *sc)
+{
+	int32_t c;
+
+	mtx_assert(&Giant, MA_OWNED);
+
+	if (sc->sc_inputs == 0) {
+	    /* start transfer, if not already started */
+	    usbd_transfer_start(sc->sc_xfer[0]);
+	}
+
+	if (sc->sc_flags & UKBD_FLAG_POLLING) {
+	    DPRINTF(1, "polling\n");
+
+	    while (sc->sc_inputs == 0) {
+
+	        usbd_do_poll(sc->sc_udev);
+
+		DELAY(1000); /* delay 1 ms */
+
+		sc->sc_time_ms ++;
+
+		/* support repetition of keys: */
+
+		ukbd_interrupt(sc);
+	    }
+	}
+
+	if (sc->sc_inputs == 0) {
+	    c = -1;
+	} else {
+	    c = sc->sc_input[sc->sc_inputhead];
+	    --(sc->sc_inputs);
+	    ++(sc->sc_inputhead);
+	    if (sc->sc_inputhead >= UKBD_IN_BUF_SIZE) {
+	        sc->sc_inputhead = 0;
+	    }
+	}
+	return c;
+}
+
+static void
+ukbd_interrupt(struct ukbd_softc *sc)
+{
+	u_int32_t n_mod;
+	u_int32_t o_mod;
+	u_int32_t now = sc->sc_time_ms;
+	u_int32_t dtime;
+	u_int32_t c;
+	u_int8_t key;
+	u_int8_t i;
+	u_int8_t j;
+
+	if (sc->sc_ndata.keycode[0] == KEY_ERROR) {
+	    goto done;
+	}
+
+	n_mod = sc->sc_ndata.modifiers;
+	o_mod = sc->sc_odata.modifiers;
+	if (n_mod != o_mod) {
+	    for (i = 0; i < UKBD_NMOD; i++) {
+	        if ((n_mod & ukbd_mods[i].mask) !=
+		    (o_mod & ukbd_mods[i].mask)) {
+		    ukbd_put_key(sc, ukbd_mods[i].key |
+				 ((n_mod & ukbd_mods[i].mask) ? 
+				  KEY_PRESS : KEY_RELEASE));
+		}
+	    }
+	}
+
+	/* Check for released keys. */
+	for (i = 0; i < UKBD_NKEYCODE; i++) {
+	    key = sc->sc_odata.keycode[i];
+	    if (key == 0) {
+	        continue;
+	    }
+	    for (j = 0; j < UKBD_NKEYCODE; j++) {
+	        if (sc->sc_ndata.keycode[j] == 0) {
+		    continue;
+		}
+		if (key == sc->sc_ndata.keycode[j]) {
+		    goto rfound;
+		}
+	    }
+	    ukbd_put_key(sc, key | KEY_RELEASE);
+	rfound:;
+	}
+
+	/* Check for pressed keys. */
+	for (i = 0; i < UKBD_NKEYCODE; i++) {
+	    key = sc->sc_ndata.keycode[i];
+	    if (key == 0) {
+	        continue;
+	    }
+	    sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay1;
+	    for (j = 0; j < UKBD_NKEYCODE; j++) {
+	        if (sc->sc_odata.keycode[j] == 0) {
+		    continue;
+		}
+		if (key == sc->sc_odata.keycode[j]) {
+
+		    /* key is still pressed */
+
+		    sc->sc_ntime[i] = sc->sc_otime[j];
+		    dtime = (sc->sc_otime[j] - now);
+
+		    if (!(dtime & 0x80000000)) {
+		        /* time has not elapsed */
+		        goto pfound;
+		    }
+		    sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay2;
+		    break;
+		}
+	    }
+	    ukbd_put_key(sc, key | KEY_PRESS);
+	pfound:;
+	}
+
+	sc->sc_odata = sc->sc_ndata;
+
+	bcopy(sc->sc_ntime, sc->sc_otime, sizeof(sc->sc_otime));
+
+	if (sc->sc_inputs == 0) {
+	    goto done;
+	}
+
+	if (sc->sc_flags & UKBD_FLAG_POLLING) {
+	    goto done;
+	}
+
+	if (KBD_IS_ACTIVE(&(sc->sc_kbd)) && 
+	    KBD_IS_BUSY(&(sc->sc_kbd))) {
+	    /* let the callback function process the input */
+	    (sc->sc_kbd.kb_callback.kc_func)(&(sc->sc_kbd), KBDIO_KEYINPUT,
+					     sc->sc_kbd.kb_callback.kc_arg);
+	} else {
+	    /* read and discard the input, no one is waiting for it */
+	    do {
+	        c = ukbd_read_char(&(sc->sc_kbd), 0);
+	    } while (c != NOKEY);
+	}
+ done:
+	return;
+}
+
+static void
+ukbd_timeout(void *arg)
+{
+	struct ukbd_softc *sc = arg;
+
+	mtx_assert(&Giant, MA_OWNED);
+
+	if (!(sc->sc_flags & UKBD_FLAG_POLLING)) {
+	    sc->sc_time_ms += 25; /* milliseconds */
+	}
+
+	ukbd_interrupt(sc);
+
+	__callout_reset(&(sc->sc_callout), hz / 40, &ukbd_timeout, sc);
+
+	mtx_unlock(&Giant);
+
+	return;
+}
+
+static void
+ukbd_clear_stall_callback(struct usbd_xfer *xfer1)
+{
+	usb_device_request_t *req = xfer1->buffer;
+	struct ukbd_softc *sc = xfer1->priv_sc;
+	struct usbd_xfer *xfer0 = sc->sc_xfer[0];
+
+	USBD_CHECK_STATUS(xfer1);
+
+ tr_setup:
+
+        /* setup a CLEAR STALL packet */
+
+        req->bmRequestType = UT_WRITE_ENDPOINT;
+        req->bRequest = UR_CLEAR_FEATURE;
+        USETW(req->wValue, UF_ENDPOINT_HALT);
+        req->wIndex[0] = xfer0->pipe->edesc->bEndpointAddress;
+        req->wIndex[1] = 0;
+        USETW(req->wLength, 0);
+
+        usbd_start_hardware(xfer1);
+        return;
+
+ tr_error:
+	DPRINTF(0, "error=%s\n", usbd_errstr(xfer1->error));
+
+ tr_transferred:
+
+	sc->sc_flags &= ~UKBD_FLAG_PIPE_ERROR;
+
+	if (xfer1->error != USBD_CANCELLED) {
+
+	    xfer0->pipe->clearstall = 0;
+	    xfer0->pipe->toggle_next = 0;
+
+	    usbd_transfer_start(xfer0);
+	}
+	return;
+}
+
+static void
+ukbd_intr_callback(struct usbd_xfer *xfer)
+{
+	struct ukbd_softc *sc = xfer->priv_sc;
+	u_int8_t *buf = xfer->buffer;
+	u_int16_t len = xfer->actlen;
+	u_int8_t i;
+
+	USBD_CHECK_STATUS(xfer);
+
+ tr_transferred:
+	DPRINTF(0, "actlen=%d bytes\n", len);
+
+	if (len > sizeof(sc->sc_ndata)) {
+	    len = sizeof(sc->sc_ndata);
+	}
+
+	if (len) {
+	    bzero(&(sc->sc_ndata), sizeof(sc->sc_ndata));
+	    bcopy(buf, &(sc->sc_ndata), len);
+#ifdef USB_DEBUG
+	    if (sc->sc_ndata.modifiers) {
+	        DPRINTF(0, "mod: 0x%04x\n", sc->sc_ndata.modifiers);
+	    }
+	    for (i = 0; i < UKBD_NKEYCODE; i++) {
+	        if (sc->sc_ndata.keycode[i]) {
+		    DPRINTF(0, "[%d] = %d\n", i, sc->sc_ndata.keycode[i]);
+		}
+	    }
+#endif /* USB_DEBUG */
+	    ukbd_interrupt(sc);
+	}
+
+ tr_setup:
+	if (!(sc->sc_flags & UKBD_FLAG_PIPE_ERROR)) {
+	    if (sc->sc_inputs < UKBD_IN_BUF_FULL) {
+	        usbd_start_hardware(xfer);
+	    } else {
+	        DPRINTF(0, "input queue is full!\n");
+	    }
+	}
+	return;
+
+ tr_error:
+	DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error));
+
+	if (xfer->error != USBD_CANCELLED) {
+
+	    /* start clear stall */
+	    sc->sc_flags |= UKBD_FLAG_PIPE_ERROR;
+
+	    usbd_transfer_start(sc->sc_xfer[1]);
+	}
+	return;
+}
+
+static void
+ukbd_set_leds_callback(struct usbd_xfer *xfer)
+{
+	usb_device_request_t *req = xfer->buffer;
+	struct ukbd_softc *sc = xfer->priv_sc;
+
+	USBD_CHECK_STATUS(xfer);
+
+ tr_transferred:
+ tr_setup:
+	if (sc->sc_flags &   UKBD_FLAG_SET_LEDS) {
+	    sc->sc_flags &= ~UKBD_FLAG_SET_LEDS;
+
+	    req->bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	    req->bRequest = UR_SET_REPORT;
+	    USETW2(req->wValue, UHID_OUTPUT_REPORT, 0);
+	    USETW(req->wIndex, sc->sc_iface->idesc->bInterfaceNumber);
+	    USETW(req->wLength, 1);
+	    req->bData[0] = sc->sc_leds;
+
+	    usbd_start_hardware(xfer);
+	}
+	return;
+
+ tr_error:
+	DPRINTF(0, "error=%s\n", usbd_errstr(xfer->error));
+	return;
+}
+
+static const struct usbd_config ukbd_config[UKBD_N_TRANSFER] = {
+
+    [0] = {
+      .type      = UE_INTERRUPT,
+      .endpoint  = -1, /* any */
+      .direction = UE_DIR_IN,
+      .flags     = USBD_SHORT_XFER_OK,
+      .bufsize   = sizeof(struct ukbd_data), /* bytes */
+      .callback  = &ukbd_intr_callback,
+    },
+
+    [1] = {
+      .type      = UE_CONTROL,
+      .endpoint  = 0x00, /* Control pipe */
+      .direction = -1,
+      .bufsize   = sizeof(usb_device_request_t),
+      .callback  = &ukbd_clear_stall_callback,
+      .timeout   = 1000, /* 1 second */
+    },
+
+    [2] = {
+      .type      = UE_CONTROL,
+      .endpoint  = 0x00, /* Control pipe */
+      .direction = -1,
+      .bufsize   = sizeof(usb_device_request_t) + 1,
+      .callback  = &ukbd_set_leds_callback,
+      .timeout   = 1000, /* 1 second */
+    },
+};
+
+static void
+ukbd_detach_complete(struct usbd_memory_info *info)
+{
+	struct ukbd_softc *sc = info->priv_sc;
+
+	mtx_lock(&Giant);
+
+	if (sc->sc_flags &   UKBD_FLAG_WAIT_USB) {
+	    sc->sc_flags &= ~UKBD_FLAG_WAIT_USB;
+	    wakeup(&(sc->sc_wakeup_detach));
+	}
+
+	mtx_unlock(&Giant);
+
+	return;
+}
+
+static int
+ukbd_probe(device_t dev)
+{
+	keyboard_switch_t *sw = kbd_get_switch(UKBD_DRIVER_NAME);
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	usb_interface_descriptor_t *id;
+
+	DPRINTF(10, "\n");
+
+	if (sw == NULL) {
+	    return UMATCH_NONE;
+	}
+
+	if (uaa->iface == NULL) {
+	    /* attach to ifaces only */
+	    return UMATCH_NONE;
+	}
+
+	/* check that the keyboard speaks the boot protocol: */
+	id = usbd_get_interface_descriptor(uaa->iface);
+	if (id
+	    && (id->bInterfaceClass == UICLASS_HID)
+	    && (id->bInterfaceSubClass == UISUBCLASS_BOOT)
+	    && (id->bInterfaceProtocol == UPROTO_BOOT_KEYBOARD)) {
+	    return UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO;
+	}
+	return UMATCH_NONE;
+}
+
+static int
+ukbd_attach(device_t dev)
+{
+	struct ukbd_softc *sc = device_get_softc(dev);
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	int32_t unit = device_get_unit(dev);
+	keyboard_t *kbd = &(sc->sc_kbd);
+	usbd_status err;
+	u_int16_t n;
+
+	if (sc == NULL) {
+	    return ENOMEM;
+	}
+
+	mtx_assert(&Giant, MA_OWNED);
+
+	kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
+
+	kbd->kb_data = (void *)sc;
+
+	usbd_set_desc(dev, uaa->device);
+
+	sc->sc_udev = uaa->device;
+	sc->sc_iface = uaa->iface;
+	sc->sc_iface_index = uaa->iface_index;
+	sc->sc_mode = K_XLATE;
+	sc->sc_iface = uaa->iface;
+
+	__callout_init_mtx(&(sc->sc_callout), &Giant,
+			   CALLOUT_RETURNUNLOCKED);
+#if 0
+	/* TODO: "__callout_init_mtx()" does not support this: */
+
+	sc->sc_flags |= UKBD_FLAG_WAIT_CO; 
+#endif
+
+	err = usbd_transfer_setup(uaa->device, uaa->iface_index, sc->sc_xfer, 
+				  ukbd_config, UKBD_N_TRANSFER, sc, 
+				  &Giant, &ukbd_detach_complete);
+	if (err) {
+	    DPRINTF(0, "error=%s\n", usbd_errstr(err)) ;
+	    goto detach;
+	}
+
+	sc->sc_flags |= UKBD_FLAG_WAIT_USB;
+
+	/* setup default keyboard maps */
+
+	sc->sc_keymap = key_map;
+	sc->sc_accmap = accent_map;
+	for (n = 0; n < UKBD_NFKEY; n++) {
+	    sc->sc_fkeymap[n] = fkey_tab[n];
+	}
+
+	kbd_set_maps(kbd, &(sc->sc_keymap), &(sc->sc_accmap), 
+		     sc->sc_fkeymap, UKBD_NFKEY);
+
+	KBD_FOUND_DEVICE(kbd);
+
+	ukbd_clear_state(kbd);
+
+	/*
+	 * FIXME: set the initial value for lock keys in "sc_state"
+	 * according to the BIOS data?
+	 */
+	KBD_PROBE_DONE(kbd);
+
+	if ((usbd_get_quirks(uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) {
+
+	    err = usbreq_set_protocol(sc->sc_udev, sc->sc_iface_index, 0);
+
+	    DPRINTF(5, "protocol set\n");
+
+	    if (err) {
+	        device_printf(dev, "set protocol failed\n");
+		goto detach;
+	    }
+	}
+
+	/* ignore if SETIDLE fails, hence it is not crucial */
+	err = usbreq_set_idle(sc->sc_udev, sc->sc_iface_index, 0, 0);
+
+	ukbd_ioctl(kbd, KDSETLED, (caddr_t)&(sc->sc_state));
+
+	KBD_INIT_DONE(kbd);
+
+	if (kbd_register(kbd) < 0) {
+	    goto detach;
+	}
+
+	KBD_CONFIG_DONE(kbd);
+
+	ukbd_enable(kbd);
+
+#ifdef KBD_INSTALL_CDEV
+	if (kbd_attach(kbd)) {
+	    goto detach;
+	}
+#endif
+	sc->sc_flags |= UKBD_FLAG_ATTACHED;
+
+	if (bootverbose) {
+	    genkbd_diag(kbd, bootverbose);
+	}
+
+	/* start the keyboard */
+
+	usbd_transfer_start(sc->sc_xfer[0]);
+
+	/* start the timer */
+
+	mtx_lock(&Giant);
+
+	ukbd_timeout(sc);
+
+	return 0; /* success */
+
+ detach:
+	ukbd_detach(dev);
+	return ENXIO; /* error */
+}
+
+int
+ukbd_detach(device_t dev)
+{
+	struct ukbd_softc *sc = device_get_softc(dev);
+	int error;
+
+	mtx_assert(&Giant, MA_OWNED);
+
+	DPRINTF(0, "\n");
+
+	if (sc->sc_flags & UKBD_FLAG_POLLING) {
+	    panic("cannot detach polled keyboard!\n");
+	}
+
+	sc->sc_flags |= UKBD_FLAG_GONE;
+
+	__callout_stop(&(sc->sc_callout));
+
+	ukbd_disable(&(sc->sc_kbd));
+
+#ifdef KBD_INSTALL_CDEV
+	if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
+	    error = kbd_detach(&(sc->sc_kbd));
+	    if (error) {
+	        /* usb attach cannot return an error */
+	        device_printf(dev, "WARNING: kbd_detach() "
+			      "returned non-zero! (ignored)\n");
+	    }
+	}
+#endif
+	if (KBD_IS_CONFIGURED(&(sc->sc_kbd))) {
+	    error = kbd_unregister(&(sc->sc_kbd));
+	    if (error) {
+	        /* usb attach cannot return an error */
+	        device_printf(dev, "WARNING: kbd_unregister() "
+			      "returned non-zero! (ignored)\n");
+	    }
+	}
+
+	sc->sc_kbd.kb_flags = 0;
+
+	usbd_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER);
+
+	/* wait for callbacks to be aborted */
+
+	while (sc->sc_flags & (UKBD_FLAG_WAIT_USB|UKBD_FLAG_WAIT_CO)) {
+
+	    error = msleep(&(sc->sc_wakeup_detach), &Giant, 
+			   PRIBIO, "ukbd_sync_2", 0);
+	}
+
+	DPRINTF(0, "%s: disconnected\n", 
+		device_get_nameunit(dev));
+
+	return 0;
+}
+
+static int
+ukbd_resume(device_t dev)
+{
+	struct ukbd_softc *sc = device_get_softc(dev);
+
+	mtx_assert(&Giant, MA_OWNED);
+
+	ukbd_clear_state(&(sc->sc_kbd));
+
+	return 0;
+}
+
+/* early keyboard probe, not supported */
+static int
+ukbd_configure(int flags)
+{
+	return 0;
+}
+
+/* detect a keyboard, not used */
+static int
+ukbd__probe(int unit, void *arg, int flags)
+{
+	mtx_assert(&Giant, MA_OWNED);
+	return ENXIO;
+}
+
+/* reset and initialize the device, not used */
+static int
+ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
+{
+	mtx_assert(&Giant, MA_OWNED);
+	return ENXIO;
+}
+
+/* test the interface to the device, not used */
+static int
+ukbd_test_if(keyboard_t *kbd)
+{
+	mtx_assert(&Giant, MA_OWNED);
+	return 0;
+}
+
+/* finish using this keyboard, not used */
+static int
+ukbd_term(keyboard_t *kbd)
+{
+	mtx_assert(&Giant, MA_OWNED);
+	return ENXIO;
+}
+
+/* keyboard interrupt routine, not used */
+static int
+ukbd_intr(keyboard_t *kbd, void *arg)
+{
+	mtx_assert(&Giant, MA_OWNED);
+	return 0;
+}
+
+/* lock the access to the keyboard, not used */
+static int
+ukbd_lock(keyboard_t *kbd, int lock)
+{
+	mtx_assert(&Giant, MA_OWNED);
+	return 1;
+}
+
+/*
+ * Enable the access to the device; until this function is called,
+ * the client cannot read from the keyboard.
+ */
+static int
+ukbd_enable(keyboard_t *kbd)
+{
+	mtx_assert(&Giant, MA_OWNED);
+	KBD_ACTIVATE(kbd);
+	return 0;
+}
+
+/* disallow the access to the device */
+static int
+ukbd_disable(keyboard_t *kbd)
+{
+	mtx_assert(&Giant, MA_OWNED);
+	KBD_DEACTIVATE(kbd);
+	return 0;
+}
+
+/* check if data is waiting */
+static int
+ukbd_check(keyboard_t *kbd)
+{
+	struct ukbd_softc *sc = kbd->kb_data;
+
+	mtx_assert(&Giant, MA_OWNED);
+
+	if (!KBD_IS_ACTIVE(kbd)) {
+	    return 0;
+	}
+#ifdef UKBD_EMULATE_ATSCANCODE
+	if (sc->sc_buffered_char[0]) {
+	    return 1;
+	}
+#endif
+	if (sc->sc_inputs > 0) {
+	    return 1;
+	}
+	return 0;
+}
+
+/* check if char is waiting */
+static int
+ukbd_check_char(keyboard_t *kbd)
+{
+	struct ukbd_softc *sc = kbd->kb_data;
+
+	mtx_assert(&Giant, MA_OWNED);
+
+	if (!KBD_IS_ACTIVE(kbd)) {
+	    return 0;
+	}
+	if ((sc->sc_composed_char > 0) &&
+	    (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
+	    return 1;
+	}
+	return ukbd_check(kbd);
+}
+
+
+/* read one byte from the keyboard if it's allowed */
+static int
+ukbd_read(keyboard_t *kbd, int wait)
+{
+	struct ukbd_softc *sc = kbd->kb_data;
+	int32_t usbcode;
+#ifdef UKBD_EMULATE_ATSCANCODE
+	u_int32_t keycode;
+	u_int32_t scancode;
+#endif
+
+	mtx_assert(&Giant, MA_OWNED);

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list