git: 08d88401754b - main - hid: Copy ukbd(4) to HID subsystem.
Vladimir Kondratyev
wulf at FreeBSD.org
Thu Jan 7 23:20:53 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-main
mailing list