Date: Thu, 7 Jan 2021 23:20:50 GMT From: Vladimir Kondratyev <wulf@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 08d88401754b - main - hid: Copy ukbd(4) to HID subsystem. Message-ID: <202101072320.107NKo5T063132@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by wulf: URL: https://cgit.FreeBSD.org/src/commit/?id=08d88401754b335a549507b86d219c90a0fc7e13 commit 08d88401754b335a549507b86d219c90a0fc7e13 Author: Vladimir Kondratyev <wulf@FreeBSD.org> AuthorDate: 2020-10-11 22:36:35 +0000 Commit: Vladimir Kondratyev <wulf@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@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@cs.chalmers.se +for +.Nx +and was substantially rewritten for +.Fx +by +.An Kazutaka YOKOTA Aq Mt yokota@zodiac.mech.utsunomiya-u.ac.jp . +.Pp +This manual page was written by +.An Nick Hibma Aq Mt n_hibma@FreeBSD.org +with a large amount of input from +.An Kazutaka YOKOTA Aq Mt yokota@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@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 ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202101072320.107NKo5T063132>