The branch main has been updated by wulf:

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

commit 08d88401754b335a549507b86d219c90a0fc7e13
Author:     Vladimir Kondratyev <w...@freebsd.org>
AuthorDate: 2020-10-11 22:36:35 +0000
Commit:     Vladimir Kondratyev <w...@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_hi...@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 augus...@cs.chalmers.se
+for
+.Nx
+and was substantially rewritten for
+.Fx
+by
+.An Kazutaka YOKOTA Aq Mt yok...@zodiac.mech.utsunomiya-u.ac.jp .
+.Pp
+This manual page was written by
+.An Nick Hibma Aq Mt n_hi...@freebsd.org
+with a large amount of input from
+.An Kazutaka YOKOTA Aq Mt yok...@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 (lenn...@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 ***
_______________________________________________
dev-commits-src-main@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-main
To unsubscribe, send any mail to "dev-commits-src-main-unsubscr...@freebsd.org"

Reply via email to