git: 08d88401754b - main - hid: Copy ukbd(4) to HID subsystem.

Vladimir Kondratyev wulf at FreeBSD.org
Thu Jan 7 23:20:52 UTC 2021


The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=08d88401754b335a549507b86d219c90a0fc7e13

commit 08d88401754b335a549507b86d219c90a0fc7e13
Author:     Vladimir Kondratyev <wulf at FreeBSD.org>
AuthorDate: 2020-10-11 22:36:35 +0000
Commit:     Vladimir Kondratyev <wulf at FreeBSD.org>
CommitDate: 2021-01-07 23:18:43 +0000

    hid: Copy ukbd(4) to HID subsystem.
---
 share/man/man4/hkbd.4 |  188 +++++
 sys/dev/hid/hkbd.c    | 2197 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2385 insertions(+)

diff --git a/share/man/man4/hkbd.4 b/share/man/man4/hkbd.4
new file mode 100644
index 000000000000..f443f51ce8e1
--- /dev/null
+++ b/share/man/man4/hkbd.4
@@ -0,0 +1,188 @@
+.\" Copyright (c) 1997, 1998
+.\"	Nick Hibma <n_hibma at FreeBSD.org>. All rights reserved.
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 24, 2018
+.Dt UKBD 4
+.Os
+.Sh NAME
+.Nm ukbd
+.Nd USB keyboard driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following line in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device ukbd"
+.Cd "device hid"
+.Cd "device usb"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+ukbd_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for keyboards that attach to the USB port.
+.Xr usb 4
+and one of
+.Xr uhci 4
+or
+.Xr ohci 4
+must be configured in the kernel as well.
+.Sh CONFIGURATION
+By default, the keyboard subsystem does not create the appropriate devices yet.
+Make sure you reconfigure your kernel with the following option in the kernel
+config file:
+.Pp
+.Dl "options KBD_INSTALL_CDEV"
+.Pp
+If both an AT keyboard USB keyboards are used at the same time, the
+AT keyboard will appear as
+.Pa kbd0
+in
+.Pa /dev .
+The USB keyboards will be
+.Pa kbd1 , kbd2 ,
+etc.
+You can see some information about the keyboard with the following command:
+.Pp
+.Dl "kbdcontrol -i < /dev/kbd1"
+.Pp
+or load a keymap with
+.Pp
+.Dl "kbdcontrol -l keymaps/pt.iso < /dev/kbd1"
+.Pp
+See
+.Xr kbdcontrol 1
+for more possible options.
+.Pp
+You can swap console keyboards by using the command
+.Pp
+.Dl "kbdcontrol -k /dev/kbd1"
+.Pp
+From this point on, the first USB keyboard will be the keyboard
+to be used by the console.
+.Pp
+If you want to use a USB keyboard as your default and not use an AT keyboard at
+all, you will have to remove the
+.Cd "device atkbd"
+line from the kernel configuration file.
+Because of the device initialization order,
+the USB keyboard will be detected
+.Em after
+the console driver
+initializes itself and you have to explicitly tell the console
+driver to use the existence of the USB keyboard.
+This can be done in
+one of the following two ways.
+.Pp
+Run the following command as a part of system initialization:
+.Pp
+.Dl "kbdcontrol -k /dev/kbd0 < /dev/ttyv0 > /dev/null"
+.Pp
+(Note that as the USB keyboard is the only keyboard, it is accessed as
+.Pa /dev/kbd0 )
+or otherwise tell the console driver to periodically look for a
+keyboard by setting a flag in the kernel configuration file:
+.Pp
+.Dl "device sc0 at isa? flags 0x100"
+.Pp
+With the above flag, the console driver will try to detect any
+keyboard in the system if it did not detect one while it was
+initialized at boot time.
+.Sh DRIVER CONFIGURATION
+.D1 Cd "options KBD_INSTALL_CDEV"
+.Pp
+Make the keyboards available through a character device in
+.Pa /dev .
+.Pp
+.D1 Cd options UKBD_DFLT_KEYMAP
+.D1 Cd makeoptions UKBD_DFLT_KEYMAP=fr.iso
+.Pp
+The above lines will put the French ISO keymap in the ukbd driver.
+You can specify any keymap in
+.Pa /usr/share/syscons/keymaps
+or
+.Pa /usr/share/vt/keymaps
+(depending on the console driver being used) with this option.
+.Pp
+.D1 Cd "options KBD_DISABLE_KEYMAP_LOADING"
+.Pp
+Do not allow the user to change the keymap.
+Note that these options also affect the AT keyboard driver,
+.Xr atkbd 4 .
+.Sh SYSCTL VARIABLES
+The following variables are available as both
+.Xr sysctl 8
+variables and
+.Xr loader 8
+tunables:
+.Bl -tag -width indent
+.It Va hw.usb.ukbd.debug
+Debug output level, where 0 is debugging disabled and larger values increase
+debug message verbosity.
+Default is 0.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /dev/kbd*" -compact
+.It Pa /dev/kbd*
+blocking device nodes
+.El
+.Sh EXAMPLES
+.D1 Cd "device ukbd"
+.Pp
+Add the
+.Nm
+driver to the kernel.
+.Sh SEE ALSO
+.Xr kbdcontrol 1 ,
+.Xr ohci 4 ,
+.Xr syscons 4 ,
+.Xr uhci 4 ,
+.Xr usb 4 ,
+.Xr vt 4 ,
+.Xr config 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Lennart Augustsson Aq Mt augustss at cs.chalmers.se
+for
+.Nx
+and was substantially rewritten for
+.Fx
+by
+.An Kazutaka YOKOTA Aq Mt yokota at zodiac.mech.utsunomiya-u.ac.jp .
+.Pp
+This manual page was written by
+.An Nick Hibma Aq Mt n_hibma at FreeBSD.org
+with a large amount of input from
+.An Kazutaka YOKOTA Aq Mt yokota at zodiac.mech.utsunomiya-u.ac.jp .
diff --git a/sys/dev/hid/hkbd.c b/sys/dev/hid/hkbd.c
new file mode 100644
index 000000000000..0edacbefcbfc
--- /dev/null
+++ b/sys/dev/hid/hkbd.c
@@ -0,0 +1,2197 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-NetBSD
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include "opt_kbd.h"
+#include "opt_ukbd.h"
+#include "opt_evdev.h"
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+
+#include <dev/hid/hid.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+
+#define	USB_DEBUG_VAR ukbd_debug
+#include <dev/usb/usb_debug.h>
+
+#include <dev/usb/quirk/usb_quirk.h>
+
+#ifdef EVDEV_SUPPORT
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+#endif
+
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/kbio.h>
+
+#include <dev/kbd/kbdreg.h>
+
+/* the initial key map, accent map and fkey strings */
+#if defined(UKBD_DFLT_KEYMAP) && !defined(KLD_MODULE)
+#define	KBD_DFLT_KEYMAP
+#include "ukbdmap.h"
+#endif
+
+/* the following file must be included after "ukbdmap.h" */
+#include <dev/kbd/kbdtables.h>
+
+#ifdef USB_DEBUG
+static int ukbd_debug = 0;
+static int ukbd_no_leds = 0;
+static int ukbd_pollrate = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+    "USB keyboard");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &ukbd_debug, 0, "Debug level");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, no_leds, CTLFLAG_RWTUN,
+    &ukbd_no_leds, 0, "Disables setting of keyboard leds");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, pollrate, CTLFLAG_RWTUN,
+    &ukbd_pollrate, 0, "Force this polling rate, 1-1000Hz");
+#endif
+
+#define	UKBD_EMULATE_ATSCANCODE	       1
+#define	UKBD_DRIVER_NAME          "ukbd"
+#define	UKBD_NKEYCODE                 256 /* units */
+#define	UKBD_IN_BUF_SIZE  (4 * UKBD_NKEYCODE) /* scancodes */
+#define	UKBD_IN_BUF_FULL  ((UKBD_IN_BUF_SIZE / 2) - 1)	/* scancodes */
+#define	UKBD_NFKEY        (sizeof(fkey_tab)/sizeof(fkey_tab[0]))	/* units */
+#define	UKBD_BUFFER_SIZE	      64	/* bytes */
+#define	UKBD_KEY_PRESSED(map, key) ({ \
+	CTASSERT((key) >= 0 && (key) < UKBD_NKEYCODE); \
+	((map)[(key) / 64] & (1ULL << ((key) % 64))); \
+})
+
+#define	MOD_EJECT	0x01
+#define	MOD_FN		0x02
+
+struct ukbd_data {
+	uint64_t bitmap[howmany(UKBD_NKEYCODE, 64)];
+};
+
+enum {
+	UKBD_INTR_DT_0,
+	UKBD_INTR_DT_1,
+	UKBD_CTRL_LED,
+	UKBD_N_TRANSFER,
+};
+
+struct ukbd_softc {
+	keyboard_t sc_kbd;
+	keymap_t sc_keymap;
+	accentmap_t sc_accmap;
+	fkeytab_t sc_fkeymap[UKBD_NFKEY];
+	uint64_t sc_loc_key_valid[howmany(UKBD_NKEYCODE, 64)];
+	struct hid_location sc_loc_apple_eject;
+	struct hid_location sc_loc_apple_fn;
+	struct hid_location sc_loc_key[UKBD_NKEYCODE];
+	struct hid_location sc_loc_numlock;
+	struct hid_location sc_loc_capslock;
+	struct hid_location sc_loc_scrolllock;
+	struct usb_callout sc_callout;
+	struct ukbd_data sc_ndata;
+	struct ukbd_data sc_odata;
+
+	struct thread *sc_poll_thread;
+	struct usb_device *sc_udev;
+	struct usb_interface *sc_iface;
+	struct usb_xfer *sc_xfer[UKBD_N_TRANSFER];
+#ifdef EVDEV_SUPPORT
+	struct evdev_dev *sc_evdev;
+#endif
+
+	sbintime_t sc_co_basetime;
+	int	sc_delay;
+	uint32_t sc_repeat_time;
+	uint32_t sc_input[UKBD_IN_BUF_SIZE];	/* input buffer */
+	uint32_t sc_time_ms;
+	uint32_t sc_composed_char;	/* composed char code, if non-zero */
+#ifdef UKBD_EMULATE_ATSCANCODE
+	uint32_t sc_buffered_char[2];
+#endif
+	uint32_t sc_flags;		/* flags */
+#define	UKBD_FLAG_COMPOSE	0x00000001
+#define	UKBD_FLAG_POLLING	0x00000002
+#define	UKBD_FLAG_SET_LEDS	0x00000004
+#define	UKBD_FLAG_ATTACHED	0x00000010
+#define	UKBD_FLAG_GONE		0x00000020
+
+#define	UKBD_FLAG_HID_MASK	0x003fffc0
+#define	UKBD_FLAG_APPLE_EJECT	0x00000040
+#define	UKBD_FLAG_APPLE_FN	0x00000080
+#define	UKBD_FLAG_APPLE_SWAP	0x00000100
+#define	UKBD_FLAG_NUMLOCK	0x00080000
+#define	UKBD_FLAG_CAPSLOCK	0x00100000
+#define	UKBD_FLAG_SCROLLLOCK 	0x00200000
+
+	int	sc_mode;		/* input mode (K_XLATE,K_RAW,K_CODE) */
+	int	sc_state;		/* shift/lock key state */
+	int	sc_accents;		/* accent key index (> 0) */
+	int	sc_polling;		/* polling recursion count */
+	int	sc_led_size;
+	int	sc_kbd_size;
+
+	uint16_t sc_inputs;
+	uint16_t sc_inputhead;
+	uint16_t sc_inputtail;
+
+	uint8_t	sc_leds;		/* store for async led requests */
+	uint8_t	sc_iface_index;
+	uint8_t	sc_iface_no;
+	uint8_t sc_id_apple_eject;
+	uint8_t sc_id_apple_fn;
+	uint8_t sc_id_loc_key[UKBD_NKEYCODE];
+	uint8_t sc_id_numlock;
+	uint8_t sc_id_capslock;
+	uint8_t sc_id_scrolllock;
+	uint8_t sc_kbd_id;
+	uint8_t sc_repeat_key;
+
+	uint8_t sc_buffer[UKBD_BUFFER_SIZE];
+};
+
+#define	KEY_NONE	  0x00
+#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)
+
+#define	UKBD_LOCK()	USB_MTX_LOCK(&Giant)
+#define	UKBD_UNLOCK()	USB_MTX_UNLOCK(&Giant)
+#define	UKBD_LOCK_ASSERT()	USB_MTX_ASSERT(&Giant, MA_OWNED)
+
+#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
+ * 
+ * USB Apple Keyboard JIS generates:
+ * 0x90: Kana
+ * 0x91: Eisu
+ */
+static const uint8_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, 123,	/* 80 - 87 */
+	124, 125, 126, 127, 128, NN, NN, NN,	/* 88 - 8F */
+	129, 130, 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 */
+};
+
+static const uint8_t ukbd_boot_desc[] = {
+	0x05, 0x01, 0x09, 0x06, 0xa1,
+	0x01, 0x05, 0x07, 0x19, 0xe0,
+	0x29, 0xe7, 0x15, 0x00, 0x25,
+	0x01, 0x75, 0x01, 0x95, 0x08,
+	0x81, 0x02, 0x95, 0x01, 0x75,
+	0x08, 0x81, 0x01, 0x95, 0x03,
+	0x75, 0x01, 0x05, 0x08, 0x19,
+	0x01, 0x29, 0x03, 0x91, 0x02,
+	0x95, 0x05, 0x75, 0x01, 0x91,
+	0x01, 0x95, 0x06, 0x75, 0x08,
+	0x15, 0x00, 0x26, 0xff, 0x00,
+	0x05, 0x07, 0x19, 0x00, 0x2a,
+	0xff, 0x00, 0x81, 0x00, 0xc0
+};
+
+/* prototypes */
+static void	ukbd_timeout(void *);
+static void	ukbd_set_leds(struct ukbd_softc *, uint8_t);
+static int	ukbd_set_typematic(keyboard_t *, int);
+#ifdef UKBD_EMULATE_ATSCANCODE
+static uint32_t	ukbd_atkeycode(int, const uint64_t *);
+static int	ukbd_key2scan(struct ukbd_softc *, int, const uint64_t *, int);
+#endif
+static uint32_t	ukbd_read_char(keyboard_t *, int);
+static void	ukbd_clear_state(keyboard_t *);
+static int	ukbd_ioctl(keyboard_t *, u_long, caddr_t);
+static int	ukbd_enable(keyboard_t *);
+static int	ukbd_disable(keyboard_t *);
+static void	ukbd_interrupt(struct ukbd_softc *);
+static void	ukbd_event_keyinput(struct ukbd_softc *);
+
+static device_probe_t ukbd_probe;
+static device_attach_t ukbd_attach;
+static device_detach_t ukbd_detach;
+static device_resume_t ukbd_resume;
+
+#ifdef EVDEV_SUPPORT
+static evdev_event_t ukbd_ev_event;
+
+static const struct evdev_methods ukbd_evdev_methods = {
+	.ev_event = ukbd_ev_event,
+};
+#endif
+
+static bool
+ukbd_any_key_pressed(struct ukbd_softc *sc)
+{
+	bool ret = false;
+	unsigned i;
+
+	for (i = 0; i != howmany(UKBD_NKEYCODE, 64); i++)
+		ret |= (sc->sc_odata.bitmap[i] != 0);
+	return (ret);
+}
+
+static bool
+ukbd_any_key_valid(struct ukbd_softc *sc)
+{
+	bool ret = false;
+	unsigned i;
+
+	for (i = 0; i != howmany(UKBD_NKEYCODE, 64); i++)
+		ret |= (sc->sc_loc_key_valid[i] != 0);
+	return (ret);
+}
+
+static bool
+ukbd_is_modifier_key(uint32_t key)
+{
+
+	return (key >= 0xe0 && key <= 0xe7);
+}
+
+static void
+ukbd_start_timer(struct ukbd_softc *sc)
+{
+	sbintime_t delay, now, prec;
+
+	now = sbinuptime();
+
+	/* check if initial delay passed and fallback to key repeat delay */
+	if (sc->sc_delay == 0)
+		sc->sc_delay = sc->sc_kbd.kb_delay2;
+
+	/* compute timeout */
+	delay = SBT_1MS * sc->sc_delay;
+	sc->sc_co_basetime += delay;
+
+	/* check if we are running behind */
+	if (sc->sc_co_basetime < now)
+		sc->sc_co_basetime = now;
+
+	/* This is rarely called, so prefer precision to efficiency. */
+	prec = qmin(delay >> 7, SBT_1MS * 10);
+	usb_callout_reset_sbt(&sc->sc_callout, sc->sc_co_basetime, prec,
+	    ukbd_timeout, sc, C_ABSOLUTE);
+}
+
+static void
+ukbd_put_key(struct ukbd_softc *sc, uint32_t key)
+{
+
+	UKBD_LOCK_ASSERT();
+
+	DPRINTF("0x%02x (%d) %s\n", key, key,
+	    (key & KEY_RELEASE) ? "released" : "pressed");
+
+#ifdef EVDEV_SUPPORT
+	if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD && sc->sc_evdev != NULL)
+		evdev_push_event(sc->sc_evdev, EV_KEY,
+		    evdev_hid2key(KEY_INDEX(key)), !(key & KEY_RELEASE));
+#endif
+
+	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("input buffer is full\n");
+	}
+}
+
+static void
+ukbd_do_poll(struct ukbd_softc *sc, uint8_t wait)
+{
+
+	UKBD_LOCK_ASSERT();
+	KASSERT((sc->sc_flags & UKBD_FLAG_POLLING) != 0,
+	    ("ukbd_do_poll called when not polling\n"));
+	DPRINTFN(2, "polling\n");
+
+	if (USB_IN_POLLING_MODE_FUNC() == 0) {
+		/*
+		 * In this context the kernel is polling for input,
+		 * but the USB subsystem works in normal interrupt-driven
+		 * mode, so we just wait on the USB threads to do the job.
+		 * Note that we currently hold the Giant, but it's also used
+		 * as the transfer mtx, so we must release it while waiting.
+		 */
+		while (sc->sc_inputs == 0) {
+			/*
+			 * Give USB threads a chance to run.  Note that
+			 * kern_yield performs DROP_GIANT + PICKUP_GIANT.
+			 */
+			kern_yield(PRI_UNCHANGED);
+			if (!wait)
+				break;
+		}
+		return;
+	}
+
+	while (sc->sc_inputs == 0) {
+		usbd_transfer_poll(sc->sc_xfer, UKBD_N_TRANSFER);
+
+		/* Delay-optimised support for repetition of keys */
+		if (ukbd_any_key_pressed(sc)) {
+			/* a key is pressed - need timekeeping */
+			DELAY(1000);
+
+			/* 1 millisecond has passed */
+			sc->sc_time_ms += 1;
+		}
+
+		ukbd_interrupt(sc);
+
+		if (!wait)
+			break;
+	}
+}
+
+static int32_t
+ukbd_get_key(struct ukbd_softc *sc, uint8_t wait)
+{
+	int32_t c;
+
+	UKBD_LOCK_ASSERT();
+	KASSERT((USB_IN_POLLING_MODE_FUNC() == 0) ||
+	    (sc->sc_flags & UKBD_FLAG_POLLING) != 0,
+	    ("not polling in kdb or panic\n"));
+
+	if (sc->sc_inputs == 0 &&
+	    (sc->sc_flags & UKBD_FLAG_GONE) == 0) {
+		/* start transfer, if not already started */
+		usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_0]);
+		usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_1]);
+	}
+
+	if (sc->sc_flags & UKBD_FLAG_POLLING)
+		ukbd_do_poll(sc, wait);
+
+	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)
+{
+	const uint32_t now = sc->sc_time_ms;
+	unsigned key;
+
+	UKBD_LOCK_ASSERT();
+
+	/* Check for modifier key changes first */
+	for (key = 0xe0; key != 0xe8; key++) {
+		const uint64_t mask = 1ULL << (key % 64);
+		const uint64_t delta =
+		    sc->sc_odata.bitmap[key / 64] ^
+		    sc->sc_ndata.bitmap[key / 64];
+
+		if (delta & mask) {
+			if (sc->sc_odata.bitmap[key / 64] & mask)
+				ukbd_put_key(sc, key | KEY_RELEASE);
+			else
+				ukbd_put_key(sc, key | KEY_PRESS);
+		}
+	}
+
+	/* Check for key changes */
+	for (key = 0; key != UKBD_NKEYCODE; key++) {
+		const uint64_t mask = 1ULL << (key % 64);
+		const uint64_t delta =
+		    sc->sc_odata.bitmap[key / 64] ^
+		    sc->sc_ndata.bitmap[key / 64];
+
+		if (mask == 1 && delta == 0) {
+			key += 63;
+			continue;	/* skip empty areas */
+		} else if (ukbd_is_modifier_key(key)) {
+			continue;
+		} else if (delta & mask) {
+			if (sc->sc_odata.bitmap[key / 64] & mask) {
+				ukbd_put_key(sc, key | KEY_RELEASE);
+
+				/* clear repeating key, if any */
+				if (sc->sc_repeat_key == key)
+					sc->sc_repeat_key = 0;
+			} else {
+				ukbd_put_key(sc, key | KEY_PRESS);
+
+				sc->sc_co_basetime = sbinuptime();
+				sc->sc_delay = sc->sc_kbd.kb_delay1;
+				ukbd_start_timer(sc);
+
+				/* set repeat time for last key */
+				sc->sc_repeat_time = now + sc->sc_kbd.kb_delay1;
+				sc->sc_repeat_key = key;
+			}
+		}
+	}
+
+	/* synchronize old data with new data */
+	sc->sc_odata = sc->sc_ndata;
+
+	/* check if last key is still pressed */
+	if (sc->sc_repeat_key != 0) {
+		const int32_t dtime = (sc->sc_repeat_time - now);
+
+		/* check if time has elapsed */
+		if (dtime <= 0) {
+			ukbd_put_key(sc, sc->sc_repeat_key | KEY_PRESS);
+			sc->sc_repeat_time = now + sc->sc_kbd.kb_delay2;
+		}
+	}
+
+#ifdef EVDEV_SUPPORT
+	if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD && sc->sc_evdev != NULL)
+		evdev_sync(sc->sc_evdev);
+#endif
+
+	/* wakeup keyboard system */
+	ukbd_event_keyinput(sc);
+}
+
+static void
+ukbd_event_keyinput(struct ukbd_softc *sc)
+{
+	int c;
+
+	UKBD_LOCK_ASSERT();
+
+	if ((sc->sc_flags & UKBD_FLAG_POLLING) != 0)
+		return;
+
+	if (sc->sc_inputs == 0)
+		return;
+
+	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);
+	}
+}
+
+static void
+ukbd_timeout(void *arg)
+{
+	struct ukbd_softc *sc = arg;
+
+	UKBD_LOCK_ASSERT();
+
+	sc->sc_time_ms += sc->sc_delay;
+	sc->sc_delay = 0;
+
+	ukbd_interrupt(sc);
+
+	/* Make sure any leftover key events gets read out */
+	ukbd_event_keyinput(sc);
+
+	if (ukbd_any_key_pressed(sc) || (sc->sc_inputs != 0)) {
+		ukbd_start_timer(sc);
+	}
+}
+
+static uint32_t
+ukbd_apple_fn(uint32_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;
+	}
+}
+
+static uint32_t
+ukbd_apple_swap(uint32_t keycode)
+{
+	switch (keycode) {
+	case 0x35: return 0x64;
+	case 0x64: return 0x35;
+	default: return keycode;
+	}
+}
+
+static void
+ukbd_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ukbd_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t i;
+	uint8_t id;
+	uint8_t modifiers;
+	int offset;
+	int len;
+
+	UKBD_LOCK_ASSERT();
+
+	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
+	pc = usbd_xfer_get_frame(xfer, 0);
+
+	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 */
+			usbd_copy_out(pc, 0, &id, 1);
+			offset = 1;
+			len--;
+			if (len == 0) {
+				DPRINTF("zero length data\n");
+				goto tr_setup;
+			}
+		} else {
+			offset = 0;
+			id = 0;
+		}
+
+		if (len > UKBD_BUFFER_SIZE)
+			len = UKBD_BUFFER_SIZE;
+
+		/* get data */
+		usbd_copy_out(pc, offset, sc->sc_buffer, len);
+
+		/* clear temporary storage */
+		memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
+
+		/* clear modifiers */
+		modifiers = 0;
+
+		/* scan through HID data */
+		if ((sc->sc_flags & UKBD_FLAG_APPLE_EJECT) &&
+		    (id == sc->sc_id_apple_eject)) {
+			if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_apple_eject))
+				modifiers |= MOD_EJECT;
+		}
+		if ((sc->sc_flags & UKBD_FLAG_APPLE_FN) &&
+		    (id == sc->sc_id_apple_fn)) {
+			if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_apple_fn))
+				modifiers |= MOD_FN;
+		}
+
+		for (i = 0; i != UKBD_NKEYCODE; i++) {
+			const uint64_t valid = sc->sc_loc_key_valid[i / 64];
+			const uint64_t mask = 1ULL << (i % 64);
+
+			if (mask == 1 && valid == 0) {
+				i += 63;
+				continue;	/* skip empty areas */
+			} else if (~valid & mask) {
+				continue;	/* location is not valid */
+			} else if (id != sc->sc_id_loc_key[i]) {
+				continue;	/* invalid HID ID */
+			} else if (i == 0) {
+				struct hid_location tmp_loc = sc->sc_loc_key[0];
+				/* range check array size */
+				if (tmp_loc.count > UKBD_NKEYCODE)
+					tmp_loc.count = UKBD_NKEYCODE;
+				while (tmp_loc.count--) {
+					uint32_t key =
+					    hid_get_udata(sc->sc_buffer, len, &tmp_loc);
+					/* advance to next location */
+					tmp_loc.pos += tmp_loc.size;
+					if (modifiers & MOD_FN)
+						key = ukbd_apple_fn(key);
+					if (sc->sc_flags & UKBD_FLAG_APPLE_SWAP)
+						key = ukbd_apple_swap(key);
+					if (key == KEY_NONE || key == KEY_ERROR || key >= UKBD_NKEYCODE)
+						continue;
+					/* set key in bitmap */
+					sc->sc_ndata.bitmap[key / 64] |= 1ULL << (key % 64);
+				}
+			} else if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_key[i])) {
+				uint32_t key = i;
+
+				if (modifiers & MOD_FN)
+					key = ukbd_apple_fn(key);
+				if (sc->sc_flags & UKBD_FLAG_APPLE_SWAP)
+					key = ukbd_apple_swap(key);
+				if (key == KEY_NONE || key == KEY_ERROR || key >= UKBD_NKEYCODE)
+					continue;
+				/* set key in bitmap */
+				sc->sc_ndata.bitmap[key / 64] |= 1ULL << (key % 64);
+			}
+		}
+#ifdef USB_DEBUG
+		DPRINTF("modifiers = 0x%04x\n", modifiers);
+		for (i = 0; i != UKBD_NKEYCODE; i++) {
+			const uint64_t valid = sc->sc_ndata.bitmap[i / 64];
+			const uint64_t mask = 1ULL << (i % 64);
+
+			if (valid & mask)
+				DPRINTF("Key 0x%02x pressed\n", i);
+		}
+#endif
+		ukbd_interrupt(sc);
+
+	case USB_ST_SETUP:
+tr_setup:
+		if (sc->sc_inputs < UKBD_IN_BUF_FULL) {
+			usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+			usbd_transfer_submit(xfer);
+		} else {
+			DPRINTF("input queue is full!\n");
+		}
+		break;
+
+	default:			/* Error */
+		DPRINTF("error=%s\n", usbd_errstr(error));
+
*** 1426 LINES SKIPPED ***


More information about the dev-commits-src-all mailing list