Bring in USB4BSD, Hans Petter Selasky rework of the USB stack
that includes significant features and SMP safety.
This commit includes a more or less complete rewrite of the *BSD USB
stack, including Host Controller and Device Controller drivers and
updating all existing USB drivers to use the new USB API:
1) A brief feature list:
- A new and mutex enabled USB API.
- Many USB drivers are now running Giant free.
- Linux USB kernel compatibility layer.
- New UGEN backend and libusb library, finally solves the "driver
unloading" problem. The new BSD licensed libusb20 library is fully
compatible with libusb-0.1.12 from sourceforge.
- New "usbconfig" utility, for easy configuration of USB.
- Full support for Split transactions, which means you can use your
full speed USB audio device on a high speed USB HUB.
- Full support for HS ISOC transactions, which makes writing drivers
for various HS webcams possible, for example.
- Full support for USB on embedded platforms, mostly cache flushing
and buffer invalidating stuff.
- Safer parsing of USB descriptors.
- Autodetect of annoying USB install disks.
- Support for USB device side mode, also called USB gadget mode,
using the same API like the USB host side. In other words the new
USB stack is symmetric with regard to host and device side.
- Support for USB transfers like I/O vectors, means more throughput
and less interrupts.
- ... see the FreeBSD quarterly status reports under "USB project"
2) To enable the driver in the default kernel build:
2.a) Remove all existing USB device options from your kernel config
file.
2.b) Add the following USB device options to your kernel configuration
file:
# USB core support
device usb2_core
# USB controller support
device usb2_controller
device usb2_controller_ehci
device usb2_controller_ohci
device usb2_controller_uhci
# USB mass storage support
device usb2_storage
device usb2_storage_mass
# USB ethernet support, requires miibus
device usb2_ethernet
device usb2_ethernet_aue
device usb2_ethernet_axe
device usb2_ethernet_cdce
device usb2_ethernet_cue
device usb2_ethernet_kue
device usb2_ethernet_rue
device usb2_ethernet_dav
# USB wireless LAN support
device usb2_wlan
device usb2_wlan_rum
device usb2_wlan_ral
device usb2_wlan_zyd
# USB serial device support
device usb2_serial
device usb2_serial_ark
device usb2_serial_bsa
device usb2_serial_bser
device usb2_serial_chcom
device usb2_serial_cycom
device usb2_serial_foma
device usb2_serial_ftdi
device usb2_serial_gensa
device usb2_serial_ipaq
device usb2_serial_lpt
device usb2_serial_mct
device usb2_serial_modem
device usb2_serial_moscom
device usb2_serial_plcom
device usb2_serial_visor
device usb2_serial_vscom
# USB bluetooth support
device usb2_bluetooth
device usb2_bluetooth_ng
# USB input device support
device usb2_input
device usb2_input_hid
device usb2_input_kbd
device usb2_input_ms
# USB sound and MIDI device support
device usb2_sound
2) To enable the driver at runtime:
2.a) Unload all existing USB modules. If USB is compiled into the
kernel then you might have to build a new kernel.
2.b) Load the "usb2_xxx.ko" modules under /boot/kernel having the same
base name like the kernel device option.
Submitted by: Hans Petter Selasky hselasky at c2i dot net
Reviewed by: imp, alfred
2008-11-04 02:31:03 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
|
|
|
|
/*-
|
|
|
|
* 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.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the NetBSD
|
|
|
|
* Foundation, Inc. and its contributors.
|
|
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "opt_compat.h"
|
|
|
|
#include "opt_kbd.h"
|
|
|
|
#include "opt_ukbd.h"
|
|
|
|
|
|
|
|
#include <dev/usb2/include/usb2_standard.h>
|
|
|
|
#include <dev/usb2/include/usb2_mfunc.h>
|
|
|
|
#include <dev/usb2/include/usb2_error.h>
|
|
|
|
#include <dev/usb2/include/usb2_hid.h>
|
|
|
|
|
|
|
|
#define USB_DEBUG_VAR ukbd_debug
|
|
|
|
|
|
|
|
#include <dev/usb2/core/usb2_core.h>
|
|
|
|
#include <dev/usb2/core/usb2_util.h>
|
|
|
|
#include <dev/usb2/core/usb2_debug.h>
|
|
|
|
#include <dev/usb2/core/usb2_busdma.h>
|
|
|
|
#include <dev/usb2/core/usb2_process.h>
|
|
|
|
#include <dev/usb2/core/usb2_transfer.h>
|
|
|
|
#include <dev/usb2/core/usb2_request.h>
|
|
|
|
#include <dev/usb2/core/usb2_dynamic.h>
|
|
|
|
#include <dev/usb2/core/usb2_hid.h>
|
|
|
|
|
|
|
|
#include <dev/usb2/input/usb2_input.h>
|
|
|
|
|
|
|
|
#include <dev/usb2/quirk/usb2_quirk.h>
|
|
|
|
|
|
|
|
#include <sys/ioccom.h>
|
|
|
|
#include <sys/filio.h>
|
|
|
|
#include <sys/tty.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>
|
|
|
|
|
|
|
|
#if USB_DEBUG
|
|
|
|
static int ukbd_debug = 0;
|
|
|
|
|
|
|
|
SYSCTL_NODE(_hw_usb2, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB ukbd");
|
|
|
|
SYSCTL_INT(_hw_usb2_ukbd, OID_AUTO, debug, CTLFLAG_RW,
|
|
|
|
&ukbd_debug, 0, "Debug level");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define UPROTO_BOOT_KEYBOARD 1
|
|
|
|
|
|
|
|
#define UKBD_EMULATE_ATSCANCODE 1
|
|
|
|
#define UKBD_DRIVER_NAME "ukbd"
|
|
|
|
#define UKBD_NMOD 8 /* units */
|
|
|
|
#define UKBD_NKEYCODE 6 /* units */
|
|
|
|
#define UKBD_N_TRANSFER 3 /* units */
|
|
|
|
#define UKBD_IN_BUF_SIZE (2*(UKBD_NMOD + (2*UKBD_NKEYCODE))) /* bytes */
|
|
|
|
#define UKBD_IN_BUF_FULL (UKBD_IN_BUF_SIZE / 2) /* bytes */
|
|
|
|
#define UKBD_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
|
|
|
|
|
|
|
|
struct ukbd_data {
|
|
|
|
uint8_t modifiers;
|
|
|
|
#define MOD_CONTROL_L 0x01
|
|
|
|
#define MOD_CONTROL_R 0x10
|
|
|
|
#define MOD_SHIFT_L 0x02
|
|
|
|
#define MOD_SHIFT_R 0x20
|
|
|
|
#define MOD_ALT_L 0x04
|
|
|
|
#define MOD_ALT_R 0x40
|
|
|
|
#define MOD_WIN_L 0x08
|
|
|
|
#define MOD_WIN_R 0x80
|
|
|
|
uint8_t reserved;
|
|
|
|
uint8_t keycode[UKBD_NKEYCODE];
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
struct ukbd_softc {
|
|
|
|
keyboard_t sc_kbd;
|
|
|
|
keymap_t sc_keymap;
|
|
|
|
accentmap_t sc_accmap;
|
|
|
|
fkeytab_t sc_fkeymap[UKBD_NFKEY];
|
|
|
|
struct usb2_callout sc_callout;
|
|
|
|
struct ukbd_data sc_ndata;
|
|
|
|
struct ukbd_data sc_odata;
|
|
|
|
|
|
|
|
struct usb2_device *sc_udev;
|
|
|
|
struct usb2_interface *sc_iface;
|
|
|
|
struct usb2_xfer *sc_xfer[UKBD_N_TRANSFER];
|
|
|
|
|
|
|
|
uint32_t sc_ntime[UKBD_NKEYCODE];
|
|
|
|
uint32_t sc_otime[UKBD_NKEYCODE];
|
|
|
|
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 0x0001
|
|
|
|
#define UKBD_FLAG_POLLING 0x0002
|
|
|
|
#define UKBD_FLAG_SET_LEDS 0x0004
|
|
|
|
#define UKBD_FLAG_INTR_STALL 0x0008
|
|
|
|
#define UKBD_FLAG_ATTACHED 0x0010
|
|
|
|
#define UKBD_FLAG_GONE 0x0020
|
|
|
|
|
|
|
|
int32_t sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
|
|
|
|
int32_t sc_state; /* shift/lock key state */
|
|
|
|
int32_t sc_accents; /* accent key index (> 0) */
|
|
|
|
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
|
|
|
#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)
|
|
|
|
|
|
|
|
struct ukbd_mods {
|
|
|
|
uint32_t mask, key;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct ukbd_mods ukbd_mods[UKBD_NMOD] = {
|
|
|
|
{MOD_CONTROL_L, 0xe0},
|
|
|
|
{MOD_CONTROL_R, 0xe4},
|
|
|
|
{MOD_SHIFT_L, 0xe1},
|
|
|
|
{MOD_SHIFT_R, 0xe5},
|
|
|
|
{MOD_ALT_L, 0xe2},
|
|
|
|
{MOD_ALT_R, 0xe6},
|
|
|
|
{MOD_WIN_L, 0xe3},
|
|
|
|
{MOD_WIN_R, 0xe7},
|
|
|
|
};
|
|
|
|
|
|
|
|
#define NN 0 /* no translation */
|
|
|
|
/*
|
|
|
|
* Translate USB keycodes to AT keyboard scancodes.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* FIXME: Mac USB keyboard generates:
|
|
|
|
* 0x53: keypad NumLock/Clear
|
|
|
|
* 0x66: Power
|
|
|
|
* 0x67: keypad =
|
|
|
|
* 0x68: F13
|
|
|
|
* 0x69: F14
|
|
|
|
* 0x6a: F15
|
|
|
|
*/
|
|
|
|
static const 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, 115, /* 80 - 87 */
|
|
|
|
112, 125, 121, 123, NN, NN, NN, NN, /* 88 - 8F */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* 90 - 97 */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */
|
|
|
|
29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */
|
|
|
|
NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* prototypes */
|
2008-12-11 23:13:02 +00:00
|
|
|
static void ukbd_timeout(void *);
|
|
|
|
static void ukbd_set_leds(struct ukbd_softc *, uint8_t);
|
|
|
|
static int ukbd_set_typematic(keyboard_t *, int);
|
Bring in USB4BSD, Hans Petter Selasky rework of the USB stack
that includes significant features and SMP safety.
This commit includes a more or less complete rewrite of the *BSD USB
stack, including Host Controller and Device Controller drivers and
updating all existing USB drivers to use the new USB API:
1) A brief feature list:
- A new and mutex enabled USB API.
- Many USB drivers are now running Giant free.
- Linux USB kernel compatibility layer.
- New UGEN backend and libusb library, finally solves the "driver
unloading" problem. The new BSD licensed libusb20 library is fully
compatible with libusb-0.1.12 from sourceforge.
- New "usbconfig" utility, for easy configuration of USB.
- Full support for Split transactions, which means you can use your
full speed USB audio device on a high speed USB HUB.
- Full support for HS ISOC transactions, which makes writing drivers
for various HS webcams possible, for example.
- Full support for USB on embedded platforms, mostly cache flushing
and buffer invalidating stuff.
- Safer parsing of USB descriptors.
- Autodetect of annoying USB install disks.
- Support for USB device side mode, also called USB gadget mode,
using the same API like the USB host side. In other words the new
USB stack is symmetric with regard to host and device side.
- Support for USB transfers like I/O vectors, means more throughput
and less interrupts.
- ... see the FreeBSD quarterly status reports under "USB project"
2) To enable the driver in the default kernel build:
2.a) Remove all existing USB device options from your kernel config
file.
2.b) Add the following USB device options to your kernel configuration
file:
# USB core support
device usb2_core
# USB controller support
device usb2_controller
device usb2_controller_ehci
device usb2_controller_ohci
device usb2_controller_uhci
# USB mass storage support
device usb2_storage
device usb2_storage_mass
# USB ethernet support, requires miibus
device usb2_ethernet
device usb2_ethernet_aue
device usb2_ethernet_axe
device usb2_ethernet_cdce
device usb2_ethernet_cue
device usb2_ethernet_kue
device usb2_ethernet_rue
device usb2_ethernet_dav
# USB wireless LAN support
device usb2_wlan
device usb2_wlan_rum
device usb2_wlan_ral
device usb2_wlan_zyd
# USB serial device support
device usb2_serial
device usb2_serial_ark
device usb2_serial_bsa
device usb2_serial_bser
device usb2_serial_chcom
device usb2_serial_cycom
device usb2_serial_foma
device usb2_serial_ftdi
device usb2_serial_gensa
device usb2_serial_ipaq
device usb2_serial_lpt
device usb2_serial_mct
device usb2_serial_modem
device usb2_serial_moscom
device usb2_serial_plcom
device usb2_serial_visor
device usb2_serial_vscom
# USB bluetooth support
device usb2_bluetooth
device usb2_bluetooth_ng
# USB input device support
device usb2_input
device usb2_input_hid
device usb2_input_kbd
device usb2_input_ms
# USB sound and MIDI device support
device usb2_sound
2) To enable the driver at runtime:
2.a) Unload all existing USB modules. If USB is compiled into the
kernel then you might have to build a new kernel.
2.b) Load the "usb2_xxx.ko" modules under /boot/kernel having the same
base name like the kernel device option.
Submitted by: Hans Petter Selasky hselasky at c2i dot net
Reviewed by: imp, alfred
2008-11-04 02:31:03 +00:00
|
|
|
#ifdef UKBD_EMULATE_ATSCANCODE
|
2008-12-11 23:13:02 +00:00
|
|
|
static int ukbd_key2scan(struct ukbd_softc *, int, int, int);
|
Bring in USB4BSD, Hans Petter Selasky rework of the USB stack
that includes significant features and SMP safety.
This commit includes a more or less complete rewrite of the *BSD USB
stack, including Host Controller and Device Controller drivers and
updating all existing USB drivers to use the new USB API:
1) A brief feature list:
- A new and mutex enabled USB API.
- Many USB drivers are now running Giant free.
- Linux USB kernel compatibility layer.
- New UGEN backend and libusb library, finally solves the "driver
unloading" problem. The new BSD licensed libusb20 library is fully
compatible with libusb-0.1.12 from sourceforge.
- New "usbconfig" utility, for easy configuration of USB.
- Full support for Split transactions, which means you can use your
full speed USB audio device on a high speed USB HUB.
- Full support for HS ISOC transactions, which makes writing drivers
for various HS webcams possible, for example.
- Full support for USB on embedded platforms, mostly cache flushing
and buffer invalidating stuff.
- Safer parsing of USB descriptors.
- Autodetect of annoying USB install disks.
- Support for USB device side mode, also called USB gadget mode,
using the same API like the USB host side. In other words the new
USB stack is symmetric with regard to host and device side.
- Support for USB transfers like I/O vectors, means more throughput
and less interrupts.
- ... see the FreeBSD quarterly status reports under "USB project"
2) To enable the driver in the default kernel build:
2.a) Remove all existing USB device options from your kernel config
file.
2.b) Add the following USB device options to your kernel configuration
file:
# USB core support
device usb2_core
# USB controller support
device usb2_controller
device usb2_controller_ehci
device usb2_controller_ohci
device usb2_controller_uhci
# USB mass storage support
device usb2_storage
device usb2_storage_mass
# USB ethernet support, requires miibus
device usb2_ethernet
device usb2_ethernet_aue
device usb2_ethernet_axe
device usb2_ethernet_cdce
device usb2_ethernet_cue
device usb2_ethernet_kue
device usb2_ethernet_rue
device usb2_ethernet_dav
# USB wireless LAN support
device usb2_wlan
device usb2_wlan_rum
device usb2_wlan_ral
device usb2_wlan_zyd
# USB serial device support
device usb2_serial
device usb2_serial_ark
device usb2_serial_bsa
device usb2_serial_bser
device usb2_serial_chcom
device usb2_serial_cycom
device usb2_serial_foma
device usb2_serial_ftdi
device usb2_serial_gensa
device usb2_serial_ipaq
device usb2_serial_lpt
device usb2_serial_mct
device usb2_serial_modem
device usb2_serial_moscom
device usb2_serial_plcom
device usb2_serial_visor
device usb2_serial_vscom
# USB bluetooth support
device usb2_bluetooth
device usb2_bluetooth_ng
# USB input device support
device usb2_input
device usb2_input_hid
device usb2_input_kbd
device usb2_input_ms
# USB sound and MIDI device support
device usb2_sound
2) To enable the driver at runtime:
2.a) Unload all existing USB modules. If USB is compiled into the
kernel then you might have to build a new kernel.
2.b) Load the "usb2_xxx.ko" modules under /boot/kernel having the same
base name like the kernel device option.
Submitted by: Hans Petter Selasky hselasky at c2i dot net
Reviewed by: imp, alfred
2008-11-04 02:31:03 +00:00
|
|
|
#endif
|
2008-12-11 23:13:02 +00:00
|
|
|
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 *);
|
Bring in USB4BSD, Hans Petter Selasky rework of the USB stack
that includes significant features and SMP safety.
This commit includes a more or less complete rewrite of the *BSD USB
stack, including Host Controller and Device Controller drivers and
updating all existing USB drivers to use the new USB API:
1) A brief feature list:
- A new and mutex enabled USB API.
- Many USB drivers are now running Giant free.
- Linux USB kernel compatibility layer.
- New UGEN backend and libusb library, finally solves the "driver
unloading" problem. The new BSD licensed libusb20 library is fully
compatible with libusb-0.1.12 from sourceforge.
- New "usbconfig" utility, for easy configuration of USB.
- Full support for Split transactions, which means you can use your
full speed USB audio device on a high speed USB HUB.
- Full support for HS ISOC transactions, which makes writing drivers
for various HS webcams possible, for example.
- Full support for USB on embedded platforms, mostly cache flushing
and buffer invalidating stuff.
- Safer parsing of USB descriptors.
- Autodetect of annoying USB install disks.
- Support for USB device side mode, also called USB gadget mode,
using the same API like the USB host side. In other words the new
USB stack is symmetric with regard to host and device side.
- Support for USB transfers like I/O vectors, means more throughput
and less interrupts.
- ... see the FreeBSD quarterly status reports under "USB project"
2) To enable the driver in the default kernel build:
2.a) Remove all existing USB device options from your kernel config
file.
2.b) Add the following USB device options to your kernel configuration
file:
# USB core support
device usb2_core
# USB controller support
device usb2_controller
device usb2_controller_ehci
device usb2_controller_ohci
device usb2_controller_uhci
# USB mass storage support
device usb2_storage
device usb2_storage_mass
# USB ethernet support, requires miibus
device usb2_ethernet
device usb2_ethernet_aue
device usb2_ethernet_axe
device usb2_ethernet_cdce
device usb2_ethernet_cue
device usb2_ethernet_kue
device usb2_ethernet_rue
device usb2_ethernet_dav
# USB wireless LAN support
device usb2_wlan
device usb2_wlan_rum
device usb2_wlan_ral
device usb2_wlan_zyd
# USB serial device support
device usb2_serial
device usb2_serial_ark
device usb2_serial_bsa
device usb2_serial_bser
device usb2_serial_chcom
device usb2_serial_cycom
device usb2_serial_foma
device usb2_serial_ftdi
device usb2_serial_gensa
device usb2_serial_ipaq
device usb2_serial_lpt
device usb2_serial_mct
device usb2_serial_modem
device usb2_serial_moscom
device usb2_serial_plcom
device usb2_serial_visor
device usb2_serial_vscom
# USB bluetooth support
device usb2_bluetooth
device usb2_bluetooth_ng
# USB input device support
device usb2_input
device usb2_input_hid
device usb2_input_kbd
device usb2_input_ms
# USB sound and MIDI device support
device usb2_sound
2) To enable the driver at runtime:
2.a) Unload all existing USB modules. If USB is compiled into the
kernel then you might have to build a new kernel.
2.b) Load the "usb2_xxx.ko" modules under /boot/kernel having the same
base name like the kernel device option.
Submitted by: Hans Petter Selasky hselasky at c2i dot net
Reviewed by: imp, alfred
2008-11-04 02:31:03 +00:00
|
|
|
|
|
|
|
static device_probe_t ukbd_probe;
|
|
|
|
static device_attach_t ukbd_attach;
|
|
|
|
static device_detach_t ukbd_detach;
|
|
|
|
static device_resume_t ukbd_resume;
|
|
|
|
|
|
|
|
static void
|
|
|
|
ukbd_put_key(struct ukbd_softc *sc, uint32_t key)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
DPRINTF("0x%02x (%d) %s\n", key, key,
|
|
|
|
(key & KEY_RELEASE) ? "released" : "pressed");
|
|
|
|
|
|
|
|
if (sc->sc_inputs < UKBD_IN_BUF_SIZE) {
|
|
|
|
sc->sc_input[sc->sc_inputtail] = key;
|
|
|
|
++(sc->sc_inputs);
|
|
|
|
++(sc->sc_inputtail);
|
|
|
|
if (sc->sc_inputtail >= UKBD_IN_BUF_SIZE) {
|
|
|
|
sc->sc_inputtail = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DPRINTF("input buffer is full\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t
|
|
|
|
ukbd_get_key(struct ukbd_softc *sc, uint8_t wait)
|
|
|
|
{
|
|
|
|
int32_t c;
|
|
|
|
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
if (sc->sc_inputs == 0) {
|
|
|
|
/* start transfer, if not already started */
|
|
|
|
usb2_transfer_start(sc->sc_xfer[0]);
|
|
|
|
}
|
|
|
|
if (sc->sc_flags & UKBD_FLAG_POLLING) {
|
|
|
|
DPRINTFN(2, "polling\n");
|
|
|
|
|
|
|
|
while (sc->sc_inputs == 0) {
|
|
|
|
|
|
|
|
usb2_do_poll(sc->sc_xfer, UKBD_N_TRANSFER);
|
|
|
|
|
|
|
|
DELAY(1000); /* delay 1 ms */
|
|
|
|
|
|
|
|
sc->sc_time_ms++;
|
|
|
|
|
|
|
|
/* support repetition of keys: */
|
|
|
|
|
|
|
|
ukbd_interrupt(sc);
|
|
|
|
|
|
|
|
if (!wait) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
uint32_t n_mod;
|
|
|
|
uint32_t o_mod;
|
|
|
|
uint32_t now = sc->sc_time_ms;
|
|
|
|
uint32_t dtime;
|
|
|
|
uint32_t c;
|
|
|
|
uint8_t key;
|
|
|
|
uint8_t i;
|
|
|
|
uint8_t j;
|
|
|
|
|
|
|
|
if (sc->sc_ndata.keycode[0] == KEY_ERROR) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
n_mod = sc->sc_ndata.modifiers;
|
|
|
|
o_mod = sc->sc_odata.modifiers;
|
|
|
|
if (n_mod != o_mod) {
|
|
|
|
for (i = 0; i < UKBD_NMOD; i++) {
|
|
|
|
if ((n_mod & ukbd_mods[i].mask) !=
|
|
|
|
(o_mod & ukbd_mods[i].mask)) {
|
|
|
|
ukbd_put_key(sc, ukbd_mods[i].key |
|
|
|
|
((n_mod & ukbd_mods[i].mask) ?
|
|
|
|
KEY_PRESS : KEY_RELEASE));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Check for released keys. */
|
|
|
|
for (i = 0; i < UKBD_NKEYCODE; i++) {
|
|
|
|
key = sc->sc_odata.keycode[i];
|
|
|
|
if (key == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (j = 0; j < UKBD_NKEYCODE; j++) {
|
|
|
|
if (sc->sc_ndata.keycode[j] == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (key == sc->sc_ndata.keycode[j]) {
|
|
|
|
goto rfound;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ukbd_put_key(sc, key | KEY_RELEASE);
|
|
|
|
rfound: ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for pressed keys. */
|
|
|
|
for (i = 0; i < UKBD_NKEYCODE; i++) {
|
|
|
|
key = sc->sc_ndata.keycode[i];
|
|
|
|
if (key == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay1;
|
|
|
|
for (j = 0; j < UKBD_NKEYCODE; j++) {
|
|
|
|
if (sc->sc_odata.keycode[j] == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (key == sc->sc_odata.keycode[j]) {
|
|
|
|
|
|
|
|
/* key is still pressed */
|
|
|
|
|
|
|
|
sc->sc_ntime[i] = sc->sc_otime[j];
|
|
|
|
dtime = (sc->sc_otime[j] - now);
|
|
|
|
|
|
|
|
if (!(dtime & 0x80000000)) {
|
|
|
|
/* time has not elapsed */
|
|
|
|
goto pfound;
|
|
|
|
}
|
|
|
|
sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ukbd_put_key(sc, key | KEY_PRESS);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If any other key is presently down, force its repeat to be
|
|
|
|
* well in the future (100s). This makes the last key to be
|
|
|
|
* pressed do the autorepeat.
|
|
|
|
*/
|
|
|
|
for (j = 0; j != UKBD_NKEYCODE; j++) {
|
|
|
|
if (j != i)
|
|
|
|
sc->sc_ntime[j] = now + (100 * 1000);
|
|
|
|
}
|
|
|
|
pfound: ;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_odata = sc->sc_ndata;
|
|
|
|
|
|
|
|
bcopy(sc->sc_ntime, sc->sc_otime, sizeof(sc->sc_otime));
|
|
|
|
|
|
|
|
if (sc->sc_inputs == 0) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
if (sc->sc_flags & UKBD_FLAG_POLLING) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
|
|
|
|
KBD_IS_BUSY(&sc->sc_kbd)) {
|
|
|
|
/* let the callback function process the input */
|
|
|
|
(sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
|
|
|
|
sc->sc_kbd.kb_callback.kc_arg);
|
|
|
|
} else {
|
|
|
|
/* read and discard the input, no one is waiting for it */
|
|
|
|
do {
|
|
|
|
c = ukbd_read_char(&sc->sc_kbd, 0);
|
|
|
|
} while (c != NOKEY);
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ukbd_timeout(void *arg)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = arg;
|
|
|
|
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
if (!(sc->sc_flags & UKBD_FLAG_POLLING)) {
|
|
|
|
sc->sc_time_ms += 25; /* milliseconds */
|
|
|
|
}
|
|
|
|
ukbd_interrupt(sc);
|
|
|
|
|
|
|
|
usb2_callout_reset(&sc->sc_callout, hz / 40, &ukbd_timeout, sc);
|
|
|
|
|
|
|
|
mtx_unlock(&Giant);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ukbd_clear_stall_callback(struct usb2_xfer *xfer)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = xfer->priv_sc;
|
|
|
|
struct usb2_xfer *xfer_other = sc->sc_xfer[0];
|
|
|
|
|
|
|
|
if (usb2_clear_stall_callback(xfer, xfer_other)) {
|
|
|
|
DPRINTF("stall cleared\n");
|
|
|
|
sc->sc_flags &= ~UKBD_FLAG_INTR_STALL;
|
|
|
|
usb2_transfer_start(xfer_other);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ukbd_intr_callback(struct usb2_xfer *xfer)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = xfer->priv_sc;
|
|
|
|
uint16_t len = xfer->actlen;
|
|
|
|
uint8_t i;
|
|
|
|
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
|
|
case USB_ST_TRANSFERRED:
|
|
|
|
DPRINTF("actlen=%d bytes\n", len);
|
|
|
|
|
|
|
|
if (len > sizeof(sc->sc_ndata)) {
|
|
|
|
len = sizeof(sc->sc_ndata);
|
|
|
|
}
|
|
|
|
if (len) {
|
|
|
|
bzero(&sc->sc_ndata, sizeof(sc->sc_ndata));
|
|
|
|
usb2_copy_out(xfer->frbuffers, 0, &sc->sc_ndata, len);
|
|
|
|
#if USB_DEBUG
|
|
|
|
if (sc->sc_ndata.modifiers) {
|
|
|
|
DPRINTF("mod: 0x%04x\n", sc->sc_ndata.modifiers);
|
|
|
|
}
|
|
|
|
for (i = 0; i < UKBD_NKEYCODE; i++) {
|
|
|
|
if (sc->sc_ndata.keycode[i]) {
|
|
|
|
DPRINTF("[%d] = %d\n", i, sc->sc_ndata.keycode[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* USB_DEBUG */
|
|
|
|
ukbd_interrupt(sc);
|
|
|
|
}
|
|
|
|
case USB_ST_SETUP:
|
|
|
|
if (sc->sc_flags & UKBD_FLAG_INTR_STALL) {
|
|
|
|
usb2_transfer_start(sc->sc_xfer[1]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (sc->sc_inputs < UKBD_IN_BUF_FULL) {
|
|
|
|
xfer->frlengths[0] = xfer->max_data_length;
|
|
|
|
usb2_start_hardware(xfer);
|
|
|
|
} else {
|
|
|
|
DPRINTF("input queue is full!\n");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
default: /* Error */
|
|
|
|
DPRINTF("error=%s\n", usb2_errstr(xfer->error));
|
|
|
|
|
|
|
|
if (xfer->error != USB_ERR_CANCELLED) {
|
|
|
|
/* try to clear stall first */
|
|
|
|
sc->sc_flags |= UKBD_FLAG_INTR_STALL;
|
|
|
|
usb2_transfer_start(sc->sc_xfer[1]);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ukbd_set_leds_callback(struct usb2_xfer *xfer)
|
|
|
|
{
|
|
|
|
struct usb2_device_request req;
|
|
|
|
uint8_t buf[1];
|
|
|
|
struct ukbd_softc *sc = xfer->priv_sc;
|
|
|
|
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
|
|
case USB_ST_TRANSFERRED:
|
|
|
|
case USB_ST_SETUP:
|
|
|
|
if (sc->sc_flags & UKBD_FLAG_SET_LEDS) {
|
|
|
|
sc->sc_flags &= ~UKBD_FLAG_SET_LEDS;
|
|
|
|
|
|
|
|
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
|
|
|
|
req.bRequest = UR_SET_REPORT;
|
|
|
|
USETW2(req.wValue, UHID_OUTPUT_REPORT, 0);
|
|
|
|
req.wIndex[0] = sc->sc_iface_no;
|
|
|
|
req.wIndex[1] = 0;
|
|
|
|
USETW(req.wLength, 1);
|
|
|
|
|
|
|
|
buf[0] = sc->sc_leds;
|
|
|
|
|
|
|
|
usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
|
|
|
|
usb2_copy_in(xfer->frbuffers + 1, 0, buf, sizeof(buf));
|
|
|
|
|
|
|
|
xfer->frlengths[0] = sizeof(req);
|
|
|
|
xfer->frlengths[1] = sizeof(buf);
|
|
|
|
xfer->nframes = 2;
|
|
|
|
usb2_start_hardware(xfer);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
default: /* Error */
|
|
|
|
DPRINTFN(0, "error=%s\n", usb2_errstr(xfer->error));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct usb2_config ukbd_config[UKBD_N_TRANSFER] = {
|
|
|
|
|
|
|
|
[0] = {
|
|
|
|
.type = UE_INTERRUPT,
|
|
|
|
.endpoint = UE_ADDR_ANY,
|
|
|
|
.direction = UE_DIR_IN,
|
|
|
|
.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
|
|
|
|
.mh.bufsize = 0, /* use wMaxPacketSize */
|
|
|
|
.mh.callback = &ukbd_intr_callback,
|
|
|
|
},
|
|
|
|
|
|
|
|
[1] = {
|
|
|
|
.type = UE_CONTROL,
|
|
|
|
.endpoint = 0x00, /* Control pipe */
|
|
|
|
.direction = UE_DIR_ANY,
|
|
|
|
.mh.bufsize = sizeof(struct usb2_device_request),
|
|
|
|
.mh.callback = &ukbd_clear_stall_callback,
|
|
|
|
.mh.timeout = 1000, /* 1 second */
|
|
|
|
.mh.interval = 50, /* 50ms */
|
|
|
|
},
|
|
|
|
|
|
|
|
[2] = {
|
|
|
|
.type = UE_CONTROL,
|
|
|
|
.endpoint = 0x00, /* Control pipe */
|
|
|
|
.direction = UE_DIR_ANY,
|
|
|
|
.mh.bufsize = sizeof(struct usb2_device_request) + 1,
|
|
|
|
.mh.callback = &ukbd_set_leds_callback,
|
|
|
|
.mh.timeout = 1000, /* 1 second */
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
ukbd_probe(device_t dev)
|
|
|
|
{
|
|
|
|
keyboard_switch_t *sw = kbd_get_switch(UKBD_DRIVER_NAME);
|
|
|
|
struct usb2_attach_arg *uaa = device_get_ivars(dev);
|
|
|
|
|
|
|
|
DPRINTFN(11, "\n");
|
|
|
|
|
|
|
|
if (sw == NULL) {
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
if (uaa->usb2_mode != USB_MODE_HOST) {
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
/* check that the keyboard speaks the boot protocol: */
|
|
|
|
if ((uaa->info.bInterfaceClass == UICLASS_HID)
|
|
|
|
&& (uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT)
|
|
|
|
&& (uaa->info.bInterfaceProtocol == UPROTO_BOOT_KEYBOARD)) {
|
|
|
|
if (usb2_test_quirk(uaa, UQ_KBD_IGNORE))
|
|
|
|
return (ENXIO);
|
|
|
|
else
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ukbd_attach(device_t dev)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = device_get_softc(dev);
|
|
|
|
struct usb2_attach_arg *uaa = device_get_ivars(dev);
|
|
|
|
int32_t unit = device_get_unit(dev);
|
|
|
|
keyboard_t *kbd = &sc->sc_kbd;
|
|
|
|
usb2_error_t err;
|
|
|
|
uint16_t n;
|
|
|
|
|
|
|
|
if (sc == NULL) {
|
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
|
|
|
|
|
|
|
|
kbd->kb_data = (void *)sc;
|
|
|
|
|
|
|
|
device_set_usb2_desc(dev);
|
|
|
|
|
|
|
|
sc->sc_udev = uaa->device;
|
|
|
|
sc->sc_iface = uaa->iface;
|
|
|
|
sc->sc_iface_index = uaa->info.bIfaceIndex;
|
|
|
|
sc->sc_iface_no = uaa->info.bIfaceNum;
|
|
|
|
sc->sc_mode = K_XLATE;
|
|
|
|
sc->sc_iface = uaa->iface;
|
|
|
|
|
|
|
|
usb2_callout_init_mtx(&sc->sc_callout, &Giant,
|
|
|
|
CALLOUT_RETURNUNLOCKED);
|
|
|
|
|
|
|
|
err = usb2_transfer_setup(uaa->device,
|
|
|
|
&uaa->info.bIfaceIndex, sc->sc_xfer, ukbd_config,
|
|
|
|
UKBD_N_TRANSFER, sc, &Giant);
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
DPRINTF("error=%s\n", usb2_errstr(err));
|
|
|
|
goto detach;
|
|
|
|
}
|
|
|
|
/* setup default keyboard maps */
|
|
|
|
|
|
|
|
sc->sc_keymap = key_map;
|
|
|
|
sc->sc_accmap = accent_map;
|
|
|
|
for (n = 0; n < UKBD_NFKEY; n++) {
|
|
|
|
sc->sc_fkeymap[n] = fkey_tab[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
|
|
|
|
sc->sc_fkeymap, UKBD_NFKEY);
|
|
|
|
|
|
|
|
KBD_FOUND_DEVICE(kbd);
|
|
|
|
|
|
|
|
ukbd_clear_state(kbd);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FIXME: set the initial value for lock keys in "sc_state"
|
|
|
|
* according to the BIOS data?
|
|
|
|
*/
|
|
|
|
KBD_PROBE_DONE(kbd);
|
|
|
|
|
|
|
|
/* ignore if SETIDLE fails, hence it is not crucial */
|
|
|
|
err = usb2_req_set_idle(sc->sc_udev, &Giant, sc->sc_iface_index, 0, 0);
|
|
|
|
|
|
|
|
ukbd_ioctl(kbd, KDSETLED, (caddr_t)&sc->sc_state);
|
|
|
|
|
|
|
|
KBD_INIT_DONE(kbd);
|
|
|
|
|
|
|
|
if (kbd_register(kbd) < 0) {
|
|
|
|
goto detach;
|
|
|
|
}
|
|
|
|
KBD_CONFIG_DONE(kbd);
|
|
|
|
|
|
|
|
ukbd_enable(kbd);
|
|
|
|
|
|
|
|
#ifdef KBD_INSTALL_CDEV
|
|
|
|
if (kbd_attach(kbd)) {
|
|
|
|
goto detach;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
sc->sc_flags |= UKBD_FLAG_ATTACHED;
|
|
|
|
|
|
|
|
if (bootverbose) {
|
|
|
|
genkbd_diag(kbd, bootverbose);
|
|
|
|
}
|
|
|
|
/* lock keyboard mutex */
|
|
|
|
|
|
|
|
mtx_lock(&Giant);
|
|
|
|
|
|
|
|
/* start the keyboard */
|
|
|
|
|
|
|
|
usb2_transfer_start(sc->sc_xfer[0]);
|
|
|
|
|
|
|
|
/* start the timer */
|
|
|
|
|
|
|
|
ukbd_timeout(sc); /* will unlock mutex */
|
|
|
|
|
|
|
|
return (0); /* success */
|
|
|
|
|
|
|
|
detach:
|
|
|
|
ukbd_detach(dev);
|
|
|
|
return (ENXIO); /* error */
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ukbd_detach(device_t dev)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = device_get_softc(dev);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
DPRINTF("\n");
|
|
|
|
|
|
|
|
if (sc->sc_flags & UKBD_FLAG_POLLING) {
|
|
|
|
panic("cannot detach polled keyboard!\n");
|
|
|
|
}
|
|
|
|
sc->sc_flags |= UKBD_FLAG_GONE;
|
|
|
|
|
|
|
|
usb2_callout_stop(&sc->sc_callout);
|
|
|
|
|
|
|
|
ukbd_disable(&sc->sc_kbd);
|
|
|
|
|
|
|
|
#ifdef KBD_INSTALL_CDEV
|
|
|
|
if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
|
|
|
|
error = kbd_detach(&sc->sc_kbd);
|
|
|
|
if (error) {
|
|
|
|
/* usb attach cannot return an error */
|
|
|
|
device_printf(dev, "WARNING: kbd_detach() "
|
|
|
|
"returned non-zero! (ignored)\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (KBD_IS_CONFIGURED(&sc->sc_kbd)) {
|
|
|
|
error = kbd_unregister(&sc->sc_kbd);
|
|
|
|
if (error) {
|
|
|
|
/* usb attach cannot return an error */
|
|
|
|
device_printf(dev, "WARNING: kbd_unregister() "
|
|
|
|
"returned non-zero! (ignored)\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->sc_kbd.kb_flags = 0;
|
|
|
|
|
|
|
|
usb2_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER);
|
|
|
|
|
|
|
|
usb2_callout_drain(&sc->sc_callout);
|
|
|
|
|
|
|
|
DPRINTF("%s: disconnected\n",
|
|
|
|
device_get_nameunit(dev));
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ukbd_resume(device_t dev)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = device_get_softc(dev);
|
|
|
|
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
ukbd_clear_state(&sc->sc_kbd);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* early keyboard probe, not supported */
|
|
|
|
static int
|
|
|
|
ukbd_configure(int flags)
|
|
|
|
{
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* detect a keyboard, not used */
|
|
|
|
static int
|
|
|
|
ukbd__probe(int unit, void *arg, int flags)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reset and initialize the device, not used */
|
|
|
|
static int
|
|
|
|
ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* test the interface to the device, not used */
|
|
|
|
static int
|
|
|
|
ukbd_test_if(keyboard_t *kbd)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* finish using this keyboard, not used */
|
|
|
|
static int
|
|
|
|
ukbd_term(keyboard_t *kbd)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* keyboard interrupt routine, not used */
|
|
|
|
static int
|
|
|
|
ukbd_intr(keyboard_t *kbd, void *arg)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* lock the access to the keyboard, not used */
|
|
|
|
static int
|
|
|
|
ukbd_lock(keyboard_t *kbd, int lock)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable the access to the device; until this function is called,
|
|
|
|
* the client cannot read from the keyboard.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ukbd_enable(keyboard_t *kbd)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
KBD_ACTIVATE(kbd);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* disallow the access to the device */
|
|
|
|
static int
|
|
|
|
ukbd_disable(keyboard_t *kbd)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
KBD_DEACTIVATE(kbd);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if data is waiting */
|
|
|
|
static int
|
|
|
|
ukbd_check(keyboard_t *kbd)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = kbd->kb_data;
|
|
|
|
|
|
|
|
if (!mtx_owned(&Giant)) {
|
|
|
|
return (0); /* XXX */
|
|
|
|
}
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
if (!KBD_IS_ACTIVE(kbd)) {
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
#ifdef UKBD_EMULATE_ATSCANCODE
|
|
|
|
if (sc->sc_buffered_char[0]) {
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (sc->sc_inputs > 0) {
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if char is waiting */
|
|
|
|
static int
|
|
|
|
ukbd_check_char(keyboard_t *kbd)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = kbd->kb_data;
|
|
|
|
|
|
|
|
if (!mtx_owned(&Giant)) {
|
|
|
|
return (0); /* XXX */
|
|
|
|
}
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
if (!KBD_IS_ACTIVE(kbd)) {
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
if ((sc->sc_composed_char > 0) &&
|
|
|
|
(!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
return (ukbd_check(kbd));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* read one byte from the keyboard if it's allowed */
|
|
|
|
static int
|
|
|
|
ukbd_read(keyboard_t *kbd, int wait)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = kbd->kb_data;
|
|
|
|
int32_t usbcode;
|
|
|
|
|
|
|
|
#ifdef UKBD_EMULATE_ATSCANCODE
|
|
|
|
uint32_t keycode;
|
|
|
|
uint32_t scancode;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!mtx_owned(&Giant)) {
|
|
|
|
return -1; /* XXX */
|
|
|
|
}
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
#ifdef UKBD_EMULATE_ATSCANCODE
|
|
|
|
if (sc->sc_buffered_char[0]) {
|
|
|
|
scancode = sc->sc_buffered_char[0];
|
|
|
|
if (scancode & SCAN_PREFIX) {
|
|
|
|
sc->sc_buffered_char[0] &= ~SCAN_PREFIX;
|
|
|
|
return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
|
|
|
|
}
|
|
|
|
sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
|
|
|
|
sc->sc_buffered_char[1] = 0;
|
|
|
|
return (scancode);
|
|
|
|
}
|
|
|
|
#endif /* UKBD_EMULATE_ATSCANCODE */
|
|
|
|
|
|
|
|
/* XXX */
|
|
|
|
usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
|
|
|
|
if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
++(kbd->kb_count);
|
|
|
|
|
|
|
|
#ifdef UKBD_EMULATE_ATSCANCODE
|
|
|
|
keycode = ukbd_trtab[KEY_INDEX(usbcode)];
|
|
|
|
if (keycode == NN) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers,
|
|
|
|
(usbcode & KEY_RELEASE)));
|
|
|
|
#else /* !UKBD_EMULATE_ATSCANCODE */
|
|
|
|
return (usbcode);
|
|
|
|
#endif /* UKBD_EMULATE_ATSCANCODE */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read char from the keyboard */
|
|
|
|
static uint32_t
|
|
|
|
ukbd_read_char(keyboard_t *kbd, int wait)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = kbd->kb_data;
|
|
|
|
uint32_t action;
|
|
|
|
uint32_t keycode;
|
|
|
|
int32_t usbcode;
|
|
|
|
|
|
|
|
#ifdef UKBD_EMULATE_ATSCANCODE
|
|
|
|
uint32_t scancode;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
if (!mtx_owned(&Giant)) {
|
|
|
|
return (NOKEY); /* XXX */
|
|
|
|
}
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
next_code:
|
|
|
|
|
|
|
|
/* do we have a composed char to return ? */
|
|
|
|
|
|
|
|
if ((sc->sc_composed_char > 0) &&
|
|
|
|
(!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
|
|
|
|
|
|
|
|
action = sc->sc_composed_char;
|
|
|
|
sc->sc_composed_char = 0;
|
|
|
|
|
|
|
|
if (action > 0xFF) {
|
|
|
|
goto errkey;
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
#ifdef UKBD_EMULATE_ATSCANCODE
|
|
|
|
|
|
|
|
/* do we have a pending raw scan code? */
|
|
|
|
|
|
|
|
if (sc->sc_mode == K_RAW) {
|
|
|
|
scancode = sc->sc_buffered_char[0];
|
|
|
|
if (scancode) {
|
|
|
|
if (scancode & SCAN_PREFIX) {
|
|
|
|
sc->sc_buffered_char[0] = (scancode & ~SCAN_PREFIX);
|
|
|
|
return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
|
|
|
|
}
|
|
|
|
sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
|
|
|
|
sc->sc_buffered_char[1] = 0;
|
|
|
|
return (scancode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* UKBD_EMULATE_ATSCANCODE */
|
|
|
|
|
|
|
|
/* see if there is something in the keyboard port */
|
|
|
|
/* XXX */
|
|
|
|
usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
|
|
|
|
if (usbcode == -1) {
|
|
|
|
return (NOKEY);
|
|
|
|
}
|
|
|
|
++kbd->kb_count;
|
|
|
|
|
|
|
|
#ifdef UKBD_EMULATE_ATSCANCODE
|
|
|
|
/* USB key index -> key code -> AT scan code */
|
|
|
|
keycode = ukbd_trtab[KEY_INDEX(usbcode)];
|
|
|
|
if (keycode == NN) {
|
|
|
|
return (NOKEY);
|
|
|
|
}
|
|
|
|
/* return an AT scan code for the K_RAW mode */
|
|
|
|
if (sc->sc_mode == K_RAW) {
|
|
|
|
return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers,
|
|
|
|
(usbcode & KEY_RELEASE)));
|
|
|
|
}
|
|
|
|
#else /* !UKBD_EMULATE_ATSCANCODE */
|
|
|
|
|
|
|
|
/* return the byte as is for the K_RAW mode */
|
|
|
|
if (sc->sc_mode == K_RAW) {
|
|
|
|
return (usbcode);
|
|
|
|
}
|
|
|
|
/* USB key index -> key code */
|
|
|
|
keycode = ukbd_trtab[KEY_INDEX(usbcode)];
|
|
|
|
if (keycode == NN) {
|
|
|
|
return (NOKEY);
|
|
|
|
}
|
|
|
|
#endif /* UKBD_EMULATE_ATSCANCODE */
|
|
|
|
|
|
|
|
switch (keycode) {
|
|
|
|
case 0x38: /* left alt (compose key) */
|
|
|
|
if (usbcode & KEY_RELEASE) {
|
|
|
|
if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
|
|
|
|
sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
|
|
|
|
|
|
|
|
if (sc->sc_composed_char > 0xFF) {
|
|
|
|
sc->sc_composed_char = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!(sc->sc_flags & UKBD_FLAG_COMPOSE)) {
|
|
|
|
sc->sc_flags |= UKBD_FLAG_COMPOSE;
|
|
|
|
sc->sc_composed_char = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/* XXX: I don't like these... */
|
|
|
|
case 0x5c: /* print screen */
|
|
|
|
if (sc->sc_flags & ALTS) {
|
|
|
|
keycode = 0x54; /* sysrq */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x68: /* pause/break */
|
|
|
|
if (sc->sc_flags & CTLS) {
|
|
|
|
keycode = 0x6c; /* break */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return the key code in the K_CODE mode */
|
|
|
|
if (usbcode & KEY_RELEASE) {
|
|
|
|
keycode |= SCAN_RELEASE;
|
|
|
|
}
|
|
|
|
if (sc->sc_mode == K_CODE) {
|
|
|
|
return (keycode);
|
|
|
|
}
|
|
|
|
/* compose a character code */
|
|
|
|
if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
|
|
|
|
switch (keycode) {
|
|
|
|
/* key pressed, process it */
|
|
|
|
case 0x47:
|
|
|
|
case 0x48:
|
|
|
|
case 0x49: /* keypad 7,8,9 */
|
|
|
|
sc->sc_composed_char *= 10;
|
|
|
|
sc->sc_composed_char += keycode - 0x40;
|
|
|
|
goto check_composed;
|
|
|
|
|
|
|
|
case 0x4B:
|
|
|
|
case 0x4C:
|
|
|
|
case 0x4D: /* keypad 4,5,6 */
|
|
|
|
sc->sc_composed_char *= 10;
|
|
|
|
sc->sc_composed_char += keycode - 0x47;
|
|
|
|
goto check_composed;
|
|
|
|
|
|
|
|
case 0x4F:
|
|
|
|
case 0x50:
|
|
|
|
case 0x51: /* keypad 1,2,3 */
|
|
|
|
sc->sc_composed_char *= 10;
|
|
|
|
sc->sc_composed_char += keycode - 0x4E;
|
|
|
|
goto check_composed;
|
|
|
|
|
|
|
|
case 0x52: /* keypad 0 */
|
|
|
|
sc->sc_composed_char *= 10;
|
|
|
|
goto check_composed;
|
|
|
|
|
|
|
|
/* key released, no interest here */
|
|
|
|
case SCAN_RELEASE | 0x47:
|
|
|
|
case SCAN_RELEASE | 0x48:
|
|
|
|
case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */
|
|
|
|
case SCAN_RELEASE | 0x4B:
|
|
|
|
case SCAN_RELEASE | 0x4C:
|
|
|
|
case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */
|
|
|
|
case SCAN_RELEASE | 0x4F:
|
|
|
|
case SCAN_RELEASE | 0x50:
|
|
|
|
case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */
|
|
|
|
case SCAN_RELEASE | 0x52: /* keypad 0 */
|
|
|
|
goto next_code;
|
|
|
|
|
|
|
|
case 0x38: /* left alt key */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (sc->sc_composed_char > 0) {
|
|
|
|
sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
|
|
|
|
sc->sc_composed_char = 0;
|
|
|
|
goto errkey;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* keycode to key action */
|
|
|
|
action = genkbd_keyaction(kbd, SCAN_CHAR(keycode),
|
|
|
|
(keycode & SCAN_RELEASE),
|
|
|
|
&sc->sc_state, &sc->sc_accents);
|
|
|
|
if (action == NOKEY) {
|
|
|
|
goto next_code;
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
return (action);
|
|
|
|
|
|
|
|
check_composed:
|
|
|
|
if (sc->sc_composed_char <= 0xFF) {
|
|
|
|
goto next_code;
|
|
|
|
}
|
|
|
|
errkey:
|
|
|
|
return (ERRKEY);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* some useful control functions */
|
|
|
|
static int
|
|
|
|
ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
|
|
|
|
{
|
|
|
|
/* translate LED_XXX bits into the device specific bits */
|
|
|
|
static const uint8_t ledmap[8] = {
|
|
|
|
0, 2, 1, 3, 4, 6, 5, 7,
|
|
|
|
};
|
|
|
|
struct ukbd_softc *sc = kbd->kb_data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
|
|
|
|
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
|
|
|
|
int ival;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
if (!mtx_owned(&Giant)) {
|
|
|
|
/*
|
|
|
|
* XXX big problem: If scroll lock is pressed and "printf()"
|
|
|
|
* is called, the CPU will get here, to un-scroll lock the
|
|
|
|
* keyboard. But if "printf()" acquires the "Giant" lock,
|
|
|
|
* there will be a locking order reversal problem, so the
|
|
|
|
* keyboard system must get out of "Giant" first, before the
|
|
|
|
* CPU can proceed here ...
|
|
|
|
*/
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case KDGKBMODE: /* get keyboard mode */
|
|
|
|
*(int *)arg = sc->sc_mode;
|
|
|
|
break;
|
|
|
|
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
|
|
|
|
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
|
|
|
|
case _IO('K', 7):
|
|
|
|
ival = IOCPARM_IVAL(arg);
|
|
|
|
arg = (caddr_t)&ival;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
#endif
|
|
|
|
case KDSKBMODE: /* set keyboard mode */
|
|
|
|
switch (*(int *)arg) {
|
|
|
|
case K_XLATE:
|
|
|
|
if (sc->sc_mode != K_XLATE) {
|
|
|
|
/* make lock key state and LED state match */
|
|
|
|
sc->sc_state &= ~LOCK_MASK;
|
|
|
|
sc->sc_state |= KBD_LED_VAL(kbd);
|
|
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case K_RAW:
|
|
|
|
case K_CODE:
|
|
|
|
if (sc->sc_mode != *(int *)arg) {
|
|
|
|
ukbd_clear_state(kbd);
|
|
|
|
sc->sc_mode = *(int *)arg;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KDGETLED: /* get keyboard LED */
|
|
|
|
*(int *)arg = KBD_LED_VAL(kbd);
|
|
|
|
break;
|
|
|
|
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
|
|
|
|
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
|
|
|
|
case _IO('K', 66):
|
|
|
|
ival = IOCPARM_IVAL(arg);
|
|
|
|
arg = (caddr_t)&ival;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
#endif
|
|
|
|
case KDSETLED: /* set keyboard LED */
|
|
|
|
/* NOTE: lock key state in "sc_state" won't be changed */
|
|
|
|
if (*(int *)arg & ~LOCK_MASK) {
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
i = *(int *)arg;
|
|
|
|
/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
|
|
|
|
if (sc->sc_mode == K_XLATE &&
|
|
|
|
kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
|
|
|
|
if (i & ALKED)
|
|
|
|
i |= CLKED;
|
|
|
|
else
|
|
|
|
i &= ~CLKED;
|
|
|
|
}
|
|
|
|
if (KBD_HAS_DEVICE(kbd)) {
|
|
|
|
ukbd_set_leds(sc, ledmap[i & LED_MASK]);
|
|
|
|
}
|
|
|
|
KBD_LED_VAL(kbd) = *(int *)arg;
|
|
|
|
break;
|
|
|
|
case KDGKBSTATE: /* get lock key state */
|
|
|
|
*(int *)arg = sc->sc_state & LOCK_MASK;
|
|
|
|
break;
|
|
|
|
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
|
|
|
|
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
|
|
|
|
case _IO('K', 20):
|
|
|
|
ival = IOCPARM_IVAL(arg);
|
|
|
|
arg = (caddr_t)&ival;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
#endif
|
|
|
|
case KDSKBSTATE: /* set lock key state */
|
|
|
|
if (*(int *)arg & ~LOCK_MASK) {
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
sc->sc_state &= ~LOCK_MASK;
|
|
|
|
sc->sc_state |= *(int *)arg;
|
|
|
|
|
|
|
|
/* set LEDs and quit */
|
|
|
|
return (ukbd_ioctl(kbd, KDSETLED, arg));
|
|
|
|
|
|
|
|
case KDSETREPEAT: /* set keyboard repeat rate (new
|
|
|
|
* interface) */
|
|
|
|
if (!KBD_HAS_DEVICE(kbd)) {
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
if (((int *)arg)[1] < 0) {
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
if (((int *)arg)[0] < 0) {
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
if (((int *)arg)[0] < 200) /* fastest possible value */
|
|
|
|
kbd->kb_delay1 = 200;
|
|
|
|
else
|
|
|
|
kbd->kb_delay1 = ((int *)arg)[0];
|
|
|
|
kbd->kb_delay2 = ((int *)arg)[1];
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
|
|
|
|
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
|
|
|
|
case _IO('K', 67):
|
|
|
|
ival = IOCPARM_IVAL(arg);
|
|
|
|
arg = (caddr_t)&ival;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
#endif
|
|
|
|
case KDSETRAD: /* set keyboard repeat rate (old
|
|
|
|
* interface) */
|
|
|
|
return (ukbd_set_typematic(kbd, *(int *)arg));
|
|
|
|
|
|
|
|
case PIO_KEYMAP: /* set keyboard translation table */
|
|
|
|
case PIO_KEYMAPENT: /* set keyboard translation table
|
|
|
|
* entry */
|
|
|
|
case PIO_DEADKEYMAP: /* set accent key translation table */
|
|
|
|
sc->sc_accents = 0;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
default:
|
|
|
|
return (genkbd_commonioctl(kbd, cmd, arg));
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clear the internal state of the keyboard */
|
|
|
|
static void
|
|
|
|
ukbd_clear_state(keyboard_t *kbd)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = kbd->kb_data;
|
|
|
|
|
|
|
|
if (!mtx_owned(&Giant)) {
|
|
|
|
return; /* XXX */
|
|
|
|
}
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
sc->sc_flags &= ~(UKBD_FLAG_COMPOSE | UKBD_FLAG_POLLING);
|
|
|
|
sc->sc_state &= LOCK_MASK; /* preserve locking key state */
|
|
|
|
sc->sc_accents = 0;
|
|
|
|
sc->sc_composed_char = 0;
|
|
|
|
#ifdef UKBD_EMULATE_ATSCANCODE
|
|
|
|
sc->sc_buffered_char[0] = 0;
|
|
|
|
sc->sc_buffered_char[1] = 0;
|
|
|
|
#endif
|
|
|
|
bzero(&sc->sc_ndata, sizeof(sc->sc_ndata));
|
|
|
|
bzero(&sc->sc_odata, sizeof(sc->sc_odata));
|
|
|
|
bzero(&sc->sc_ntime, sizeof(sc->sc_ntime));
|
|
|
|
bzero(&sc->sc_otime, sizeof(sc->sc_otime));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* save the internal state, not used */
|
|
|
|
static int
|
|
|
|
ukbd_get_state(keyboard_t *kbd, void *buf, size_t len)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
return (len == 0) ? 1 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the internal state, not used */
|
|
|
|
static int
|
|
|
|
ukbd_set_state(keyboard_t *kbd, void *buf, size_t len)
|
|
|
|
{
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ukbd_poll(keyboard_t *kbd, int on)
|
|
|
|
{
|
|
|
|
struct ukbd_softc *sc = kbd->kb_data;
|
|
|
|
|
|
|
|
if (!mtx_owned(&Giant)) {
|
|
|
|
return (0); /* XXX */
|
|
|
|
}
|
|
|
|
mtx_assert(&Giant, MA_OWNED);
|
|
|
|
|
|
|
|
if (on) {
|
|
|
|
sc->sc_flags |= UKBD_FLAG_POLLING;
|
|
|
|
} else {
|
|
|
|
sc->sc_flags &= ~UKBD_FLAG_POLLING;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* local functions */
|
|
|
|
|
|
|
|
static void
|
|
|
|
ukbd_set_leds(struct ukbd_softc *sc, uint8_t leds)
|
|
|
|
{
|
|
|
|
DPRINTF("leds=0x%02x\n", leds);
|
|
|
|
|
|
|
|
sc->sc_leds = leds;
|
|
|
|
sc->sc_flags |= UKBD_FLAG_SET_LEDS;
|
|
|
|
|
|
|
|
/* start transfer, if not already started */
|
|
|
|
|
|
|
|
usb2_transfer_start(sc->sc_xfer[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ukbd_set_typematic(keyboard_t *kbd, int code)
|
|
|
|
{
|
|
|
|
static const int delays[] = {250, 500, 750, 1000};
|
|
|
|
static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
|
|
|
|
68, 76, 84, 92, 100, 110, 118, 126,
|
|
|
|
136, 152, 168, 184, 200, 220, 236, 252,
|
|
|
|
272, 304, 336, 368, 400, 440, 472, 504};
|
|
|
|
|
|
|
|
if (code & ~0x7f) {
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
kbd->kb_delay1 = delays[(code >> 5) & 3];
|
|
|
|
kbd->kb_delay2 = rates[code & 0x1f];
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UKBD_EMULATE_ATSCANCODE
|
|
|
|
static int
|
|
|
|
ukbd_key2scan(struct ukbd_softc *sc, int code, int shift, int up)
|
|
|
|
{
|
|
|
|
static const int scan[] = {
|
|
|
|
0x1c, 0x1d, 0x35,
|
|
|
|
0x37 | SCAN_PREFIX_SHIFT, /* PrintScreen */
|
|
|
|
0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f,
|
|
|
|
0x50, 0x51, 0x52, 0x53,
|
|
|
|
0x46, /* XXX Pause/Break */
|
|
|
|
0x5b, 0x5c, 0x5d,
|
|
|
|
/* SUN TYPE 6 USB KEYBOARD */
|
|
|
|
0x68, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
|
|
|
|
0x64, 0x65, 0x66, 0x67, 0x25, 0x1f, 0x1e,
|
|
|
|
0x20,
|
|
|
|
};
|
|
|
|
|
|
|
|
if ((code >= 89) && (code < (89 + (sizeof(scan) / sizeof(scan[0]))))) {
|
|
|
|
code = scan[code - 89] | SCAN_PREFIX_E0;
|
|
|
|
}
|
|
|
|
/* Pause/Break */
|
|
|
|
if ((code == 104) && (!(shift & (MOD_CONTROL_L | MOD_CONTROL_R)))) {
|
|
|
|
code = (0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL);
|
|
|
|
}
|
|
|
|
if (shift & (MOD_SHIFT_L | MOD_SHIFT_R)) {
|
|
|
|
code &= ~SCAN_PREFIX_SHIFT;
|
|
|
|
}
|
|
|
|
code |= (up ? SCAN_RELEASE : SCAN_PRESS);
|
|
|
|
|
|
|
|
if (code & SCAN_PREFIX) {
|
|
|
|
if (code & SCAN_PREFIX_CTL) {
|
|
|
|
/* Ctrl */
|
|
|
|
sc->sc_buffered_char[0] = (0x1d | (code & SCAN_RELEASE));
|
|
|
|
sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX);
|
|
|
|
} else if (code & SCAN_PREFIX_SHIFT) {
|
|
|
|
/* Shift */
|
|
|
|
sc->sc_buffered_char[0] = (0x2a | (code & SCAN_RELEASE));
|
|
|
|
sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX_SHIFT);
|
|
|
|
} else {
|
|
|
|
sc->sc_buffered_char[0] = (code & ~SCAN_PREFIX);
|
|
|
|
sc->sc_buffered_char[1] = 0;
|
|
|
|
}
|
|
|
|
return ((code & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
|
|
|
|
}
|
|
|
|
return (code);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* UKBD_EMULATE_ATSCANCODE */
|
|
|
|
|
|
|
|
keyboard_switch_t ukbdsw = {
|
|
|
|
.probe = &ukbd__probe,
|
|
|
|
.init = &ukbd_init,
|
|
|
|
.term = &ukbd_term,
|
|
|
|
.intr = &ukbd_intr,
|
|
|
|
.test_if = &ukbd_test_if,
|
|
|
|
.enable = &ukbd_enable,
|
|
|
|
.disable = &ukbd_disable,
|
|
|
|
.read = &ukbd_read,
|
|
|
|
.check = &ukbd_check,
|
|
|
|
.read_char = &ukbd_read_char,
|
|
|
|
.check_char = &ukbd_check_char,
|
|
|
|
.ioctl = &ukbd_ioctl,
|
|
|
|
.lock = &ukbd_lock,
|
|
|
|
.clear_state = &ukbd_clear_state,
|
|
|
|
.get_state = &ukbd_get_state,
|
|
|
|
.set_state = &ukbd_set_state,
|
|
|
|
.get_fkeystr = &genkbd_get_fkeystr,
|
|
|
|
.poll = &ukbd_poll,
|
|
|
|
.diag = &genkbd_diag,
|
|
|
|
};
|
|
|
|
|
|
|
|
KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure);
|
|
|
|
|
|
|
|
static int
|
|
|
|
ukbd_driver_load(module_t mod, int what, void *arg)
|
|
|
|
{
|
|
|
|
switch (what) {
|
|
|
|
case MOD_LOAD:
|
|
|
|
kbd_add_driver(&ukbd_kbd_driver);
|
|
|
|
break;
|
|
|
|
case MOD_UNLOAD:
|
|
|
|
kbd_delete_driver(&ukbd_kbd_driver);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static devclass_t ukbd_devclass;
|
|
|
|
|
|
|
|
static device_method_t ukbd_methods[] = {
|
|
|
|
DEVMETHOD(device_probe, ukbd_probe),
|
|
|
|
DEVMETHOD(device_attach, ukbd_attach),
|
|
|
|
DEVMETHOD(device_detach, ukbd_detach),
|
|
|
|
DEVMETHOD(device_resume, ukbd_resume),
|
|
|
|
{0, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
static driver_t ukbd_driver = {
|
|
|
|
.name = "ukbd",
|
|
|
|
.methods = ukbd_methods,
|
|
|
|
.size = sizeof(struct ukbd_softc),
|
|
|
|
};
|
|
|
|
|
|
|
|
DRIVER_MODULE(ukbd, ushub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0);
|
|
|
|
MODULE_DEPEND(ukbd, usb2_input, 1, 1, 1);
|
|
|
|
MODULE_DEPEND(ukbd, usb2_core, 1, 1, 1);
|