102ac6454SAndrew Thompson #include <sys/cdefs.h>
202ac6454SAndrew Thompson /*-
3b61a5730SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
4718cf2ccSPedro F. Giffuni *
502ac6454SAndrew Thompson * Copyright (c) 1998 The NetBSD Foundation, Inc.
602ac6454SAndrew Thompson * All rights reserved.
702ac6454SAndrew Thompson *
802ac6454SAndrew Thompson * This code is derived from software contributed to The NetBSD Foundation
902ac6454SAndrew Thompson * by Lennart Augustsson (lennart@augustsson.net) at
1002ac6454SAndrew Thompson * Carlstedt Research & Technology.
1102ac6454SAndrew Thompson *
1202ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without
1302ac6454SAndrew Thompson * modification, are permitted provided that the following conditions
1402ac6454SAndrew Thompson * are met:
1502ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright
1602ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer.
1702ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright
1802ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the
1902ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution.
2002ac6454SAndrew Thompson *
2102ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2202ac6454SAndrew Thompson * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2302ac6454SAndrew Thompson * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2402ac6454SAndrew Thompson * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2502ac6454SAndrew Thompson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2602ac6454SAndrew Thompson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2702ac6454SAndrew Thompson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2802ac6454SAndrew Thompson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2902ac6454SAndrew Thompson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3002ac6454SAndrew Thompson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3102ac6454SAndrew Thompson * POSSIBILITY OF SUCH DAMAGE.
3202ac6454SAndrew Thompson *
3302ac6454SAndrew Thompson */
3402ac6454SAndrew Thompson
3502ac6454SAndrew Thompson /*
3602ac6454SAndrew Thompson * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
3702ac6454SAndrew Thompson */
3802ac6454SAndrew Thompson
3902ac6454SAndrew Thompson #include "opt_kbd.h"
4002ac6454SAndrew Thompson #include "opt_ukbd.h"
4110063b79SOleksandr Tymoshenko #include "opt_evdev.h"
4202ac6454SAndrew Thompson
43ed6d949aSAndrew Thompson #include <sys/stdint.h>
44ed6d949aSAndrew Thompson #include <sys/stddef.h>
45ed6d949aSAndrew Thompson #include <sys/param.h>
46ed6d949aSAndrew Thompson #include <sys/queue.h>
47ed6d949aSAndrew Thompson #include <sys/types.h>
48ed6d949aSAndrew Thompson #include <sys/systm.h>
49ed6d949aSAndrew Thompson #include <sys/kernel.h>
50ed6d949aSAndrew Thompson #include <sys/bus.h>
51ed6d949aSAndrew Thompson #include <sys/module.h>
52ed6d949aSAndrew Thompson #include <sys/lock.h>
53ed6d949aSAndrew Thompson #include <sys/mutex.h>
54ed6d949aSAndrew Thompson #include <sys/condvar.h>
55ed6d949aSAndrew Thompson #include <sys/sysctl.h>
56ed6d949aSAndrew Thompson #include <sys/sx.h>
57ed6d949aSAndrew Thompson #include <sys/unistd.h>
58ed6d949aSAndrew Thompson #include <sys/callout.h>
59ed6d949aSAndrew Thompson #include <sys/malloc.h>
60ed6d949aSAndrew Thompson #include <sys/priv.h>
612b6ce80cSHans Petter Selasky #include <sys/proc.h>
62ed6d949aSAndrew Thompson
63eead9017SVladimir Kondratyev #include <dev/hid/hid.h>
64eead9017SVladimir Kondratyev
6502ac6454SAndrew Thompson #include <dev/usb/usb.h>
66ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
67ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
6802ac6454SAndrew Thompson #include <dev/usb/usbhid.h>
6902ac6454SAndrew Thompson
7002ac6454SAndrew Thompson #define USB_DEBUG_VAR ukbd_debug
7102ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
7202ac6454SAndrew Thompson
7302ac6454SAndrew Thompson #include <dev/usb/quirk/usb_quirk.h>
7402ac6454SAndrew Thompson
75*541e7a98SFrank Hilgendorf #include "usbdevs.h"
76*541e7a98SFrank Hilgendorf
77a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
7810063b79SOleksandr Tymoshenko #include <dev/evdev/input.h>
7910063b79SOleksandr Tymoshenko #include <dev/evdev/evdev.h>
8010063b79SOleksandr Tymoshenko #endif
8110063b79SOleksandr Tymoshenko
8202ac6454SAndrew Thompson #include <sys/ioccom.h>
8302ac6454SAndrew Thompson #include <sys/filio.h>
8402ac6454SAndrew Thompson #include <sys/kbio.h>
8502ac6454SAndrew Thompson
8602ac6454SAndrew Thompson #include <dev/kbd/kbdreg.h>
8702ac6454SAndrew Thompson
8802ac6454SAndrew Thompson /* the initial key map, accent map and fkey strings */
8902ac6454SAndrew Thompson #if defined(UKBD_DFLT_KEYMAP) && !defined(KLD_MODULE)
9002ac6454SAndrew Thompson #define KBD_DFLT_KEYMAP
9102ac6454SAndrew Thompson #include "ukbdmap.h"
9202ac6454SAndrew Thompson #endif
9302ac6454SAndrew Thompson
9402ac6454SAndrew Thompson /* the following file must be included after "ukbdmap.h" */
9502ac6454SAndrew Thompson #include <dev/kbd/kbdtables.h>
9602ac6454SAndrew Thompson
97b850ecc1SAndrew Thompson #ifdef USB_DEBUG
9802ac6454SAndrew Thompson static int ukbd_debug = 0;
99247549d1SAlfred Perlstein static int ukbd_no_leds = 0;
1000ccd2fb0SHans Petter Selasky static int ukbd_pollrate = 0;
10102ac6454SAndrew Thompson
102f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
103f8d2b1f3SPawel Biernacki "USB keyboard");
104af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, debug, CTLFLAG_RWTUN,
10502ac6454SAndrew Thompson &ukbd_debug, 0, "Debug level");
106af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, no_leds, CTLFLAG_RWTUN,
10783cadd7dSHans Petter Selasky &ukbd_no_leds, 0, "Disables setting of keyboard leds");
108af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, pollrate, CTLFLAG_RWTUN,
1090ccd2fb0SHans Petter Selasky &ukbd_pollrate, 0, "Force this polling rate, 1-1000Hz");
11002ac6454SAndrew Thompson #endif
11102ac6454SAndrew Thompson
11202ac6454SAndrew Thompson #define UKBD_EMULATE_ATSCANCODE 1
11302ac6454SAndrew Thompson #define UKBD_DRIVER_NAME "ukbd"
114d4028678SHans Petter Selasky #define UKBD_NKEYCODE 256 /* units */
115d4028678SHans Petter Selasky #define UKBD_IN_BUF_SIZE (4 * UKBD_NKEYCODE) /* scancodes */
116d4028678SHans Petter Selasky #define UKBD_IN_BUF_FULL ((UKBD_IN_BUF_SIZE / 2) - 1) /* scancodes */
11702ac6454SAndrew Thompson #define UKBD_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
118e92091adSHans Petter Selasky #define UKBD_BUFFER_SIZE 64 /* bytes */
119d4028678SHans Petter Selasky #define UKBD_KEY_PRESSED(map, key) ({ \
120d4028678SHans Petter Selasky CTASSERT((key) >= 0 && (key) < UKBD_NKEYCODE); \
121d4028678SHans Petter Selasky ((map)[(key) / 64] & (1ULL << ((key) % 64))); \
122d4028678SHans Petter Selasky })
123d4028678SHans Petter Selasky
124d4028678SHans Petter Selasky #define MOD_EJECT 0x01
125d4028678SHans Petter Selasky #define MOD_FN 0x02
12602ac6454SAndrew Thompson
12702ac6454SAndrew Thompson struct ukbd_data {
128d4028678SHans Petter Selasky uint64_t bitmap[howmany(UKBD_NKEYCODE, 64)];
129fa64b944SAndrew Thompson };
13002ac6454SAndrew Thompson
13102ac6454SAndrew Thompson enum {
132a6ae9251SHans Petter Selasky UKBD_INTR_DT_0,
133a6ae9251SHans Petter Selasky UKBD_INTR_DT_1,
13402ac6454SAndrew Thompson UKBD_CTRL_LED,
135fa64b944SAndrew Thompson UKBD_N_TRANSFER,
13602ac6454SAndrew Thompson };
13702ac6454SAndrew Thompson
13802ac6454SAndrew Thompson struct ukbd_softc {
13902ac6454SAndrew Thompson keyboard_t sc_kbd;
14002ac6454SAndrew Thompson keymap_t sc_keymap;
14102ac6454SAndrew Thompson accentmap_t sc_accmap;
14202ac6454SAndrew Thompson fkeytab_t sc_fkeymap[UKBD_NFKEY];
143d4028678SHans Petter Selasky uint64_t sc_loc_key_valid[howmany(UKBD_NKEYCODE, 64)];
144fa64b944SAndrew Thompson struct hid_location sc_loc_apple_eject;
145fa64b944SAndrew Thompson struct hid_location sc_loc_apple_fn;
146d4028678SHans Petter Selasky struct hid_location sc_loc_key[UKBD_NKEYCODE];
147e92091adSHans Petter Selasky struct hid_location sc_loc_numlock;
148e92091adSHans Petter Selasky struct hid_location sc_loc_capslock;
149e92091adSHans Petter Selasky struct hid_location sc_loc_scrolllock;
150760bc48eSAndrew Thompson struct usb_callout sc_callout;
15102ac6454SAndrew Thompson struct ukbd_data sc_ndata;
15202ac6454SAndrew Thompson struct ukbd_data sc_odata;
15302ac6454SAndrew Thompson
1541bdb81f1SAndrew Thompson struct thread *sc_poll_thread;
155760bc48eSAndrew Thompson struct usb_device *sc_udev;
156760bc48eSAndrew Thompson struct usb_interface *sc_iface;
157760bc48eSAndrew Thompson struct usb_xfer *sc_xfer[UKBD_N_TRANSFER];
158a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
15910063b79SOleksandr Tymoshenko struct evdev_dev *sc_evdev;
16010063b79SOleksandr Tymoshenko #endif
16102ac6454SAndrew Thompson
1621a58327bSBruce Evans sbintime_t sc_co_basetime;
1631a58327bSBruce Evans int sc_delay;
164d4028678SHans Petter Selasky uint32_t sc_repeat_time;
16502ac6454SAndrew Thompson uint32_t sc_input[UKBD_IN_BUF_SIZE]; /* input buffer */
16602ac6454SAndrew Thompson uint32_t sc_time_ms;
16702ac6454SAndrew Thompson uint32_t sc_composed_char; /* composed char code, if non-zero */
16802ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
16902ac6454SAndrew Thompson uint32_t sc_buffered_char[2];
17002ac6454SAndrew Thompson #endif
17102ac6454SAndrew Thompson uint32_t sc_flags; /* flags */
172e92091adSHans Petter Selasky #define UKBD_FLAG_COMPOSE 0x00000001
173e92091adSHans Petter Selasky #define UKBD_FLAG_POLLING 0x00000002
174e92091adSHans Petter Selasky #define UKBD_FLAG_SET_LEDS 0x00000004
175e92091adSHans Petter Selasky #define UKBD_FLAG_ATTACHED 0x00000010
176e92091adSHans Petter Selasky #define UKBD_FLAG_GONE 0x00000020
177e92091adSHans Petter Selasky
178*541e7a98SFrank Hilgendorf /* set in ukbd_attach */
179*541e7a98SFrank Hilgendorf #define UKBD_FLAG_APPLE_SWAP 0x00000040
180*541e7a98SFrank Hilgendorf /* set in ukbd_parse_hid */
181*541e7a98SFrank Hilgendorf #define UKBD_FLAG_APPLE_EJECT 0x00000080
182*541e7a98SFrank Hilgendorf #define UKBD_FLAG_APPLE_FN 0x00000100
183e92091adSHans Petter Selasky #define UKBD_FLAG_NUMLOCK 0x00080000
184e92091adSHans Petter Selasky #define UKBD_FLAG_CAPSLOCK 0x00100000
185e92091adSHans Petter Selasky #define UKBD_FLAG_SCROLLLOCK 0x00200000
186*541e7a98SFrank Hilgendorf #define UKBD_FLAG_HID_MASK UKBD_FLAG_APPLE_EJECT | \
187*541e7a98SFrank Hilgendorf UKBD_FLAG_APPLE_FN | \
188*541e7a98SFrank Hilgendorf UKBD_FLAG_NUMLOCK | \
189*541e7a98SFrank Hilgendorf UKBD_FLAG_CAPSLOCK | \
190*541e7a98SFrank Hilgendorf UKBD_FLAG_SCROLLLOCK
19102ac6454SAndrew Thompson
1921bdb81f1SAndrew Thompson int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
1931bdb81f1SAndrew Thompson int sc_state; /* shift/lock key state */
1941bdb81f1SAndrew Thompson int sc_accents; /* accent key index (> 0) */
195f87a304cSHans Petter Selasky int sc_polling; /* polling recursion count */
196e92091adSHans Petter Selasky int sc_led_size;
197e92091adSHans Petter Selasky int sc_kbd_size;
19802ac6454SAndrew Thompson
19902ac6454SAndrew Thompson uint16_t sc_inputs;
20002ac6454SAndrew Thompson uint16_t sc_inputhead;
20102ac6454SAndrew Thompson uint16_t sc_inputtail;
20202ac6454SAndrew Thompson
20302ac6454SAndrew Thompson uint8_t sc_leds; /* store for async led requests */
20402ac6454SAndrew Thompson uint8_t sc_iface_index;
20502ac6454SAndrew Thompson uint8_t sc_iface_no;
206e92091adSHans Petter Selasky uint8_t sc_id_apple_eject;
207e92091adSHans Petter Selasky uint8_t sc_id_apple_fn;
208d4028678SHans Petter Selasky uint8_t sc_id_loc_key[UKBD_NKEYCODE];
209e92091adSHans Petter Selasky uint8_t sc_id_numlock;
210e92091adSHans Petter Selasky uint8_t sc_id_capslock;
211e92091adSHans Petter Selasky uint8_t sc_id_scrolllock;
212fa64b944SAndrew Thompson uint8_t sc_kbd_id;
213d4028678SHans Petter Selasky uint8_t sc_repeat_key;
214e92091adSHans Petter Selasky
215e92091adSHans Petter Selasky uint8_t sc_buffer[UKBD_BUFFER_SIZE];
21602ac6454SAndrew Thompson };
21702ac6454SAndrew Thompson
218d4028678SHans Petter Selasky #define KEY_NONE 0x00
21902ac6454SAndrew Thompson #define KEY_ERROR 0x01
22002ac6454SAndrew Thompson
22102ac6454SAndrew Thompson #define KEY_PRESS 0
22202ac6454SAndrew Thompson #define KEY_RELEASE 0x400
22302ac6454SAndrew Thompson #define KEY_INDEX(c) ((c) & 0xFF)
22402ac6454SAndrew Thompson
22502ac6454SAndrew Thompson #define SCAN_PRESS 0
22602ac6454SAndrew Thompson #define SCAN_RELEASE 0x80
22702ac6454SAndrew Thompson #define SCAN_PREFIX_E0 0x100
22802ac6454SAndrew Thompson #define SCAN_PREFIX_E1 0x200
22902ac6454SAndrew Thompson #define SCAN_PREFIX_CTL 0x400
23002ac6454SAndrew Thompson #define SCAN_PREFIX_SHIFT 0x800
23102ac6454SAndrew Thompson #define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | \
23202ac6454SAndrew Thompson SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT)
23302ac6454SAndrew Thompson #define SCAN_CHAR(c) ((c) & 0x7f)
23402ac6454SAndrew Thompson
2350eb8d462SHans Petter Selasky #define UKBD_LOCK() USB_MTX_LOCK(&Giant)
2360eb8d462SHans Petter Selasky #define UKBD_UNLOCK() USB_MTX_UNLOCK(&Giant)
2370eb8d462SHans Petter Selasky #define UKBD_LOCK_ASSERT() USB_MTX_ASSERT(&Giant, MA_OWNED)
238bc40a969SAndriy Gapon
23902ac6454SAndrew Thompson #define NN 0 /* no translation */
24002ac6454SAndrew Thompson /*
24102ac6454SAndrew Thompson * Translate USB keycodes to AT keyboard scancodes.
24202ac6454SAndrew Thompson */
24302ac6454SAndrew Thompson /*
24402ac6454SAndrew Thompson * FIXME: Mac USB keyboard generates:
24502ac6454SAndrew Thompson * 0x53: keypad NumLock/Clear
24602ac6454SAndrew Thompson * 0x66: Power
24702ac6454SAndrew Thompson * 0x67: keypad =
24802ac6454SAndrew Thompson * 0x68: F13
24902ac6454SAndrew Thompson * 0x69: F14
25002ac6454SAndrew Thompson * 0x6a: F15
251db002659SHans Petter Selasky *
252db002659SHans Petter Selasky * USB Apple Keyboard JIS generates:
253db002659SHans Petter Selasky * 0x90: Kana
254db002659SHans Petter Selasky * 0x91: Eisu
25502ac6454SAndrew Thompson */
25602ac6454SAndrew Thompson static const uint8_t ukbd_trtab[256] = {
25702ac6454SAndrew Thompson 0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */
25802ac6454SAndrew Thompson 18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */
25902ac6454SAndrew Thompson 50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */
26002ac6454SAndrew Thompson 22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */
26102ac6454SAndrew Thompson 4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */
26202ac6454SAndrew Thompson 28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */
26302ac6454SAndrew Thompson 27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */
26402ac6454SAndrew Thompson 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */
26502ac6454SAndrew Thompson 65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */
26602ac6454SAndrew Thompson 104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */
26702ac6454SAndrew Thompson 97, 100, 95, 69, 91, 55, 74, 78,/* 50 - 57 */
26802ac6454SAndrew Thompson 89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */
26902ac6454SAndrew Thompson 72, 73, 82, 83, 86, 107, 122, NN, /* 60 - 67 */
27002ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */
27102ac6454SAndrew Thompson NN, NN, NN, NN, 115, 108, 111, 113, /* 70 - 77 */
27202ac6454SAndrew Thompson 109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */
273aea0c7f3SHiroki Sato 121, 120, NN, NN, NN, NN, NN, 123, /* 80 - 87 */
274ded67349SBruce Evans 124, 125, 126, 127, 128, NN, NN, NN, /* 88 - 8F */
275db002659SHans Petter Selasky 129, 130, NN, NN, NN, NN, NN, NN, /* 90 - 97 */
27602ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */
27702ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */
27802ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */
27902ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */
28002ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */
28102ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */
28202ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */
28302ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */
28402ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */
28502ac6454SAndrew Thompson 29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */
28602ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */
28702ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */
28802ac6454SAndrew Thompson NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */
28902ac6454SAndrew Thompson };
29002ac6454SAndrew Thompson
291e92091adSHans Petter Selasky static const uint8_t ukbd_boot_desc[] = {
292e92091adSHans Petter Selasky 0x05, 0x01, 0x09, 0x06, 0xa1,
293e92091adSHans Petter Selasky 0x01, 0x05, 0x07, 0x19, 0xe0,
294e92091adSHans Petter Selasky 0x29, 0xe7, 0x15, 0x00, 0x25,
295e92091adSHans Petter Selasky 0x01, 0x75, 0x01, 0x95, 0x08,
296e92091adSHans Petter Selasky 0x81, 0x02, 0x95, 0x01, 0x75,
297e92091adSHans Petter Selasky 0x08, 0x81, 0x01, 0x95, 0x03,
298e92091adSHans Petter Selasky 0x75, 0x01, 0x05, 0x08, 0x19,
299e92091adSHans Petter Selasky 0x01, 0x29, 0x03, 0x91, 0x02,
300e92091adSHans Petter Selasky 0x95, 0x05, 0x75, 0x01, 0x91,
301e92091adSHans Petter Selasky 0x01, 0x95, 0x06, 0x75, 0x08,
302e92091adSHans Petter Selasky 0x15, 0x00, 0x26, 0xff, 0x00,
303e92091adSHans Petter Selasky 0x05, 0x07, 0x19, 0x00, 0x2a,
304e92091adSHans Petter Selasky 0xff, 0x00, 0x81, 0x00, 0xc0
305e92091adSHans Petter Selasky };
306e92091adSHans Petter Selasky
307*541e7a98SFrank Hilgendorf static const STRUCT_USB_HOST_ID ukbd_apple_iso_models[] = {
308*541e7a98SFrank Hilgendorf /* PowerBooks Feb 2005, iBooks G4 */
309*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_FOUNTAIN_ISO) },
310*541e7a98SFrank Hilgendorf /* PowerBooks Oct 2005 */
311*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_GEYSER_ISO) },
312*541e7a98SFrank Hilgendorf /* Core Duo MacBook & MacBook Pro */
313*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_GEYSER3_ISO) },
314*541e7a98SFrank Hilgendorf /* Core2 Duo MacBook & MacBook Pro */
315*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_GEYSER4_ISO) },
316*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_GEYSER4_HF_ISO) },
317*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ALU_MINI_ISO) },
318*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ALU_ISO) },
319*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ALU_REVB_ISO) },
320*541e7a98SFrank Hilgendorf /* MacbookAir, aka wellspring */
321*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING_ISO) },
322*541e7a98SFrank Hilgendorf /* MacbookProPenryn, aka wellspring2 */
323*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING2_ISO) },
324*541e7a98SFrank Hilgendorf /* Macbook5,1 (unibody), aka wellspring3 */
325*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING3_ISO) },
326*541e7a98SFrank Hilgendorf /* MacbookAir3,2 (unibody), aka wellspring4 */
327*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING4_ISO) },
328*541e7a98SFrank Hilgendorf /* MacbookAir3,1 (unibody), aka wellspring4 */
329*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING4A_ISO) },
330*541e7a98SFrank Hilgendorf /* Macbook8 (unibody, March 2011) */
331*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING5_ISO) },
332*541e7a98SFrank Hilgendorf /* Macbook8,2 (unibody) */
333*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING5A_ISO) },
334*541e7a98SFrank Hilgendorf /* MacbookAir4,2 (unibody, July 2011) */
335*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING6_ISO) },
336*541e7a98SFrank Hilgendorf /* MacbookAir4,1 (unibody, July 2011) */
337*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING6A_ISO) },
338*541e7a98SFrank Hilgendorf /* MacbookPro10,1 (unibody, June 2012) */
339*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING7_ISO) },
340*541e7a98SFrank Hilgendorf /* MacbookPro10,2 (unibody, October 2012) */
341*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING7A_ISO) },
342*541e7a98SFrank Hilgendorf /* MacbookAir6,2 (unibody, June 2013) */
343*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING8_ISO) },
344*541e7a98SFrank Hilgendorf /* MacbookPro12,1 */
345*541e7a98SFrank Hilgendorf { USB_VP(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_WELLSPRING9_ISO) },
346*541e7a98SFrank Hilgendorf };
347*541e7a98SFrank Hilgendorf
348*541e7a98SFrank Hilgendorf
34902ac6454SAndrew Thompson /* prototypes */
35002ac6454SAndrew Thompson static void ukbd_timeout(void *);
35102ac6454SAndrew Thompson static void ukbd_set_leds(struct ukbd_softc *, uint8_t);
35202ac6454SAndrew Thompson static int ukbd_set_typematic(keyboard_t *, int);
35302ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
354d4028678SHans Petter Selasky static uint32_t ukbd_atkeycode(int, const uint64_t *);
355d4028678SHans Petter Selasky static int ukbd_key2scan(struct ukbd_softc *, int, const uint64_t *, int);
35602ac6454SAndrew Thompson #endif
35702ac6454SAndrew Thompson static uint32_t ukbd_read_char(keyboard_t *, int);
35802ac6454SAndrew Thompson static void ukbd_clear_state(keyboard_t *);
35902ac6454SAndrew Thompson static int ukbd_ioctl(keyboard_t *, u_long, caddr_t);
36002ac6454SAndrew Thompson static int ukbd_enable(keyboard_t *);
36102ac6454SAndrew Thompson static int ukbd_disable(keyboard_t *);
36202ac6454SAndrew Thompson static void ukbd_interrupt(struct ukbd_softc *);
3631bdb81f1SAndrew Thompson static void ukbd_event_keyinput(struct ukbd_softc *);
36402ac6454SAndrew Thompson
36502ac6454SAndrew Thompson static device_probe_t ukbd_probe;
36602ac6454SAndrew Thompson static device_attach_t ukbd_attach;
36702ac6454SAndrew Thompson static device_detach_t ukbd_detach;
36802ac6454SAndrew Thompson static device_resume_t ukbd_resume;
36902ac6454SAndrew Thompson
370a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
37148f2b006SVladimir Kondratyev static evdev_event_t ukbd_ev_event;
37248f2b006SVladimir Kondratyev
3735163e77fSOleksandr Tymoshenko static const struct evdev_methods ukbd_evdev_methods = {
37448f2b006SVladimir Kondratyev .ev_event = ukbd_ev_event,
37510063b79SOleksandr Tymoshenko };
37610063b79SOleksandr Tymoshenko #endif
37710063b79SOleksandr Tymoshenko
378d4028678SHans Petter Selasky static bool
ukbd_any_key_pressed(struct ukbd_softc * sc)379247549d1SAlfred Perlstein ukbd_any_key_pressed(struct ukbd_softc *sc)
380247549d1SAlfred Perlstein {
381d4028678SHans Petter Selasky bool ret = false;
382d4028678SHans Petter Selasky unsigned i;
383247549d1SAlfred Perlstein
384d4028678SHans Petter Selasky for (i = 0; i != howmany(UKBD_NKEYCODE, 64); i++)
385d4028678SHans Petter Selasky ret |= (sc->sc_odata.bitmap[i] != 0);
386d4028678SHans Petter Selasky return (ret);
387d4028678SHans Petter Selasky }
388247549d1SAlfred Perlstein
389d4028678SHans Petter Selasky static bool
ukbd_any_key_valid(struct ukbd_softc * sc)390d4028678SHans Petter Selasky ukbd_any_key_valid(struct ukbd_softc *sc)
391d4028678SHans Petter Selasky {
392d4028678SHans Petter Selasky bool ret = false;
393d4028678SHans Petter Selasky unsigned i;
394d4028678SHans Petter Selasky
395d4028678SHans Petter Selasky for (i = 0; i != howmany(UKBD_NKEYCODE, 64); i++)
396d4028678SHans Petter Selasky ret |= (sc->sc_loc_key_valid[i] != 0);
397d4028678SHans Petter Selasky return (ret);
398247549d1SAlfred Perlstein }
399247549d1SAlfred Perlstein
400144d6690SHans Petter Selasky static bool
ukbd_is_modifier_key(uint32_t key)401144d6690SHans Petter Selasky ukbd_is_modifier_key(uint32_t key)
402144d6690SHans Petter Selasky {
403144d6690SHans Petter Selasky
404144d6690SHans Petter Selasky return (key >= 0xe0 && key <= 0xe7);
405144d6690SHans Petter Selasky }
406144d6690SHans Petter Selasky
407247549d1SAlfred Perlstein static void
ukbd_start_timer(struct ukbd_softc * sc)408247549d1SAlfred Perlstein ukbd_start_timer(struct ukbd_softc *sc)
409247549d1SAlfred Perlstein {
4106e786862SHans Petter Selasky sbintime_t delay, now, prec;
4111a58327bSBruce Evans
4126e786862SHans Petter Selasky now = sbinuptime();
4136e786862SHans Petter Selasky
4146e786862SHans Petter Selasky /* check if initial delay passed and fallback to key repeat delay */
4156e786862SHans Petter Selasky if (sc->sc_delay == 0)
4166e786862SHans Petter Selasky sc->sc_delay = sc->sc_kbd.kb_delay2;
4176e786862SHans Petter Selasky
4186e786862SHans Petter Selasky /* compute timeout */
4191a58327bSBruce Evans delay = SBT_1MS * sc->sc_delay;
4201a58327bSBruce Evans sc->sc_co_basetime += delay;
4216e786862SHans Petter Selasky
4226e786862SHans Petter Selasky /* check if we are running behind */
4236e786862SHans Petter Selasky if (sc->sc_co_basetime < now)
4246e786862SHans Petter Selasky sc->sc_co_basetime = now;
4256e786862SHans Petter Selasky
4261a58327bSBruce Evans /* This is rarely called, so prefer precision to efficiency. */
4271a58327bSBruce Evans prec = qmin(delay >> 7, SBT_1MS * 10);
4280eb8d462SHans Petter Selasky usb_callout_reset_sbt(&sc->sc_callout, sc->sc_co_basetime, prec,
4291a58327bSBruce Evans ukbd_timeout, sc, C_ABSOLUTE);
430247549d1SAlfred Perlstein }
431247549d1SAlfred Perlstein
43202ac6454SAndrew Thompson static void
ukbd_put_key(struct ukbd_softc * sc,uint32_t key)43302ac6454SAndrew Thompson ukbd_put_key(struct ukbd_softc *sc, uint32_t key)
43402ac6454SAndrew Thompson {
435bc40a969SAndriy Gapon
4360eb8d462SHans Petter Selasky UKBD_LOCK_ASSERT();
43702ac6454SAndrew Thompson
43802ac6454SAndrew Thompson DPRINTF("0x%02x (%d) %s\n", key, key,
43902ac6454SAndrew Thompson (key & KEY_RELEASE) ? "released" : "pressed");
44002ac6454SAndrew Thompson
441a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
442769935a4SVladimir Kondratyev if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD && sc->sc_evdev != NULL)
44310063b79SOleksandr Tymoshenko evdev_push_event(sc->sc_evdev, EV_KEY,
44410063b79SOleksandr Tymoshenko evdev_hid2key(KEY_INDEX(key)), !(key & KEY_RELEASE));
44518308893SVladimir Kondratyev if (sc->sc_evdev != NULL && evdev_is_grabbed(sc->sc_evdev))
44618308893SVladimir Kondratyev return;
44710063b79SOleksandr Tymoshenko #endif
44810063b79SOleksandr Tymoshenko
44902ac6454SAndrew Thompson if (sc->sc_inputs < UKBD_IN_BUF_SIZE) {
45002ac6454SAndrew Thompson sc->sc_input[sc->sc_inputtail] = key;
45102ac6454SAndrew Thompson ++(sc->sc_inputs);
45202ac6454SAndrew Thompson ++(sc->sc_inputtail);
45302ac6454SAndrew Thompson if (sc->sc_inputtail >= UKBD_IN_BUF_SIZE) {
45402ac6454SAndrew Thompson sc->sc_inputtail = 0;
45502ac6454SAndrew Thompson }
45602ac6454SAndrew Thompson } else {
45702ac6454SAndrew Thompson DPRINTF("input buffer is full\n");
45802ac6454SAndrew Thompson }
45902ac6454SAndrew Thompson }
46002ac6454SAndrew Thompson
461dddb25f9SAlfred Perlstein static void
ukbd_do_poll(struct ukbd_softc * sc,uint8_t wait)462dddb25f9SAlfred Perlstein ukbd_do_poll(struct ukbd_softc *sc, uint8_t wait)
46302ac6454SAndrew Thompson {
464bc40a969SAndriy Gapon
4650eb8d462SHans Petter Selasky UKBD_LOCK_ASSERT();
466bc40a969SAndriy Gapon KASSERT((sc->sc_flags & UKBD_FLAG_POLLING) != 0,
467bc40a969SAndriy Gapon ("ukbd_do_poll called when not polling\n"));
46802ac6454SAndrew Thompson DPRINTFN(2, "polling\n");
46902ac6454SAndrew Thompson
4700eb8d462SHans Petter Selasky if (USB_IN_POLLING_MODE_FUNC() == 0) {
471bc40a969SAndriy Gapon /*
472bc40a969SAndriy Gapon * In this context the kernel is polling for input,
473bc40a969SAndriy Gapon * but the USB subsystem works in normal interrupt-driven
474bc40a969SAndriy Gapon * mode, so we just wait on the USB threads to do the job.
475bc40a969SAndriy Gapon * Note that we currently hold the Giant, but it's also used
476bc40a969SAndriy Gapon * as the transfer mtx, so we must release it while waiting.
477bc40a969SAndriy Gapon */
4781bdb81f1SAndrew Thompson while (sc->sc_inputs == 0) {
479bc40a969SAndriy Gapon /*
480bc40a969SAndriy Gapon * Give USB threads a chance to run. Note that
481bc40a969SAndriy Gapon * kern_yield performs DROP_GIANT + PICKUP_GIANT.
482bc40a969SAndriy Gapon */
483bc40a969SAndriy Gapon kern_yield(PRI_UNCHANGED);
4841bdb81f1SAndrew Thompson if (!wait)
4851bdb81f1SAndrew Thompson break;
4861bdb81f1SAndrew Thompson }
487bc40a969SAndriy Gapon return;
4881bdb81f1SAndrew Thompson }
489c3139ea6SAndrew Thompson
49002ac6454SAndrew Thompson while (sc->sc_inputs == 0) {
491ed6d949aSAndrew Thompson usbd_transfer_poll(sc->sc_xfer, UKBD_N_TRANSFER);
49202ac6454SAndrew Thompson
493247549d1SAlfred Perlstein /* Delay-optimised support for repetition of keys */
494247549d1SAlfred Perlstein if (ukbd_any_key_pressed(sc)) {
495247549d1SAlfred Perlstein /* a key is pressed - need timekeeping */
496247549d1SAlfred Perlstein DELAY(1000);
49702ac6454SAndrew Thompson
498247549d1SAlfred Perlstein /* 1 millisecond has passed */
499247549d1SAlfred Perlstein sc->sc_time_ms += 1;
500247549d1SAlfred Perlstein }
50102ac6454SAndrew Thompson
50202ac6454SAndrew Thompson ukbd_interrupt(sc);
50302ac6454SAndrew Thompson
504dddb25f9SAlfred Perlstein if (!wait)
50502ac6454SAndrew Thompson break;
50602ac6454SAndrew Thompson }
50702ac6454SAndrew Thompson }
508dddb25f9SAlfred Perlstein
509dddb25f9SAlfred Perlstein static int32_t
ukbd_get_key(struct ukbd_softc * sc,uint8_t wait)510dddb25f9SAlfred Perlstein ukbd_get_key(struct ukbd_softc *sc, uint8_t wait)
511dddb25f9SAlfred Perlstein {
512dddb25f9SAlfred Perlstein int32_t c;
513dddb25f9SAlfred Perlstein
5140eb8d462SHans Petter Selasky UKBD_LOCK_ASSERT();
5150eb8d462SHans Petter Selasky KASSERT((USB_IN_POLLING_MODE_FUNC() == 0) ||
5160eb8d462SHans Petter Selasky (sc->sc_flags & UKBD_FLAG_POLLING) != 0,
517bc40a969SAndriy Gapon ("not polling in kdb or panic\n"));
518dddb25f9SAlfred Perlstein
5191c006b57SHans Petter Selasky if (sc->sc_inputs == 0 &&
5201c006b57SHans Petter Selasky (sc->sc_flags & UKBD_FLAG_GONE) == 0) {
521dddb25f9SAlfred Perlstein /* start transfer, if not already started */
522a6ae9251SHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_0]);
523a6ae9251SHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_1]);
524dddb25f9SAlfred Perlstein }
5251bdb81f1SAndrew Thompson
5261bdb81f1SAndrew Thompson if (sc->sc_flags & UKBD_FLAG_POLLING)
527dddb25f9SAlfred Perlstein ukbd_do_poll(sc, wait);
5281bdb81f1SAndrew Thompson
52902ac6454SAndrew Thompson if (sc->sc_inputs == 0) {
53002ac6454SAndrew Thompson c = -1;
53102ac6454SAndrew Thompson } else {
53202ac6454SAndrew Thompson c = sc->sc_input[sc->sc_inputhead];
53302ac6454SAndrew Thompson --(sc->sc_inputs);
53402ac6454SAndrew Thompson ++(sc->sc_inputhead);
53502ac6454SAndrew Thompson if (sc->sc_inputhead >= UKBD_IN_BUF_SIZE) {
53602ac6454SAndrew Thompson sc->sc_inputhead = 0;
53702ac6454SAndrew Thompson }
53802ac6454SAndrew Thompson }
53902ac6454SAndrew Thompson return (c);
54002ac6454SAndrew Thompson }
54102ac6454SAndrew Thompson
54202ac6454SAndrew Thompson static void
ukbd_interrupt(struct ukbd_softc * sc)54302ac6454SAndrew Thompson ukbd_interrupt(struct ukbd_softc *sc)
54402ac6454SAndrew Thompson {
545d4028678SHans Petter Selasky const uint32_t now = sc->sc_time_ms;
546d4028678SHans Petter Selasky unsigned key;
54702ac6454SAndrew Thompson
5480eb8d462SHans Petter Selasky UKBD_LOCK_ASSERT();
549bc40a969SAndriy Gapon
5508e2e5ae7SHans Petter Selasky /* Check for modifier key changes first */
5518e2e5ae7SHans Petter Selasky for (key = 0xe0; key != 0xe8; key++) {
5528e2e5ae7SHans Petter Selasky const uint64_t mask = 1ULL << (key % 64);
5538e2e5ae7SHans Petter Selasky const uint64_t delta =
5548e2e5ae7SHans Petter Selasky sc->sc_odata.bitmap[key / 64] ^
5558e2e5ae7SHans Petter Selasky sc->sc_ndata.bitmap[key / 64];
5568e2e5ae7SHans Petter Selasky
5578e2e5ae7SHans Petter Selasky if (delta & mask) {
5588e2e5ae7SHans Petter Selasky if (sc->sc_odata.bitmap[key / 64] & mask)
5598e2e5ae7SHans Petter Selasky ukbd_put_key(sc, key | KEY_RELEASE);
5608e2e5ae7SHans Petter Selasky else
5618e2e5ae7SHans Petter Selasky ukbd_put_key(sc, key | KEY_PRESS);
5628e2e5ae7SHans Petter Selasky }
5638e2e5ae7SHans Petter Selasky }
5648e2e5ae7SHans Petter Selasky
565d4028678SHans Petter Selasky /* Check for key changes */
566d4028678SHans Petter Selasky for (key = 0; key != UKBD_NKEYCODE; key++) {
567d4028678SHans Petter Selasky const uint64_t mask = 1ULL << (key % 64);
568d4028678SHans Petter Selasky const uint64_t delta =
569d4028678SHans Petter Selasky sc->sc_odata.bitmap[key / 64] ^
570d4028678SHans Petter Selasky sc->sc_ndata.bitmap[key / 64];
571d4028678SHans Petter Selasky
572d4028678SHans Petter Selasky if (mask == 1 && delta == 0) {
573d4028678SHans Petter Selasky key += 63;
574d4028678SHans Petter Selasky continue; /* skip empty areas */
5758e2e5ae7SHans Petter Selasky } else if (ukbd_is_modifier_key(key)) {
5768e2e5ae7SHans Petter Selasky continue;
577d4028678SHans Petter Selasky } else if (delta & mask) {
578d4028678SHans Petter Selasky if (sc->sc_odata.bitmap[key / 64] & mask) {
57902ac6454SAndrew Thompson ukbd_put_key(sc, key | KEY_RELEASE);
580d4028678SHans Petter Selasky
581d4028678SHans Petter Selasky /* clear repeating key, if any */
582d4028678SHans Petter Selasky if (sc->sc_repeat_key == key)
583d4028678SHans Petter Selasky sc->sc_repeat_key = 0;
584d4028678SHans Petter Selasky } else {
585d4028678SHans Petter Selasky ukbd_put_key(sc, key | KEY_PRESS);
586d4028678SHans Petter Selasky
587144d6690SHans Petter Selasky sc->sc_co_basetime = sbinuptime();
588144d6690SHans Petter Selasky sc->sc_delay = sc->sc_kbd.kb_delay1;
589144d6690SHans Petter Selasky ukbd_start_timer(sc);
590144d6690SHans Petter Selasky
591d4028678SHans Petter Selasky /* set repeat time for last key */
592d4028678SHans Petter Selasky sc->sc_repeat_time = now + sc->sc_kbd.kb_delay1;
593d4028678SHans Petter Selasky sc->sc_repeat_key = key;
594d4028678SHans Petter Selasky }
595d4028678SHans Petter Selasky }
59602ac6454SAndrew Thompson }
59702ac6454SAndrew Thompson
598d4028678SHans Petter Selasky /* synchronize old data with new data */
599d4028678SHans Petter Selasky sc->sc_odata = sc->sc_ndata;
60002ac6454SAndrew Thompson
601d4028678SHans Petter Selasky /* check if last key is still pressed */
602d4028678SHans Petter Selasky if (sc->sc_repeat_key != 0) {
603d4028678SHans Petter Selasky const int32_t dtime = (sc->sc_repeat_time - now);
60402ac6454SAndrew Thompson
605d4028678SHans Petter Selasky /* check if time has elapsed */
606d4028678SHans Petter Selasky if (dtime <= 0) {
607d4028678SHans Petter Selasky ukbd_put_key(sc, sc->sc_repeat_key | KEY_PRESS);
608d4028678SHans Petter Selasky sc->sc_repeat_time = now + sc->sc_kbd.kb_delay2;
609d4028678SHans Petter Selasky }
610d4028678SHans Petter Selasky }
61102ac6454SAndrew Thompson
612769935a4SVladimir Kondratyev #ifdef EVDEV_SUPPORT
613769935a4SVladimir Kondratyev if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD && sc->sc_evdev != NULL)
614769935a4SVladimir Kondratyev evdev_sync(sc->sc_evdev);
61518308893SVladimir Kondratyev if (sc->sc_evdev != NULL && evdev_is_grabbed(sc->sc_evdev))
61618308893SVladimir Kondratyev return;
617769935a4SVladimir Kondratyev #endif
618769935a4SVladimir Kondratyev
619d4028678SHans Petter Selasky /* wakeup keyboard system */
6201bdb81f1SAndrew Thompson ukbd_event_keyinput(sc);
62102ac6454SAndrew Thompson }
6221bdb81f1SAndrew Thompson
6231bdb81f1SAndrew Thompson static void
ukbd_event_keyinput(struct ukbd_softc * sc)6241bdb81f1SAndrew Thompson ukbd_event_keyinput(struct ukbd_softc *sc)
6251bdb81f1SAndrew Thompson {
6261bdb81f1SAndrew Thompson int c;
6271bdb81f1SAndrew Thompson
6280eb8d462SHans Petter Selasky UKBD_LOCK_ASSERT();
629bc40a969SAndriy Gapon
630bc40a969SAndriy Gapon if ((sc->sc_flags & UKBD_FLAG_POLLING) != 0)
6311bdb81f1SAndrew Thompson return;
6321bdb81f1SAndrew Thompson
6331bdb81f1SAndrew Thompson if (sc->sc_inputs == 0)
6341bdb81f1SAndrew Thompson return;
6351bdb81f1SAndrew Thompson
63602ac6454SAndrew Thompson if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
63702ac6454SAndrew Thompson KBD_IS_BUSY(&sc->sc_kbd)) {
63802ac6454SAndrew Thompson /* let the callback function process the input */
63902ac6454SAndrew Thompson (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
64002ac6454SAndrew Thompson sc->sc_kbd.kb_callback.kc_arg);
64102ac6454SAndrew Thompson } else {
64202ac6454SAndrew Thompson /* read and discard the input, no one is waiting for it */
64302ac6454SAndrew Thompson do {
64402ac6454SAndrew Thompson c = ukbd_read_char(&sc->sc_kbd, 0);
64502ac6454SAndrew Thompson } while (c != NOKEY);
64602ac6454SAndrew Thompson }
64702ac6454SAndrew Thompson }
64802ac6454SAndrew Thompson
64902ac6454SAndrew Thompson static void
ukbd_timeout(void * arg)65002ac6454SAndrew Thompson ukbd_timeout(void *arg)
65102ac6454SAndrew Thompson {
65202ac6454SAndrew Thompson struct ukbd_softc *sc = arg;
65302ac6454SAndrew Thompson
654bc40a969SAndriy Gapon UKBD_LOCK_ASSERT();
65502ac6454SAndrew Thompson
6561a58327bSBruce Evans sc->sc_time_ms += sc->sc_delay;
6571a58327bSBruce Evans sc->sc_delay = 0;
6581bdb81f1SAndrew Thompson
65902ac6454SAndrew Thompson ukbd_interrupt(sc);
66002ac6454SAndrew Thompson
6611bdb81f1SAndrew Thompson /* Make sure any leftover key events gets read out */
6621bdb81f1SAndrew Thompson ukbd_event_keyinput(sc);
6631bdb81f1SAndrew Thompson
6641bdb81f1SAndrew Thompson if (ukbd_any_key_pressed(sc) || (sc->sc_inputs != 0)) {
665247549d1SAlfred Perlstein ukbd_start_timer(sc);
666247549d1SAlfred Perlstein }
66702ac6454SAndrew Thompson }
66802ac6454SAndrew Thompson
669d4028678SHans Petter Selasky static uint32_t
ukbd_apple_fn(uint32_t keycode)670d4028678SHans Petter Selasky ukbd_apple_fn(uint32_t keycode)
671d4028678SHans Petter Selasky {
672fa64b944SAndrew Thompson switch (keycode) {
673fa64b944SAndrew Thompson case 0x28: return 0x49; /* RETURN -> INSERT */
674fa64b944SAndrew Thompson case 0x2a: return 0x4c; /* BACKSPACE -> DEL */
675fa64b944SAndrew Thompson case 0x50: return 0x4a; /* LEFT ARROW -> HOME */
676fa64b944SAndrew Thompson case 0x4f: return 0x4d; /* RIGHT ARROW -> END */
677fa64b944SAndrew Thompson case 0x52: return 0x4b; /* UP ARROW -> PGUP */
678fa64b944SAndrew Thompson case 0x51: return 0x4e; /* DOWN ARROW -> PGDN */
679fa64b944SAndrew Thompson default: return keycode;
680fa64b944SAndrew Thompson }
681fa64b944SAndrew Thompson }
68202ac6454SAndrew Thompson
683d4028678SHans Petter Selasky static uint32_t
ukbd_apple_swap(uint32_t keycode)684d4028678SHans Petter Selasky ukbd_apple_swap(uint32_t keycode)
685d4028678SHans Petter Selasky {
686fa64b944SAndrew Thompson switch (keycode) {
687fa64b944SAndrew Thompson case 0x35: return 0x64;
688fa64b944SAndrew Thompson case 0x64: return 0x35;
689fa64b944SAndrew Thompson default: return keycode;
69002ac6454SAndrew Thompson }
69102ac6454SAndrew Thompson }
69202ac6454SAndrew Thompson
69302ac6454SAndrew Thompson static void
ukbd_intr_callback(struct usb_xfer * xfer,usb_error_t error)694ed6d949aSAndrew Thompson ukbd_intr_callback(struct usb_xfer *xfer, usb_error_t error)
69502ac6454SAndrew Thompson {
696ed6d949aSAndrew Thompson struct ukbd_softc *sc = usbd_xfer_softc(xfer);
697ed6d949aSAndrew Thompson struct usb_page_cache *pc;
698d4028678SHans Petter Selasky uint32_t i;
699fa64b944SAndrew Thompson uint8_t id;
700d4028678SHans Petter Selasky uint8_t modifiers;
701d4028678SHans Petter Selasky int offset;
702ed6d949aSAndrew Thompson int len;
703ed6d949aSAndrew Thompson
704bc40a969SAndriy Gapon UKBD_LOCK_ASSERT();
705bc40a969SAndriy Gapon
706ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
707ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
70802ac6454SAndrew Thompson
70902ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) {
71002ac6454SAndrew Thompson case USB_ST_TRANSFERRED:
71102ac6454SAndrew Thompson DPRINTF("actlen=%d bytes\n", len);
71202ac6454SAndrew Thompson
713fa64b944SAndrew Thompson if (len == 0) {
714fa64b944SAndrew Thompson DPRINTF("zero length data\n");
715fa64b944SAndrew Thompson goto tr_setup;
716fa64b944SAndrew Thompson }
717fa64b944SAndrew Thompson
718fa64b944SAndrew Thompson if (sc->sc_kbd_id != 0) {
719fa64b944SAndrew Thompson /* check and remove HID ID byte */
720ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, &id, 1);
721fa64b944SAndrew Thompson offset = 1;
722fa64b944SAndrew Thompson len--;
723e92091adSHans Petter Selasky if (len == 0) {
724e92091adSHans Petter Selasky DPRINTF("zero length data\n");
725e92091adSHans Petter Selasky goto tr_setup;
726e92091adSHans Petter Selasky }
727fa64b944SAndrew Thompson } else {
728fa64b944SAndrew Thompson offset = 0;
729e92091adSHans Petter Selasky id = 0;
730fa64b944SAndrew Thompson }
731fa64b944SAndrew Thompson
732e92091adSHans Petter Selasky if (len > UKBD_BUFFER_SIZE)
733e92091adSHans Petter Selasky len = UKBD_BUFFER_SIZE;
734fa64b944SAndrew Thompson
735e92091adSHans Petter Selasky /* get data */
736e92091adSHans Petter Selasky usbd_copy_out(pc, offset, sc->sc_buffer, len);
737e92091adSHans Petter Selasky
738e92091adSHans Petter Selasky /* clear temporary storage */
739fa64b944SAndrew Thompson memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
740fa64b944SAndrew Thompson
741d4028678SHans Petter Selasky /* clear modifiers */
742d4028678SHans Petter Selasky modifiers = 0;
743d4028678SHans Petter Selasky
744e92091adSHans Petter Selasky /* scan through HID data */
745fa64b944SAndrew Thompson if ((sc->sc_flags & UKBD_FLAG_APPLE_EJECT) &&
746e92091adSHans Petter Selasky (id == sc->sc_id_apple_eject)) {
747e92091adSHans Petter Selasky if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_apple_eject))
748d4028678SHans Petter Selasky modifiers |= MOD_EJECT;
74902ac6454SAndrew Thompson }
750e92091adSHans Petter Selasky if ((sc->sc_flags & UKBD_FLAG_APPLE_FN) &&
751e92091adSHans Petter Selasky (id == sc->sc_id_apple_fn)) {
752e92091adSHans Petter Selasky if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_apple_fn))
753d4028678SHans Petter Selasky modifiers |= MOD_FN;
754e92091adSHans Petter Selasky }
755e92091adSHans Petter Selasky
756d4028678SHans Petter Selasky for (i = 0; i != UKBD_NKEYCODE; i++) {
757d4028678SHans Petter Selasky const uint64_t valid = sc->sc_loc_key_valid[i / 64];
758d4028678SHans Petter Selasky const uint64_t mask = 1ULL << (i % 64);
759e92091adSHans Petter Selasky
760d4028678SHans Petter Selasky if (mask == 1 && valid == 0) {
761d4028678SHans Petter Selasky i += 63;
762d4028678SHans Petter Selasky continue; /* skip empty areas */
763d4028678SHans Petter Selasky } else if (~valid & mask) {
764d4028678SHans Petter Selasky continue; /* location is not valid */
765d4028678SHans Petter Selasky } else if (id != sc->sc_id_loc_key[i]) {
766d4028678SHans Petter Selasky continue; /* invalid HID ID */
767d4028678SHans Petter Selasky } else if (i == 0) {
7680ba49072SHans Petter Selasky struct hid_location tmp_loc = sc->sc_loc_key[0];
7690ba49072SHans Petter Selasky /* range check array size */
7700ba49072SHans Petter Selasky if (tmp_loc.count > UKBD_NKEYCODE)
7710ba49072SHans Petter Selasky tmp_loc.count = UKBD_NKEYCODE;
7720ba49072SHans Petter Selasky while (tmp_loc.count--) {
773d4028678SHans Petter Selasky uint32_t key =
774eead9017SVladimir Kondratyev hid_get_udata(sc->sc_buffer, len, &tmp_loc);
7750ba49072SHans Petter Selasky /* advance to next location */
7760ba49072SHans Petter Selasky tmp_loc.pos += tmp_loc.size;
777032d3153SVladimir Kondratyev if (key == KEY_ERROR) {
778032d3153SVladimir Kondratyev DPRINTF("KEY_ERROR\n");
779032d3153SVladimir Kondratyev sc->sc_ndata = sc->sc_odata;
780032d3153SVladimir Kondratyev goto tr_setup; /* ignore */
781032d3153SVladimir Kondratyev }
782d4028678SHans Petter Selasky if (modifiers & MOD_FN)
783d4028678SHans Petter Selasky key = ukbd_apple_fn(key);
784d4028678SHans Petter Selasky if (sc->sc_flags & UKBD_FLAG_APPLE_SWAP)
785d4028678SHans Petter Selasky key = ukbd_apple_swap(key);
786032d3153SVladimir Kondratyev if (key == KEY_NONE || key >= UKBD_NKEYCODE)
787d4028678SHans Petter Selasky continue;
788d4028678SHans Petter Selasky /* set key in bitmap */
789d4028678SHans Petter Selasky sc->sc_ndata.bitmap[key / 64] |= 1ULL << (key % 64);
790d4028678SHans Petter Selasky }
791d4028678SHans Petter Selasky } else if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_key[i])) {
792d4028678SHans Petter Selasky uint32_t key = i;
793d4028678SHans Petter Selasky
794d4028678SHans Petter Selasky if (modifiers & MOD_FN)
795d4028678SHans Petter Selasky key = ukbd_apple_fn(key);
796d4028678SHans Petter Selasky if (sc->sc_flags & UKBD_FLAG_APPLE_SWAP)
797d4028678SHans Petter Selasky key = ukbd_apple_swap(key);
798d4028678SHans Petter Selasky if (key == KEY_NONE || key == KEY_ERROR || key >= UKBD_NKEYCODE)
799d4028678SHans Petter Selasky continue;
800d4028678SHans Petter Selasky /* set key in bitmap */
801d4028678SHans Petter Selasky sc->sc_ndata.bitmap[key / 64] |= 1ULL << (key % 64);
802e92091adSHans Petter Selasky }
803e92091adSHans Petter Selasky }
804e92091adSHans Petter Selasky #ifdef USB_DEBUG
805d4028678SHans Petter Selasky DPRINTF("modifiers = 0x%04x\n", modifiers);
806d4028678SHans Petter Selasky for (i = 0; i != UKBD_NKEYCODE; i++) {
807d4028678SHans Petter Selasky const uint64_t valid = sc->sc_ndata.bitmap[i / 64];
808d4028678SHans Petter Selasky const uint64_t mask = 1ULL << (i % 64);
809d4028678SHans Petter Selasky
810d4028678SHans Petter Selasky if (valid & mask)
811d4028678SHans Petter Selasky DPRINTF("Key 0x%02x pressed\n", i);
81202ac6454SAndrew Thompson }
813e92091adSHans Petter Selasky #endif
81402ac6454SAndrew Thompson ukbd_interrupt(sc);
815247549d1SAlfred Perlstein
81602ac6454SAndrew Thompson case USB_ST_SETUP:
817fa64b944SAndrew Thompson tr_setup:
81802ac6454SAndrew Thompson if (sc->sc_inputs < UKBD_IN_BUF_FULL) {
819ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
820a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
82102ac6454SAndrew Thompson } else {
82202ac6454SAndrew Thompson DPRINTF("input queue is full!\n");
82302ac6454SAndrew Thompson }
824fa64b944SAndrew Thompson break;
82502ac6454SAndrew Thompson
82602ac6454SAndrew Thompson default: /* Error */
827ed6d949aSAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error));
82802ac6454SAndrew Thompson
829ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) {
83002ac6454SAndrew Thompson /* try to clear stall first */
831ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer);
832fa64b944SAndrew Thompson goto tr_setup;
83302ac6454SAndrew Thompson }
834fa64b944SAndrew Thompson break;
83502ac6454SAndrew Thompson }
83602ac6454SAndrew Thompson }
83702ac6454SAndrew Thompson
83802ac6454SAndrew Thompson static void
ukbd_set_leds_callback(struct usb_xfer * xfer,usb_error_t error)839ed6d949aSAndrew Thompson ukbd_set_leds_callback(struct usb_xfer *xfer, usb_error_t error)
84002ac6454SAndrew Thompson {
841e92091adSHans Petter Selasky struct ukbd_softc *sc = usbd_xfer_softc(xfer);
842760bc48eSAndrew Thompson struct usb_device_request req;
843ed6d949aSAndrew Thompson struct usb_page_cache *pc;
844e92091adSHans Petter Selasky uint8_t id;
845e92091adSHans Petter Selasky uint8_t any;
846e92091adSHans Petter Selasky int len;
84702ac6454SAndrew Thompson
848bc40a969SAndriy Gapon UKBD_LOCK_ASSERT();
849bc40a969SAndriy Gapon
850b850ecc1SAndrew Thompson #ifdef USB_DEBUG
851247549d1SAlfred Perlstein if (ukbd_no_leds)
852247549d1SAlfred Perlstein return;
853247549d1SAlfred Perlstein #endif
854247549d1SAlfred Perlstein
85502ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) {
85602ac6454SAndrew Thompson case USB_ST_TRANSFERRED:
85702ac6454SAndrew Thompson case USB_ST_SETUP:
858e92091adSHans Petter Selasky if (!(sc->sc_flags & UKBD_FLAG_SET_LEDS))
859e92091adSHans Petter Selasky break;
86002ac6454SAndrew Thompson sc->sc_flags &= ~UKBD_FLAG_SET_LEDS;
86102ac6454SAndrew Thompson
86202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
86302ac6454SAndrew Thompson req.bRequest = UR_SET_REPORT;
86402ac6454SAndrew Thompson USETW2(req.wValue, UHID_OUTPUT_REPORT, 0);
86502ac6454SAndrew Thompson req.wIndex[0] = sc->sc_iface_no;
86602ac6454SAndrew Thompson req.wIndex[1] = 0;
867fa64b944SAndrew Thompson req.wLength[1] = 0;
86802ac6454SAndrew Thompson
869e92091adSHans Petter Selasky memset(sc->sc_buffer, 0, UKBD_BUFFER_SIZE);
870e92091adSHans Petter Selasky
871e92091adSHans Petter Selasky id = 0;
872e92091adSHans Petter Selasky any = 0;
873e92091adSHans Petter Selasky
874e92091adSHans Petter Selasky /* Assumption: All led bits must be in the same ID. */
875e92091adSHans Petter Selasky
876e92091adSHans Petter Selasky if (sc->sc_flags & UKBD_FLAG_NUMLOCK) {
877e92091adSHans Petter Selasky if (sc->sc_leds & NLKED) {
878eead9017SVladimir Kondratyev hid_put_udata(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
879e92091adSHans Petter Selasky &sc->sc_loc_numlock, 1);
880e92091adSHans Petter Selasky }
881e92091adSHans Petter Selasky id = sc->sc_id_numlock;
882e92091adSHans Petter Selasky any = 1;
883fa64b944SAndrew Thompson }
88402ac6454SAndrew Thompson
885e92091adSHans Petter Selasky if (sc->sc_flags & UKBD_FLAG_SCROLLLOCK) {
886e92091adSHans Petter Selasky if (sc->sc_leds & SLKED) {
887eead9017SVladimir Kondratyev hid_put_udata(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
888e92091adSHans Petter Selasky &sc->sc_loc_scrolllock, 1);
889e92091adSHans Petter Selasky }
890e92091adSHans Petter Selasky id = sc->sc_id_scrolllock;
891e92091adSHans Petter Selasky any = 1;
892e92091adSHans Petter Selasky }
893e92091adSHans Petter Selasky
894e92091adSHans Petter Selasky if (sc->sc_flags & UKBD_FLAG_CAPSLOCK) {
895e92091adSHans Petter Selasky if (sc->sc_leds & CLKED) {
896eead9017SVladimir Kondratyev hid_put_udata(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
897e92091adSHans Petter Selasky &sc->sc_loc_capslock, 1);
898e92091adSHans Petter Selasky }
899e92091adSHans Petter Selasky id = sc->sc_id_capslock;
900e92091adSHans Petter Selasky any = 1;
901e92091adSHans Petter Selasky }
902e92091adSHans Petter Selasky
903e92091adSHans Petter Selasky /* if no leds, nothing to do */
904e92091adSHans Petter Selasky if (!any)
905e92091adSHans Petter Selasky break;
906e92091adSHans Petter Selasky
907e92091adSHans Petter Selasky /* range check output report length */
908e92091adSHans Petter Selasky len = sc->sc_led_size;
909e92091adSHans Petter Selasky if (len > (UKBD_BUFFER_SIZE - 1))
910e92091adSHans Petter Selasky len = (UKBD_BUFFER_SIZE - 1);
911e92091adSHans Petter Selasky
912e92091adSHans Petter Selasky /* check if we need to prefix an ID byte */
913e92091adSHans Petter Selasky sc->sc_buffer[0] = id;
914e92091adSHans Petter Selasky
915e92091adSHans Petter Selasky pc = usbd_xfer_get_frame(xfer, 1);
916e92091adSHans Petter Selasky if (id != 0) {
917e92091adSHans Petter Selasky len++;
918e92091adSHans Petter Selasky usbd_copy_in(pc, 0, sc->sc_buffer, len);
919e92091adSHans Petter Selasky } else {
920e92091adSHans Petter Selasky usbd_copy_in(pc, 0, sc->sc_buffer + 1, len);
921e92091adSHans Petter Selasky }
922e92091adSHans Petter Selasky req.wLength[0] = len;
923e92091adSHans Petter Selasky usbd_xfer_set_frame_len(xfer, 1, len);
924e92091adSHans Petter Selasky
925e92091adSHans Petter Selasky DPRINTF("len=%d, id=%d\n", len, id);
926e92091adSHans Petter Selasky
927e92091adSHans Petter Selasky /* setup control request last */
928ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
929ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, &req, sizeof(req));
930ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
931e92091adSHans Petter Selasky
932e92091adSHans Petter Selasky /* start data transfer */
933ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, 2);
934a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
935247549d1SAlfred Perlstein break;
93602ac6454SAndrew Thompson
93702ac6454SAndrew Thompson default: /* Error */
938a6eb8319SAndrew Thompson DPRINTFN(1, "error=%s\n", usbd_errstr(error));
939247549d1SAlfred Perlstein break;
94002ac6454SAndrew Thompson }
94102ac6454SAndrew Thompson }
94202ac6454SAndrew Thompson
943760bc48eSAndrew Thompson static const struct usb_config ukbd_config[UKBD_N_TRANSFER] = {
944a6ae9251SHans Petter Selasky [UKBD_INTR_DT_0] = {
945a6ae9251SHans Petter Selasky .type = UE_INTERRUPT,
946a6ae9251SHans Petter Selasky .endpoint = UE_ADDR_ANY,
947a6ae9251SHans Petter Selasky .direction = UE_DIR_IN,
948a6ae9251SHans Petter Selasky .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
949a6ae9251SHans Petter Selasky .bufsize = 0, /* use wMaxPacketSize */
950a6ae9251SHans Petter Selasky .callback = &ukbd_intr_callback,
951a6ae9251SHans Petter Selasky },
952a6ae9251SHans Petter Selasky
953a6ae9251SHans Petter Selasky [UKBD_INTR_DT_1] = {
95402ac6454SAndrew Thompson .type = UE_INTERRUPT,
95502ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY,
95602ac6454SAndrew Thompson .direction = UE_DIR_IN,
9574eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
9584eae601eSAndrew Thompson .bufsize = 0, /* use wMaxPacketSize */
9594eae601eSAndrew Thompson .callback = &ukbd_intr_callback,
96002ac6454SAndrew Thompson },
96102ac6454SAndrew Thompson
96202ac6454SAndrew Thompson [UKBD_CTRL_LED] = {
96302ac6454SAndrew Thompson .type = UE_CONTROL,
96402ac6454SAndrew Thompson .endpoint = 0x00, /* Control pipe */
96502ac6454SAndrew Thompson .direction = UE_DIR_ANY,
966e92091adSHans Petter Selasky .bufsize = sizeof(struct usb_device_request) + UKBD_BUFFER_SIZE,
9674eae601eSAndrew Thompson .callback = &ukbd_set_leds_callback,
9684eae601eSAndrew Thompson .timeout = 1000, /* 1 second */
96902ac6454SAndrew Thompson },
97002ac6454SAndrew Thompson };
97102ac6454SAndrew Thompson
9729c916c62SHans Petter Selasky /* A match on these entries will load ukbd */
9739c916c62SHans Petter Selasky static const STRUCT_USB_HOST_ID __used ukbd_devs[] = {
9749c916c62SHans Petter Selasky {USB_IFACE_CLASS(UICLASS_HID),
9759c916c62SHans Petter Selasky USB_IFACE_SUBCLASS(UISUBCLASS_BOOT),
9769c916c62SHans Petter Selasky USB_IFACE_PROTOCOL(UIPROTO_BOOT_KEYBOARD),},
9779c916c62SHans Petter Selasky };
9789c916c62SHans Petter Selasky
97902ac6454SAndrew Thompson static int
ukbd_probe(device_t dev)98002ac6454SAndrew Thompson ukbd_probe(device_t dev)
98102ac6454SAndrew Thompson {
98202ac6454SAndrew Thompson keyboard_switch_t *sw = kbd_get_switch(UKBD_DRIVER_NAME);
983760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev);
984191e179aSAndrew Thompson void *d_ptr;
985191e179aSAndrew Thompson int error;
986191e179aSAndrew Thompson uint16_t d_len;
98702ac6454SAndrew Thompson
988bc40a969SAndriy Gapon UKBD_LOCK_ASSERT();
98902ac6454SAndrew Thompson DPRINTFN(11, "\n");
99002ac6454SAndrew Thompson
99102ac6454SAndrew Thompson if (sw == NULL) {
99202ac6454SAndrew Thompson return (ENXIO);
99302ac6454SAndrew Thompson }
994f29a0724SAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST) {
99502ac6454SAndrew Thompson return (ENXIO);
99602ac6454SAndrew Thompson }
997191e179aSAndrew Thompson
998191e179aSAndrew Thompson if (uaa->info.bInterfaceClass != UICLASS_HID)
999191e179aSAndrew Thompson return (ENXIO);
1000191e179aSAndrew Thompson
1001a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_KBD_IGNORE))
100202ac6454SAndrew Thompson return (ENXIO);
100360867f57SHans Petter Selasky
100460867f57SHans Petter Selasky if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
100560867f57SHans Petter Selasky (uaa->info.bInterfaceProtocol == UIPROTO_BOOT_KEYBOARD))
1006cd10bffaSAndriy Gapon return (BUS_PROBE_DEFAULT);
1007191e179aSAndrew Thompson
1008a593f6b8SAndrew Thompson error = usbd_req_get_hid_desc(uaa->device, NULL,
1009191e179aSAndrew Thompson &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
1010191e179aSAndrew Thompson
1011191e179aSAndrew Thompson if (error)
101202ac6454SAndrew Thompson return (ENXIO);
1013191e179aSAndrew Thompson
101460867f57SHans Petter Selasky if (hid_is_keyboard(d_ptr, d_len)) {
101560867f57SHans Petter Selasky if (hid_is_mouse(d_ptr, d_len)) {
1016dddb25f9SAlfred Perlstein /*
101760867f57SHans Petter Selasky * NOTE: We currently don't support USB mouse
101860867f57SHans Petter Selasky * and USB keyboard on the same USB endpoint.
101960867f57SHans Petter Selasky * Let "ums" driver win.
1020dddb25f9SAlfred Perlstein */
1021dddb25f9SAlfred Perlstein error = ENXIO;
102260867f57SHans Petter Selasky } else {
1023cd10bffaSAndriy Gapon error = BUS_PROBE_DEFAULT;
102460867f57SHans Petter Selasky }
102560867f57SHans Petter Selasky } else {
1026191e179aSAndrew Thompson error = ENXIO;
102760867f57SHans Petter Selasky }
1028191e179aSAndrew Thompson free(d_ptr, M_TEMP);
1029191e179aSAndrew Thompson return (error);
103002ac6454SAndrew Thompson }
103102ac6454SAndrew Thompson
1032e92091adSHans Petter Selasky static void
ukbd_parse_hid(struct ukbd_softc * sc,const uint8_t * ptr,uint32_t len)1033e92091adSHans Petter Selasky ukbd_parse_hid(struct ukbd_softc *sc, const uint8_t *ptr, uint32_t len)
1034e92091adSHans Petter Selasky {
1035e92091adSHans Petter Selasky uint32_t flags;
1036d4028678SHans Petter Selasky uint32_t key;
1037e92091adSHans Petter Selasky
1038e92091adSHans Petter Selasky /* reset detected bits */
1039e92091adSHans Petter Selasky sc->sc_flags &= ~UKBD_FLAG_HID_MASK;
1040e92091adSHans Petter Selasky
1041d4028678SHans Petter Selasky /* reset detected keys */
1042d4028678SHans Petter Selasky memset(sc->sc_loc_key_valid, 0, sizeof(sc->sc_loc_key_valid));
1043d4028678SHans Petter Selasky
1044e92091adSHans Petter Selasky /* check if there is an ID byte */
1045eead9017SVladimir Kondratyev sc->sc_kbd_size = hid_report_size_max(ptr, len,
1046e92091adSHans Petter Selasky hid_input, &sc->sc_kbd_id);
1047e92091adSHans Petter Selasky
1048e92091adSHans Petter Selasky /* investigate if this is an Apple Keyboard */
1049e92091adSHans Petter Selasky if (hid_locate(ptr, len,
1050e92091adSHans Petter Selasky HID_USAGE2(HUP_CONSUMER, HUG_APPLE_EJECT),
1051e92091adSHans Petter Selasky hid_input, 0, &sc->sc_loc_apple_eject, &flags,
1052e92091adSHans Petter Selasky &sc->sc_id_apple_eject)) {
1053e92091adSHans Petter Selasky if (flags & HIO_VARIABLE)
1054*541e7a98SFrank Hilgendorf sc->sc_flags |= UKBD_FLAG_APPLE_EJECT;
1055e92091adSHans Petter Selasky DPRINTFN(1, "Found Apple eject-key\n");
1056e92091adSHans Petter Selasky }
1057e92091adSHans Petter Selasky if (hid_locate(ptr, len,
1058e92091adSHans Petter Selasky HID_USAGE2(0xFFFF, 0x0003),
1059e92091adSHans Petter Selasky hid_input, 0, &sc->sc_loc_apple_fn, &flags,
1060e92091adSHans Petter Selasky &sc->sc_id_apple_fn)) {
1061e92091adSHans Petter Selasky if (flags & HIO_VARIABLE)
1062e92091adSHans Petter Selasky sc->sc_flags |= UKBD_FLAG_APPLE_FN;
1063e92091adSHans Petter Selasky DPRINTFN(1, "Found Apple FN-key\n");
1064e92091adSHans Petter Selasky }
1065d4028678SHans Petter Selasky
1066e92091adSHans Petter Selasky /* figure out event buffer */
1067e92091adSHans Petter Selasky if (hid_locate(ptr, len,
1068e92091adSHans Petter Selasky HID_USAGE2(HUP_KEYBOARD, 0x00),
1069d4028678SHans Petter Selasky hid_input, 0, &sc->sc_loc_key[0], &flags,
1070d4028678SHans Petter Selasky &sc->sc_id_loc_key[0])) {
10713a8f0c44SHans Petter Selasky if (flags & HIO_VARIABLE) {
10723a8f0c44SHans Petter Selasky DPRINTFN(1, "Ignoring keyboard event control\n");
10733a8f0c44SHans Petter Selasky } else {
1074d4028678SHans Petter Selasky sc->sc_loc_key_valid[0] |= 1;
10753a8f0c44SHans Petter Selasky DPRINTFN(1, "Found keyboard event array\n");
10763a8f0c44SHans Petter Selasky }
1077e92091adSHans Petter Selasky }
1078e92091adSHans Petter Selasky
1079d4028678SHans Petter Selasky /* figure out the keys */
1080d4028678SHans Petter Selasky for (key = 1; key != UKBD_NKEYCODE; key++) {
1081d4028678SHans Petter Selasky if (hid_locate(ptr, len,
1082d4028678SHans Petter Selasky HID_USAGE2(HUP_KEYBOARD, key),
1083d4028678SHans Petter Selasky hid_input, 0, &sc->sc_loc_key[key], &flags,
1084d4028678SHans Petter Selasky &sc->sc_id_loc_key[key])) {
1085d4028678SHans Petter Selasky if (flags & HIO_VARIABLE) {
1086d4028678SHans Petter Selasky sc->sc_loc_key_valid[key / 64] |=
1087d4028678SHans Petter Selasky 1ULL << (key % 64);
1088d4028678SHans Petter Selasky DPRINTFN(1, "Found key 0x%02x\n", key);
1089d4028678SHans Petter Selasky }
1090d4028678SHans Petter Selasky }
1091d4028678SHans Petter Selasky }
1092d4028678SHans Petter Selasky
1093e92091adSHans Petter Selasky /* figure out leds on keyboard */
1094eead9017SVladimir Kondratyev sc->sc_led_size = hid_report_size_max(ptr, len,
1095e92091adSHans Petter Selasky hid_output, NULL);
1096e92091adSHans Petter Selasky
1097e92091adSHans Petter Selasky if (hid_locate(ptr, len,
1098e92091adSHans Petter Selasky HID_USAGE2(HUP_LEDS, 0x01),
1099e92091adSHans Petter Selasky hid_output, 0, &sc->sc_loc_numlock, &flags,
1100e92091adSHans Petter Selasky &sc->sc_id_numlock)) {
1101e92091adSHans Petter Selasky if (flags & HIO_VARIABLE)
1102e92091adSHans Petter Selasky sc->sc_flags |= UKBD_FLAG_NUMLOCK;
1103e92091adSHans Petter Selasky DPRINTFN(1, "Found keyboard numlock\n");
1104e92091adSHans Petter Selasky }
1105e92091adSHans Petter Selasky if (hid_locate(ptr, len,
1106e92091adSHans Petter Selasky HID_USAGE2(HUP_LEDS, 0x02),
1107e92091adSHans Petter Selasky hid_output, 0, &sc->sc_loc_capslock, &flags,
1108e92091adSHans Petter Selasky &sc->sc_id_capslock)) {
1109e92091adSHans Petter Selasky if (flags & HIO_VARIABLE)
1110e92091adSHans Petter Selasky sc->sc_flags |= UKBD_FLAG_CAPSLOCK;
1111e92091adSHans Petter Selasky DPRINTFN(1, "Found keyboard capslock\n");
1112e92091adSHans Petter Selasky }
1113e92091adSHans Petter Selasky if (hid_locate(ptr, len,
1114e92091adSHans Petter Selasky HID_USAGE2(HUP_LEDS, 0x03),
1115e92091adSHans Petter Selasky hid_output, 0, &sc->sc_loc_scrolllock, &flags,
1116e92091adSHans Petter Selasky &sc->sc_id_scrolllock)) {
1117e92091adSHans Petter Selasky if (flags & HIO_VARIABLE)
1118e92091adSHans Petter Selasky sc->sc_flags |= UKBD_FLAG_SCROLLLOCK;
1119e92091adSHans Petter Selasky DPRINTFN(1, "Found keyboard scrolllock\n");
1120e92091adSHans Petter Selasky }
1121e92091adSHans Petter Selasky }
1122e92091adSHans Petter Selasky
112302ac6454SAndrew Thompson static int
ukbd_attach(device_t dev)112402ac6454SAndrew Thompson ukbd_attach(device_t dev)
112502ac6454SAndrew Thompson {
112602ac6454SAndrew Thompson struct ukbd_softc *sc = device_get_softc(dev);
1127760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev);
11280ccd2fb0SHans Petter Selasky int unit = device_get_unit(dev);
112902ac6454SAndrew Thompson keyboard_t *kbd = &sc->sc_kbd;
1130fa64b944SAndrew Thompson void *hid_ptr = NULL;
1131e0a69b51SAndrew Thompson usb_error_t err;
113202ac6454SAndrew Thompson uint16_t n;
1133fa64b944SAndrew Thompson uint16_t hid_len;
1134a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
113510063b79SOleksandr Tymoshenko struct evdev_dev *evdev;
113610063b79SOleksandr Tymoshenko int i;
113710063b79SOleksandr Tymoshenko #endif
11380ccd2fb0SHans Petter Selasky #ifdef USB_DEBUG
11390ccd2fb0SHans Petter Selasky int rate;
11400ccd2fb0SHans Petter Selasky #endif
1141bc40a969SAndriy Gapon UKBD_LOCK_ASSERT();
1142bc40a969SAndriy Gapon
114302ac6454SAndrew Thompson kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
114402ac6454SAndrew Thompson
114502ac6454SAndrew Thompson kbd->kb_data = (void *)sc;
114602ac6454SAndrew Thompson
1147a593f6b8SAndrew Thompson device_set_usb_desc(dev);
114802ac6454SAndrew Thompson
114902ac6454SAndrew Thompson sc->sc_udev = uaa->device;
115002ac6454SAndrew Thompson sc->sc_iface = uaa->iface;
115102ac6454SAndrew Thompson sc->sc_iface_index = uaa->info.bIfaceIndex;
115202ac6454SAndrew Thompson sc->sc_iface_no = uaa->info.bIfaceNum;
115302ac6454SAndrew Thompson sc->sc_mode = K_XLATE;
115402ac6454SAndrew Thompson
1155a593f6b8SAndrew Thompson usb_callout_init_mtx(&sc->sc_callout, &Giant, 0);
115602ac6454SAndrew Thompson
1157a6ae9251SHans Petter Selasky #ifdef UKBD_NO_POLLING
1158a593f6b8SAndrew Thompson err = usbd_transfer_setup(uaa->device,
115902ac6454SAndrew Thompson &uaa->info.bIfaceIndex, sc->sc_xfer, ukbd_config,
116002ac6454SAndrew Thompson UKBD_N_TRANSFER, sc, &Giant);
1161a6ae9251SHans Petter Selasky #else
1162a6ae9251SHans Petter Selasky /*
1163a6ae9251SHans Petter Selasky * Setup the UKBD USB transfers one by one, so they are memory
1164a6ae9251SHans Petter Selasky * independent which allows for handling panics triggered by
1165a6ae9251SHans Petter Selasky * the keyboard driver itself, typically via CTRL+ALT+ESC
1166a6ae9251SHans Petter Selasky * sequences. Or if the USB keyboard driver was processing a
1167a6ae9251SHans Petter Selasky * key at the moment of panic.
1168a6ae9251SHans Petter Selasky */
1169a6ae9251SHans Petter Selasky for (n = 0; n != UKBD_N_TRANSFER; n++) {
1170a6ae9251SHans Petter Selasky err = usbd_transfer_setup(uaa->device,
1171a6ae9251SHans Petter Selasky &uaa->info.bIfaceIndex, sc->sc_xfer + n, ukbd_config + n,
1172a6ae9251SHans Petter Selasky 1, sc, &Giant);
1173a6ae9251SHans Petter Selasky if (err)
1174a6ae9251SHans Petter Selasky break;
1175a6ae9251SHans Petter Selasky }
1176a6ae9251SHans Petter Selasky #endif
117702ac6454SAndrew Thompson
117802ac6454SAndrew Thompson if (err) {
1179a593f6b8SAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(err));
118002ac6454SAndrew Thompson goto detach;
118102ac6454SAndrew Thompson }
118202ac6454SAndrew Thompson /* setup default keyboard maps */
118302ac6454SAndrew Thompson
118402ac6454SAndrew Thompson sc->sc_keymap = key_map;
118502ac6454SAndrew Thompson sc->sc_accmap = accent_map;
118602ac6454SAndrew Thompson for (n = 0; n < UKBD_NFKEY; n++) {
118702ac6454SAndrew Thompson sc->sc_fkeymap[n] = fkey_tab[n];
118802ac6454SAndrew Thompson }
118902ac6454SAndrew Thompson
1190*541e7a98SFrank Hilgendorf /* check if this is an Apple keyboard with swapped key codes
1191*541e7a98SFrank Hilgendorf * apparently, these are the ISO layout models
1192*541e7a98SFrank Hilgendorf */
1193*541e7a98SFrank Hilgendorf DPRINTF("uaa vendor: 0x%04x, uaa product 0x%04x\n", uaa->info.idVendor, uaa->info.idProduct );
1194*541e7a98SFrank Hilgendorf if (usbd_lookup_id_by_uaa(ukbd_apple_iso_models, sizeof(ukbd_apple_iso_models), uaa) == 0) {
1195*541e7a98SFrank Hilgendorf sc->sc_flags |= UKBD_FLAG_APPLE_SWAP;
1196*541e7a98SFrank Hilgendorf DPRINTF("UKBD_FLAG_APPLE_SWAP set\n");
1197*541e7a98SFrank Hilgendorf } else {
1198*541e7a98SFrank Hilgendorf DPRINTF("UKBD_FLAG_APPLE_SWAP not set\n");
1199*541e7a98SFrank Hilgendorf }
1200*541e7a98SFrank Hilgendorf
120102ac6454SAndrew Thompson kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
120202ac6454SAndrew Thompson sc->sc_fkeymap, UKBD_NFKEY);
120302ac6454SAndrew Thompson
120402ac6454SAndrew Thompson KBD_FOUND_DEVICE(kbd);
120502ac6454SAndrew Thompson
120602ac6454SAndrew Thompson ukbd_clear_state(kbd);
120702ac6454SAndrew Thompson
120802ac6454SAndrew Thompson /*
120902ac6454SAndrew Thompson * FIXME: set the initial value for lock keys in "sc_state"
121002ac6454SAndrew Thompson * according to the BIOS data?
121102ac6454SAndrew Thompson */
121202ac6454SAndrew Thompson KBD_PROBE_DONE(kbd);
121302ac6454SAndrew Thompson
1214e92091adSHans Petter Selasky /* get HID descriptor */
1215a593f6b8SAndrew Thompson err = usbd_req_get_hid_desc(uaa->device, NULL, &hid_ptr,
1216fa64b944SAndrew Thompson &hid_len, M_TEMP, uaa->info.bIfaceIndex);
1217e92091adSHans Petter Selasky
1218fa64b944SAndrew Thompson if (err == 0) {
1219e92091adSHans Petter Selasky DPRINTF("Parsing HID descriptor of %d bytes\n",
1220e92091adSHans Petter Selasky (int)hid_len);
1221fa64b944SAndrew Thompson
1222e92091adSHans Petter Selasky ukbd_parse_hid(sc, hid_ptr, hid_len);
1223fa64b944SAndrew Thompson
1224fa64b944SAndrew Thompson free(hid_ptr, M_TEMP);
1225fa64b944SAndrew Thompson }
1226fa64b944SAndrew Thompson
1227e92091adSHans Petter Selasky /* check if we should use the boot protocol */
1228e92091adSHans Petter Selasky if (usb_test_quirk(uaa, UQ_KBD_BOOTPROTO) ||
1229d4028678SHans Petter Selasky (err != 0) || ukbd_any_key_valid(sc) == false) {
1230e92091adSHans Petter Selasky DPRINTF("Forcing boot protocol\n");
1231e92091adSHans Petter Selasky
1232e92091adSHans Petter Selasky err = usbd_req_set_protocol(sc->sc_udev, NULL,
1233e92091adSHans Petter Selasky sc->sc_iface_index, 0);
1234e92091adSHans Petter Selasky
1235e92091adSHans Petter Selasky if (err != 0) {
1236e92091adSHans Petter Selasky DPRINTF("Set protocol error=%s (ignored)\n",
1237e92091adSHans Petter Selasky usbd_errstr(err));
1238e92091adSHans Petter Selasky }
1239e92091adSHans Petter Selasky
1240e92091adSHans Petter Selasky ukbd_parse_hid(sc, ukbd_boot_desc, sizeof(ukbd_boot_desc));
1241e92091adSHans Petter Selasky }
1242e92091adSHans Petter Selasky
124302ac6454SAndrew Thompson /* ignore if SETIDLE fails, hence it is not crucial */
1244e92091adSHans Petter Selasky usbd_req_set_idle(sc->sc_udev, NULL, sc->sc_iface_index, 0, 0);
124502ac6454SAndrew Thompson
124602ac6454SAndrew Thompson ukbd_ioctl(kbd, KDSETLED, (caddr_t)&sc->sc_state);
124702ac6454SAndrew Thompson
124802ac6454SAndrew Thompson KBD_INIT_DONE(kbd);
124902ac6454SAndrew Thompson
125002ac6454SAndrew Thompson if (kbd_register(kbd) < 0) {
125102ac6454SAndrew Thompson goto detach;
125202ac6454SAndrew Thompson }
125302ac6454SAndrew Thompson KBD_CONFIG_DONE(kbd);
125402ac6454SAndrew Thompson
125502ac6454SAndrew Thompson ukbd_enable(kbd);
125602ac6454SAndrew Thompson
125702ac6454SAndrew Thompson #ifdef KBD_INSTALL_CDEV
125802ac6454SAndrew Thompson if (kbd_attach(kbd)) {
125902ac6454SAndrew Thompson goto detach;
126002ac6454SAndrew Thompson }
126102ac6454SAndrew Thompson #endif
126210063b79SOleksandr Tymoshenko
1263a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
126410063b79SOleksandr Tymoshenko evdev = evdev_alloc();
126510063b79SOleksandr Tymoshenko evdev_set_name(evdev, device_get_desc(dev));
126610063b79SOleksandr Tymoshenko evdev_set_phys(evdev, device_get_nameunit(dev));
126710063b79SOleksandr Tymoshenko evdev_set_id(evdev, BUS_USB, uaa->info.idVendor,
126810063b79SOleksandr Tymoshenko uaa->info.idProduct, 0);
126910063b79SOleksandr Tymoshenko evdev_set_serial(evdev, usb_get_serial(uaa->device));
127010063b79SOleksandr Tymoshenko evdev_set_methods(evdev, kbd, &ukbd_evdev_methods);
127110063b79SOleksandr Tymoshenko evdev_support_event(evdev, EV_SYN);
127210063b79SOleksandr Tymoshenko evdev_support_event(evdev, EV_KEY);
127310063b79SOleksandr Tymoshenko if (sc->sc_flags & (UKBD_FLAG_NUMLOCK | UKBD_FLAG_CAPSLOCK |
127410063b79SOleksandr Tymoshenko UKBD_FLAG_SCROLLLOCK))
127510063b79SOleksandr Tymoshenko evdev_support_event(evdev, EV_LED);
127610063b79SOleksandr Tymoshenko evdev_support_event(evdev, EV_REP);
127710063b79SOleksandr Tymoshenko
127810063b79SOleksandr Tymoshenko for (i = 0x00; i <= 0xFF; i++)
127910063b79SOleksandr Tymoshenko evdev_support_key(evdev, evdev_hid2key(i));
128010063b79SOleksandr Tymoshenko if (sc->sc_flags & UKBD_FLAG_NUMLOCK)
128110063b79SOleksandr Tymoshenko evdev_support_led(evdev, LED_NUML);
128210063b79SOleksandr Tymoshenko if (sc->sc_flags & UKBD_FLAG_CAPSLOCK)
128310063b79SOleksandr Tymoshenko evdev_support_led(evdev, LED_CAPSL);
128410063b79SOleksandr Tymoshenko if (sc->sc_flags & UKBD_FLAG_SCROLLLOCK)
128510063b79SOleksandr Tymoshenko evdev_support_led(evdev, LED_SCROLLL);
128610063b79SOleksandr Tymoshenko
1287f86e7267SVladimir Kondratyev if (evdev_register_mtx(evdev, &Giant))
128810063b79SOleksandr Tymoshenko evdev_free(evdev);
128910063b79SOleksandr Tymoshenko else
129010063b79SOleksandr Tymoshenko sc->sc_evdev = evdev;
129110063b79SOleksandr Tymoshenko #endif
129210063b79SOleksandr Tymoshenko
129302ac6454SAndrew Thompson sc->sc_flags |= UKBD_FLAG_ATTACHED;
129402ac6454SAndrew Thompson
129502ac6454SAndrew Thompson if (bootverbose) {
12964434de96SKyle Evans kbdd_diag(kbd, bootverbose);
129702ac6454SAndrew Thompson }
129802ac6454SAndrew Thompson
12990ccd2fb0SHans Petter Selasky #ifdef USB_DEBUG
13000ccd2fb0SHans Petter Selasky /* check for polling rate override */
13010ccd2fb0SHans Petter Selasky rate = ukbd_pollrate;
13020ccd2fb0SHans Petter Selasky if (rate > 0) {
13030ccd2fb0SHans Petter Selasky if (rate > 1000)
13040ccd2fb0SHans Petter Selasky rate = 1;
13050ccd2fb0SHans Petter Selasky else
13060ccd2fb0SHans Petter Selasky rate = 1000 / rate;
13070ccd2fb0SHans Petter Selasky
13080ccd2fb0SHans Petter Selasky /* set new polling interval in ms */
1309a6ae9251SHans Petter Selasky usbd_xfer_set_interval(sc->sc_xfer[UKBD_INTR_DT_0], rate);
1310a6ae9251SHans Petter Selasky usbd_xfer_set_interval(sc->sc_xfer[UKBD_INTR_DT_1], rate);
13110ccd2fb0SHans Petter Selasky }
13120ccd2fb0SHans Petter Selasky #endif
131302ac6454SAndrew Thompson /* start the keyboard */
1314a6ae9251SHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_0]);
1315a6ae9251SHans Petter Selasky usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_1]);
131602ac6454SAndrew Thompson
131702ac6454SAndrew Thompson return (0); /* success */
131802ac6454SAndrew Thompson
131902ac6454SAndrew Thompson detach:
132002ac6454SAndrew Thompson ukbd_detach(dev);
132102ac6454SAndrew Thompson return (ENXIO); /* error */
132202ac6454SAndrew Thompson }
132302ac6454SAndrew Thompson
13247b8b6d35SAndrew Thompson static int
ukbd_detach(device_t dev)132502ac6454SAndrew Thompson ukbd_detach(device_t dev)
132602ac6454SAndrew Thompson {
132702ac6454SAndrew Thompson struct ukbd_softc *sc = device_get_softc(dev);
132802ac6454SAndrew Thompson int error;
132902ac6454SAndrew Thompson
1330bc40a969SAndriy Gapon UKBD_LOCK_ASSERT();
133102ac6454SAndrew Thompson
1332bc40a969SAndriy Gapon DPRINTF("\n");
13331bdb81f1SAndrew Thompson
133402ac6454SAndrew Thompson sc->sc_flags |= UKBD_FLAG_GONE;
133502ac6454SAndrew Thompson
1336a593f6b8SAndrew Thompson usb_callout_stop(&sc->sc_callout);
133702ac6454SAndrew Thompson
13381c006b57SHans Petter Selasky /* kill any stuck keys */
13391c006b57SHans Petter Selasky if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
13401c006b57SHans Petter Selasky /* stop receiving events from the USB keyboard */
1341a6ae9251SHans Petter Selasky usbd_transfer_stop(sc->sc_xfer[UKBD_INTR_DT_0]);
1342a6ae9251SHans Petter Selasky usbd_transfer_stop(sc->sc_xfer[UKBD_INTR_DT_1]);
13431c006b57SHans Petter Selasky
13441c006b57SHans Petter Selasky /* release all leftover keys, if any */
13451c006b57SHans Petter Selasky memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
13461c006b57SHans Petter Selasky
13471c006b57SHans Petter Selasky /* process releasing of all keys */
13481c006b57SHans Petter Selasky ukbd_interrupt(sc);
13491c006b57SHans Petter Selasky }
13501c006b57SHans Petter Selasky
135102ac6454SAndrew Thompson ukbd_disable(&sc->sc_kbd);
135202ac6454SAndrew Thompson
135302ac6454SAndrew Thompson #ifdef KBD_INSTALL_CDEV
135402ac6454SAndrew Thompson if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
135502ac6454SAndrew Thompson error = kbd_detach(&sc->sc_kbd);
135602ac6454SAndrew Thompson if (error) {
135702ac6454SAndrew Thompson /* usb attach cannot return an error */
135802ac6454SAndrew Thompson device_printf(dev, "WARNING: kbd_detach() "
135902ac6454SAndrew Thompson "returned non-zero! (ignored)\n");
136002ac6454SAndrew Thompson }
136102ac6454SAndrew Thompson }
136202ac6454SAndrew Thompson #endif
136310063b79SOleksandr Tymoshenko
1364a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
136510063b79SOleksandr Tymoshenko evdev_free(sc->sc_evdev);
136610063b79SOleksandr Tymoshenko #endif
136710063b79SOleksandr Tymoshenko
136802ac6454SAndrew Thompson if (KBD_IS_CONFIGURED(&sc->sc_kbd)) {
136902ac6454SAndrew Thompson error = kbd_unregister(&sc->sc_kbd);
137002ac6454SAndrew Thompson if (error) {
137102ac6454SAndrew Thompson /* usb attach cannot return an error */
137202ac6454SAndrew Thompson device_printf(dev, "WARNING: kbd_unregister() "
137302ac6454SAndrew Thompson "returned non-zero! (ignored)\n");
137402ac6454SAndrew Thompson }
137502ac6454SAndrew Thompson }
137602ac6454SAndrew Thompson sc->sc_kbd.kb_flags = 0;
137702ac6454SAndrew Thompson
1378a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER);
137902ac6454SAndrew Thompson
1380a593f6b8SAndrew Thompson usb_callout_drain(&sc->sc_callout);
138102ac6454SAndrew Thompson
138202ac6454SAndrew Thompson DPRINTF("%s: disconnected\n",
138302ac6454SAndrew Thompson device_get_nameunit(dev));
138402ac6454SAndrew Thompson
138502ac6454SAndrew Thompson return (0);
138602ac6454SAndrew Thompson }
138702ac6454SAndrew Thompson
138802ac6454SAndrew Thompson static int
ukbd_resume(device_t dev)138902ac6454SAndrew Thompson ukbd_resume(device_t dev)
139002ac6454SAndrew Thompson {
139102ac6454SAndrew Thompson struct ukbd_softc *sc = device_get_softc(dev);
139202ac6454SAndrew Thompson
1393bc40a969SAndriy Gapon UKBD_LOCK_ASSERT();
13941bdb81f1SAndrew Thompson
139502ac6454SAndrew Thompson ukbd_clear_state(&sc->sc_kbd);
139602ac6454SAndrew Thompson
139702ac6454SAndrew Thompson return (0);
139802ac6454SAndrew Thompson }
139902ac6454SAndrew Thompson
140048f2b006SVladimir Kondratyev #ifdef EVDEV_SUPPORT
140148f2b006SVladimir Kondratyev static void
ukbd_ev_event(struct evdev_dev * evdev,uint16_t type,uint16_t code,int32_t value)140248f2b006SVladimir Kondratyev ukbd_ev_event(struct evdev_dev *evdev, uint16_t type, uint16_t code,
140348f2b006SVladimir Kondratyev int32_t value)
140448f2b006SVladimir Kondratyev {
140548f2b006SVladimir Kondratyev keyboard_t *kbd = evdev_get_softc(evdev);
140648f2b006SVladimir Kondratyev
140748f2b006SVladimir Kondratyev if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD &&
140848f2b006SVladimir Kondratyev (type == EV_LED || type == EV_REP)) {
140948f2b006SVladimir Kondratyev mtx_lock(&Giant);
141048f2b006SVladimir Kondratyev kbd_ev_event(kbd, type, code, value);
141148f2b006SVladimir Kondratyev mtx_unlock(&Giant);
141248f2b006SVladimir Kondratyev }
141348f2b006SVladimir Kondratyev }
141448f2b006SVladimir Kondratyev #endif
141548f2b006SVladimir Kondratyev
141602ac6454SAndrew Thompson /* early keyboard probe, not supported */
141702ac6454SAndrew Thompson static int
ukbd_configure(int flags)141802ac6454SAndrew Thompson ukbd_configure(int flags)
141902ac6454SAndrew Thompson {
142002ac6454SAndrew Thompson return (0);
142102ac6454SAndrew Thompson }
142202ac6454SAndrew Thompson
142302ac6454SAndrew Thompson /* detect a keyboard, not used */
142402ac6454SAndrew Thompson static int
ukbd__probe(int unit,void * arg,int flags)142502ac6454SAndrew Thompson ukbd__probe(int unit, void *arg, int flags)
142602ac6454SAndrew Thompson {
142702ac6454SAndrew Thompson return (ENXIO);
142802ac6454SAndrew Thompson }
142902ac6454SAndrew Thompson
143002ac6454SAndrew Thompson /* reset and initialize the device, not used */
143102ac6454SAndrew Thompson static int
ukbd_init(int unit,keyboard_t ** kbdp,void * arg,int flags)143202ac6454SAndrew Thompson ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
143302ac6454SAndrew Thompson {
143402ac6454SAndrew Thompson return (ENXIO);
143502ac6454SAndrew Thompson }
143602ac6454SAndrew Thompson
143702ac6454SAndrew Thompson /* test the interface to the device, not used */
143802ac6454SAndrew Thompson static int
ukbd_test_if(keyboard_t * kbd)143902ac6454SAndrew Thompson ukbd_test_if(keyboard_t *kbd)
144002ac6454SAndrew Thompson {
144102ac6454SAndrew Thompson return (0);
144202ac6454SAndrew Thompson }
144302ac6454SAndrew Thompson
144402ac6454SAndrew Thompson /* finish using this keyboard, not used */
144502ac6454SAndrew Thompson static int
ukbd_term(keyboard_t * kbd)144602ac6454SAndrew Thompson ukbd_term(keyboard_t *kbd)
144702ac6454SAndrew Thompson {
144802ac6454SAndrew Thompson return (ENXIO);
144902ac6454SAndrew Thompson }
145002ac6454SAndrew Thompson
145102ac6454SAndrew Thompson /* keyboard interrupt routine, not used */
145202ac6454SAndrew Thompson static int
ukbd_intr(keyboard_t * kbd,void * arg)145302ac6454SAndrew Thompson ukbd_intr(keyboard_t *kbd, void *arg)
145402ac6454SAndrew Thompson {
145502ac6454SAndrew Thompson return (0);
145602ac6454SAndrew Thompson }
145702ac6454SAndrew Thompson
145802ac6454SAndrew Thompson /* lock the access to the keyboard, not used */
145902ac6454SAndrew Thompson static int
ukbd_lock(keyboard_t * kbd,int lock)146002ac6454SAndrew Thompson ukbd_lock(keyboard_t *kbd, int lock)
146102ac6454SAndrew Thompson {
146202ac6454SAndrew Thompson return (1);
146302ac6454SAndrew Thompson }
146402ac6454SAndrew Thompson
146502ac6454SAndrew Thompson /*
146602ac6454SAndrew Thompson * Enable the access to the device; until this function is called,
146702ac6454SAndrew Thompson * the client cannot read from the keyboard.
146802ac6454SAndrew Thompson */
146902ac6454SAndrew Thompson static int
ukbd_enable(keyboard_t * kbd)147002ac6454SAndrew Thompson ukbd_enable(keyboard_t *kbd)
147102ac6454SAndrew Thompson {
1472bc40a969SAndriy Gapon
1473bc40a969SAndriy Gapon UKBD_LOCK();
147402ac6454SAndrew Thompson KBD_ACTIVATE(kbd);
1475bc40a969SAndriy Gapon UKBD_UNLOCK();
1476bc40a969SAndriy Gapon
147702ac6454SAndrew Thompson return (0);
147802ac6454SAndrew Thompson }
147902ac6454SAndrew Thompson
148002ac6454SAndrew Thompson /* disallow the access to the device */
148102ac6454SAndrew Thompson static int
ukbd_disable(keyboard_t * kbd)148202ac6454SAndrew Thompson ukbd_disable(keyboard_t *kbd)
148302ac6454SAndrew Thompson {
1484bc40a969SAndriy Gapon
1485bc40a969SAndriy Gapon UKBD_LOCK();
148602ac6454SAndrew Thompson KBD_DEACTIVATE(kbd);
1487bc40a969SAndriy Gapon UKBD_UNLOCK();
1488bc40a969SAndriy Gapon
148902ac6454SAndrew Thompson return (0);
149002ac6454SAndrew Thompson }
149102ac6454SAndrew Thompson
149202ac6454SAndrew Thompson /* check if data is waiting */
1493bc40a969SAndriy Gapon /* Currently unused. */
149402ac6454SAndrew Thompson static int
ukbd_check(keyboard_t * kbd)149502ac6454SAndrew Thompson ukbd_check(keyboard_t *kbd)
149602ac6454SAndrew Thompson {
149702ac6454SAndrew Thompson struct ukbd_softc *sc = kbd->kb_data;
149802ac6454SAndrew Thompson
14990eb8d462SHans Petter Selasky UKBD_LOCK_ASSERT();
1500bc40a969SAndriy Gapon
1501dddb25f9SAlfred Perlstein if (!KBD_IS_ACTIVE(kbd))
1502dddb25f9SAlfred Perlstein return (0);
1503dddb25f9SAlfred Perlstein
15041bdb81f1SAndrew Thompson if (sc->sc_flags & UKBD_FLAG_POLLING)
15051bdb81f1SAndrew Thompson ukbd_do_poll(sc, 0);
15061bdb81f1SAndrew Thompson
150702ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
150802ac6454SAndrew Thompson if (sc->sc_buffered_char[0]) {
150902ac6454SAndrew Thompson return (1);
151002ac6454SAndrew Thompson }
151102ac6454SAndrew Thompson #endif
151202ac6454SAndrew Thompson if (sc->sc_inputs > 0) {
151302ac6454SAndrew Thompson return (1);
151402ac6454SAndrew Thompson }
151502ac6454SAndrew Thompson return (0);
151602ac6454SAndrew Thompson }
151702ac6454SAndrew Thompson
151802ac6454SAndrew Thompson /* check if char is waiting */
151902ac6454SAndrew Thompson static int
ukbd_check_char_locked(keyboard_t * kbd)1520bc40a969SAndriy Gapon ukbd_check_char_locked(keyboard_t *kbd)
152102ac6454SAndrew Thompson {
152202ac6454SAndrew Thompson struct ukbd_softc *sc = kbd->kb_data;
152302ac6454SAndrew Thompson
15240eb8d462SHans Petter Selasky UKBD_LOCK_ASSERT();
1525bc40a969SAndriy Gapon
1526dddb25f9SAlfred Perlstein if (!KBD_IS_ACTIVE(kbd))
1527dddb25f9SAlfred Perlstein return (0);
1528dddb25f9SAlfred Perlstein
152902ac6454SAndrew Thompson if ((sc->sc_composed_char > 0) &&
153002ac6454SAndrew Thompson (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
153102ac6454SAndrew Thompson return (1);
153202ac6454SAndrew Thompson }
153302ac6454SAndrew Thompson return (ukbd_check(kbd));
153402ac6454SAndrew Thompson }
153502ac6454SAndrew Thompson
1536bc40a969SAndriy Gapon static int
ukbd_check_char(keyboard_t * kbd)1537bc40a969SAndriy Gapon ukbd_check_char(keyboard_t *kbd)
1538bc40a969SAndriy Gapon {
1539bc40a969SAndriy Gapon int result;
1540bc40a969SAndriy Gapon
1541bc40a969SAndriy Gapon UKBD_LOCK();
1542bc40a969SAndriy Gapon result = ukbd_check_char_locked(kbd);
1543bc40a969SAndriy Gapon UKBD_UNLOCK();
1544bc40a969SAndriy Gapon
1545bc40a969SAndriy Gapon return (result);
1546bc40a969SAndriy Gapon }
154702ac6454SAndrew Thompson
154802ac6454SAndrew Thompson /* read one byte from the keyboard if it's allowed */
1549bc40a969SAndriy Gapon /* Currently unused. */
155002ac6454SAndrew Thompson static int
ukbd_read(keyboard_t * kbd,int wait)155102ac6454SAndrew Thompson ukbd_read(keyboard_t *kbd, int wait)
155202ac6454SAndrew Thompson {
155302ac6454SAndrew Thompson struct ukbd_softc *sc = kbd->kb_data;
155402ac6454SAndrew Thompson int32_t usbcode;
155502ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
155602ac6454SAndrew Thompson uint32_t keycode;
155702ac6454SAndrew Thompson uint32_t scancode;
155802ac6454SAndrew Thompson
155902ac6454SAndrew Thompson #endif
1560bc40a969SAndriy Gapon
15610eb8d462SHans Petter Selasky UKBD_LOCK_ASSERT();
1562bc40a969SAndriy Gapon
1563dddb25f9SAlfred Perlstein if (!KBD_IS_ACTIVE(kbd))
1564dddb25f9SAlfred Perlstein return (-1);
156502ac6454SAndrew Thompson
156602ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
156702ac6454SAndrew Thompson if (sc->sc_buffered_char[0]) {
156802ac6454SAndrew Thompson scancode = sc->sc_buffered_char[0];
156902ac6454SAndrew Thompson if (scancode & SCAN_PREFIX) {
157002ac6454SAndrew Thompson sc->sc_buffered_char[0] &= ~SCAN_PREFIX;
157102ac6454SAndrew Thompson return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
157202ac6454SAndrew Thompson }
157302ac6454SAndrew Thompson sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
157402ac6454SAndrew Thompson sc->sc_buffered_char[1] = 0;
157502ac6454SAndrew Thompson return (scancode);
157602ac6454SAndrew Thompson }
157702ac6454SAndrew Thompson #endif /* UKBD_EMULATE_ATSCANCODE */
157802ac6454SAndrew Thompson
157902ac6454SAndrew Thompson /* XXX */
158002ac6454SAndrew Thompson usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
1581dddb25f9SAlfred Perlstein if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1))
1582dddb25f9SAlfred Perlstein return (-1);
1583dddb25f9SAlfred Perlstein
158402ac6454SAndrew Thompson ++(kbd->kb_count);
158502ac6454SAndrew Thompson
158602ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
1587d4028678SHans Petter Selasky keycode = ukbd_atkeycode(usbcode, sc->sc_ndata.bitmap);
158802ac6454SAndrew Thompson if (keycode == NN) {
158902ac6454SAndrew Thompson return -1;
159002ac6454SAndrew Thompson }
1591d4028678SHans Petter Selasky return (ukbd_key2scan(sc, keycode, sc->sc_ndata.bitmap,
159202ac6454SAndrew Thompson (usbcode & KEY_RELEASE)));
159302ac6454SAndrew Thompson #else /* !UKBD_EMULATE_ATSCANCODE */
159402ac6454SAndrew Thompson return (usbcode);
159502ac6454SAndrew Thompson #endif /* UKBD_EMULATE_ATSCANCODE */
159602ac6454SAndrew Thompson }
159702ac6454SAndrew Thompson
159802ac6454SAndrew Thompson /* read char from the keyboard */
159902ac6454SAndrew Thompson static uint32_t
ukbd_read_char_locked(keyboard_t * kbd,int wait)1600bc40a969SAndriy Gapon ukbd_read_char_locked(keyboard_t *kbd, int wait)
160102ac6454SAndrew Thompson {
160202ac6454SAndrew Thompson struct ukbd_softc *sc = kbd->kb_data;
160302ac6454SAndrew Thompson uint32_t action;
160402ac6454SAndrew Thompson uint32_t keycode;
160502ac6454SAndrew Thompson int32_t usbcode;
160602ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
160702ac6454SAndrew Thompson uint32_t scancode;
160802ac6454SAndrew Thompson #endif
1609dddb25f9SAlfred Perlstein
16100eb8d462SHans Petter Selasky UKBD_LOCK_ASSERT();
1611bc40a969SAndriy Gapon
1612dddb25f9SAlfred Perlstein if (!KBD_IS_ACTIVE(kbd))
1613dddb25f9SAlfred Perlstein return (NOKEY);
1614dddb25f9SAlfred Perlstein
161502ac6454SAndrew Thompson next_code:
161602ac6454SAndrew Thompson
161702ac6454SAndrew Thompson /* do we have a composed char to return ? */
161802ac6454SAndrew Thompson
161902ac6454SAndrew Thompson if ((sc->sc_composed_char > 0) &&
162002ac6454SAndrew Thompson (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
162102ac6454SAndrew Thompson action = sc->sc_composed_char;
162202ac6454SAndrew Thompson sc->sc_composed_char = 0;
162302ac6454SAndrew Thompson
162402ac6454SAndrew Thompson if (action > 0xFF) {
162502ac6454SAndrew Thompson goto errkey;
162602ac6454SAndrew Thompson }
162702ac6454SAndrew Thompson goto done;
162802ac6454SAndrew Thompson }
162902ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
163002ac6454SAndrew Thompson
163102ac6454SAndrew Thompson /* do we have a pending raw scan code? */
163202ac6454SAndrew Thompson
163302ac6454SAndrew Thompson if (sc->sc_mode == K_RAW) {
163402ac6454SAndrew Thompson scancode = sc->sc_buffered_char[0];
163502ac6454SAndrew Thompson if (scancode) {
163602ac6454SAndrew Thompson if (scancode & SCAN_PREFIX) {
163702ac6454SAndrew Thompson sc->sc_buffered_char[0] = (scancode & ~SCAN_PREFIX);
163802ac6454SAndrew Thompson return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
163902ac6454SAndrew Thompson }
164002ac6454SAndrew Thompson sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
164102ac6454SAndrew Thompson sc->sc_buffered_char[1] = 0;
164202ac6454SAndrew Thompson return (scancode);
164302ac6454SAndrew Thompson }
164402ac6454SAndrew Thompson }
164502ac6454SAndrew Thompson #endif /* UKBD_EMULATE_ATSCANCODE */
164602ac6454SAndrew Thompson
164702ac6454SAndrew Thompson /* see if there is something in the keyboard port */
164802ac6454SAndrew Thompson /* XXX */
164902ac6454SAndrew Thompson usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
165002ac6454SAndrew Thompson if (usbcode == -1) {
165102ac6454SAndrew Thompson return (NOKEY);
165202ac6454SAndrew Thompson }
165302ac6454SAndrew Thompson ++kbd->kb_count;
165402ac6454SAndrew Thompson
165502ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
165602ac6454SAndrew Thompson /* USB key index -> key code -> AT scan code */
1657d4028678SHans Petter Selasky keycode = ukbd_atkeycode(usbcode, sc->sc_ndata.bitmap);
165802ac6454SAndrew Thompson if (keycode == NN) {
165902ac6454SAndrew Thompson return (NOKEY);
166002ac6454SAndrew Thompson }
166102ac6454SAndrew Thompson /* return an AT scan code for the K_RAW mode */
166202ac6454SAndrew Thompson if (sc->sc_mode == K_RAW) {
1663d4028678SHans Petter Selasky return (ukbd_key2scan(sc, keycode, sc->sc_ndata.bitmap,
166402ac6454SAndrew Thompson (usbcode & KEY_RELEASE)));
166502ac6454SAndrew Thompson }
166602ac6454SAndrew Thompson #else /* !UKBD_EMULATE_ATSCANCODE */
166702ac6454SAndrew Thompson
166802ac6454SAndrew Thompson /* return the byte as is for the K_RAW mode */
166902ac6454SAndrew Thompson if (sc->sc_mode == K_RAW) {
167002ac6454SAndrew Thompson return (usbcode);
167102ac6454SAndrew Thompson }
167202ac6454SAndrew Thompson /* USB key index -> key code */
167302ac6454SAndrew Thompson keycode = ukbd_trtab[KEY_INDEX(usbcode)];
167402ac6454SAndrew Thompson if (keycode == NN) {
167502ac6454SAndrew Thompson return (NOKEY);
167602ac6454SAndrew Thompson }
167702ac6454SAndrew Thompson #endif /* UKBD_EMULATE_ATSCANCODE */
167802ac6454SAndrew Thompson
167902ac6454SAndrew Thompson switch (keycode) {
168002ac6454SAndrew Thompson case 0x38: /* left alt (compose key) */
168102ac6454SAndrew Thompson if (usbcode & KEY_RELEASE) {
168202ac6454SAndrew Thompson if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
168302ac6454SAndrew Thompson sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
168402ac6454SAndrew Thompson
168502ac6454SAndrew Thompson if (sc->sc_composed_char > 0xFF) {
168602ac6454SAndrew Thompson sc->sc_composed_char = 0;
168702ac6454SAndrew Thompson }
168802ac6454SAndrew Thompson }
168902ac6454SAndrew Thompson } else {
169002ac6454SAndrew Thompson if (!(sc->sc_flags & UKBD_FLAG_COMPOSE)) {
169102ac6454SAndrew Thompson sc->sc_flags |= UKBD_FLAG_COMPOSE;
169202ac6454SAndrew Thompson sc->sc_composed_char = 0;
169302ac6454SAndrew Thompson }
169402ac6454SAndrew Thompson }
169502ac6454SAndrew Thompson break;
169602ac6454SAndrew Thompson }
169702ac6454SAndrew Thompson
169802ac6454SAndrew Thompson /* return the key code in the K_CODE mode */
169902ac6454SAndrew Thompson if (usbcode & KEY_RELEASE) {
170002ac6454SAndrew Thompson keycode |= SCAN_RELEASE;
170102ac6454SAndrew Thompson }
170202ac6454SAndrew Thompson if (sc->sc_mode == K_CODE) {
170302ac6454SAndrew Thompson return (keycode);
170402ac6454SAndrew Thompson }
170502ac6454SAndrew Thompson /* compose a character code */
170602ac6454SAndrew Thompson if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
170702ac6454SAndrew Thompson switch (keycode) {
170802ac6454SAndrew Thompson /* key pressed, process it */
170902ac6454SAndrew Thompson case 0x47:
171002ac6454SAndrew Thompson case 0x48:
171102ac6454SAndrew Thompson case 0x49: /* keypad 7,8,9 */
171202ac6454SAndrew Thompson sc->sc_composed_char *= 10;
171302ac6454SAndrew Thompson sc->sc_composed_char += keycode - 0x40;
171402ac6454SAndrew Thompson goto check_composed;
171502ac6454SAndrew Thompson
171602ac6454SAndrew Thompson case 0x4B:
171702ac6454SAndrew Thompson case 0x4C:
171802ac6454SAndrew Thompson case 0x4D: /* keypad 4,5,6 */
171902ac6454SAndrew Thompson sc->sc_composed_char *= 10;
172002ac6454SAndrew Thompson sc->sc_composed_char += keycode - 0x47;
172102ac6454SAndrew Thompson goto check_composed;
172202ac6454SAndrew Thompson
172302ac6454SAndrew Thompson case 0x4F:
172402ac6454SAndrew Thompson case 0x50:
172502ac6454SAndrew Thompson case 0x51: /* keypad 1,2,3 */
172602ac6454SAndrew Thompson sc->sc_composed_char *= 10;
172702ac6454SAndrew Thompson sc->sc_composed_char += keycode - 0x4E;
172802ac6454SAndrew Thompson goto check_composed;
172902ac6454SAndrew Thompson
173002ac6454SAndrew Thompson case 0x52: /* keypad 0 */
173102ac6454SAndrew Thompson sc->sc_composed_char *= 10;
173202ac6454SAndrew Thompson goto check_composed;
173302ac6454SAndrew Thompson
173402ac6454SAndrew Thompson /* key released, no interest here */
173502ac6454SAndrew Thompson case SCAN_RELEASE | 0x47:
173602ac6454SAndrew Thompson case SCAN_RELEASE | 0x48:
173702ac6454SAndrew Thompson case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */
173802ac6454SAndrew Thompson case SCAN_RELEASE | 0x4B:
173902ac6454SAndrew Thompson case SCAN_RELEASE | 0x4C:
174002ac6454SAndrew Thompson case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */
174102ac6454SAndrew Thompson case SCAN_RELEASE | 0x4F:
174202ac6454SAndrew Thompson case SCAN_RELEASE | 0x50:
174302ac6454SAndrew Thompson case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */
174402ac6454SAndrew Thompson case SCAN_RELEASE | 0x52: /* keypad 0 */
174502ac6454SAndrew Thompson goto next_code;
174602ac6454SAndrew Thompson
174702ac6454SAndrew Thompson case 0x38: /* left alt key */
174802ac6454SAndrew Thompson break;
174902ac6454SAndrew Thompson
175002ac6454SAndrew Thompson default:
175102ac6454SAndrew Thompson if (sc->sc_composed_char > 0) {
175202ac6454SAndrew Thompson sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
175302ac6454SAndrew Thompson sc->sc_composed_char = 0;
175402ac6454SAndrew Thompson goto errkey;
175502ac6454SAndrew Thompson }
175602ac6454SAndrew Thompson break;
175702ac6454SAndrew Thompson }
175802ac6454SAndrew Thompson }
175902ac6454SAndrew Thompson /* keycode to key action */
176002ac6454SAndrew Thompson action = genkbd_keyaction(kbd, SCAN_CHAR(keycode),
176102ac6454SAndrew Thompson (keycode & SCAN_RELEASE),
176202ac6454SAndrew Thompson &sc->sc_state, &sc->sc_accents);
176302ac6454SAndrew Thompson if (action == NOKEY) {
176402ac6454SAndrew Thompson goto next_code;
176502ac6454SAndrew Thompson }
176602ac6454SAndrew Thompson done:
176702ac6454SAndrew Thompson return (action);
176802ac6454SAndrew Thompson
176902ac6454SAndrew Thompson check_composed:
177002ac6454SAndrew Thompson if (sc->sc_composed_char <= 0xFF) {
177102ac6454SAndrew Thompson goto next_code;
177202ac6454SAndrew Thompson }
177302ac6454SAndrew Thompson errkey:
177402ac6454SAndrew Thompson return (ERRKEY);
177502ac6454SAndrew Thompson }
177602ac6454SAndrew Thompson
1777bc40a969SAndriy Gapon /* Currently wait is always false. */
1778bc40a969SAndriy Gapon static uint32_t
ukbd_read_char(keyboard_t * kbd,int wait)1779bc40a969SAndriy Gapon ukbd_read_char(keyboard_t *kbd, int wait)
1780bc40a969SAndriy Gapon {
1781bc40a969SAndriy Gapon uint32_t keycode;
1782bc40a969SAndriy Gapon
1783bc40a969SAndriy Gapon UKBD_LOCK();
1784bc40a969SAndriy Gapon keycode = ukbd_read_char_locked(kbd, wait);
1785bc40a969SAndriy Gapon UKBD_UNLOCK();
1786bc40a969SAndriy Gapon
1787bc40a969SAndriy Gapon return (keycode);
1788bc40a969SAndriy Gapon }
1789bc40a969SAndriy Gapon
179002ac6454SAndrew Thompson /* some useful control functions */
179102ac6454SAndrew Thompson static int
ukbd_ioctl_locked(keyboard_t * kbd,u_long cmd,caddr_t arg)1792bc40a969SAndriy Gapon ukbd_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
179302ac6454SAndrew Thompson {
179402ac6454SAndrew Thompson struct ukbd_softc *sc = kbd->kb_data;
179502ac6454SAndrew Thompson int i;
179602ac6454SAndrew Thompson #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
179702ac6454SAndrew Thompson defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
179802ac6454SAndrew Thompson int ival;
179902ac6454SAndrew Thompson
180002ac6454SAndrew Thompson #endif
1801bc40a969SAndriy Gapon
1802bc40a969SAndriy Gapon UKBD_LOCK_ASSERT();
180302ac6454SAndrew Thompson
180402ac6454SAndrew Thompson switch (cmd) {
180502ac6454SAndrew Thompson case KDGKBMODE: /* get keyboard mode */
180602ac6454SAndrew Thompson *(int *)arg = sc->sc_mode;
180702ac6454SAndrew Thompson break;
180802ac6454SAndrew Thompson #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
180902ac6454SAndrew Thompson defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
181002ac6454SAndrew Thompson case _IO('K', 7):
181102ac6454SAndrew Thompson ival = IOCPARM_IVAL(arg);
181202ac6454SAndrew Thompson arg = (caddr_t)&ival;
181302ac6454SAndrew Thompson /* FALLTHROUGH */
181402ac6454SAndrew Thompson #endif
181502ac6454SAndrew Thompson case KDSKBMODE: /* set keyboard mode */
181602ac6454SAndrew Thompson switch (*(int *)arg) {
181702ac6454SAndrew Thompson case K_XLATE:
181802ac6454SAndrew Thompson if (sc->sc_mode != K_XLATE) {
181902ac6454SAndrew Thompson /* make lock key state and LED state match */
182002ac6454SAndrew Thompson sc->sc_state &= ~LOCK_MASK;
182102ac6454SAndrew Thompson sc->sc_state |= KBD_LED_VAL(kbd);
182202ac6454SAndrew Thompson }
182302ac6454SAndrew Thompson /* FALLTHROUGH */
182402ac6454SAndrew Thompson case K_RAW:
182502ac6454SAndrew Thompson case K_CODE:
182602ac6454SAndrew Thompson if (sc->sc_mode != *(int *)arg) {
1827bc40a969SAndriy Gapon if ((sc->sc_flags & UKBD_FLAG_POLLING) == 0)
182802ac6454SAndrew Thompson ukbd_clear_state(kbd);
182902ac6454SAndrew Thompson sc->sc_mode = *(int *)arg;
183002ac6454SAndrew Thompson }
183102ac6454SAndrew Thompson break;
183202ac6454SAndrew Thompson default:
183302ac6454SAndrew Thompson return (EINVAL);
183402ac6454SAndrew Thompson }
183502ac6454SAndrew Thompson break;
183602ac6454SAndrew Thompson
183702ac6454SAndrew Thompson case KDGETLED: /* get keyboard LED */
183802ac6454SAndrew Thompson *(int *)arg = KBD_LED_VAL(kbd);
183902ac6454SAndrew Thompson break;
184002ac6454SAndrew Thompson #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
184102ac6454SAndrew Thompson defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
184202ac6454SAndrew Thompson case _IO('K', 66):
184302ac6454SAndrew Thompson ival = IOCPARM_IVAL(arg);
184402ac6454SAndrew Thompson arg = (caddr_t)&ival;
184502ac6454SAndrew Thompson /* FALLTHROUGH */
184602ac6454SAndrew Thompson #endif
184702ac6454SAndrew Thompson case KDSETLED: /* set keyboard LED */
184802ac6454SAndrew Thompson /* NOTE: lock key state in "sc_state" won't be changed */
1849e92091adSHans Petter Selasky if (*(int *)arg & ~LOCK_MASK)
185002ac6454SAndrew Thompson return (EINVAL);
1851e92091adSHans Petter Selasky
185202ac6454SAndrew Thompson i = *(int *)arg;
1853e92091adSHans Petter Selasky
185402ac6454SAndrew Thompson /* replace CAPS LED with ALTGR LED for ALTGR keyboards */
185502ac6454SAndrew Thompson if (sc->sc_mode == K_XLATE &&
185602ac6454SAndrew Thompson kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
185702ac6454SAndrew Thompson if (i & ALKED)
185802ac6454SAndrew Thompson i |= CLKED;
185902ac6454SAndrew Thompson else
186002ac6454SAndrew Thompson i &= ~CLKED;
186102ac6454SAndrew Thompson }
1862e92091adSHans Petter Selasky if (KBD_HAS_DEVICE(kbd))
1863e92091adSHans Petter Selasky ukbd_set_leds(sc, i);
1864e92091adSHans Petter Selasky
186502ac6454SAndrew Thompson KBD_LED_VAL(kbd) = *(int *)arg;
186602ac6454SAndrew Thompson break;
186702ac6454SAndrew Thompson case KDGKBSTATE: /* get lock key state */
186802ac6454SAndrew Thompson *(int *)arg = sc->sc_state & LOCK_MASK;
186902ac6454SAndrew Thompson break;
187002ac6454SAndrew Thompson #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
187102ac6454SAndrew Thompson defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
187202ac6454SAndrew Thompson case _IO('K', 20):
187302ac6454SAndrew Thompson ival = IOCPARM_IVAL(arg);
187402ac6454SAndrew Thompson arg = (caddr_t)&ival;
187502ac6454SAndrew Thompson /* FALLTHROUGH */
187602ac6454SAndrew Thompson #endif
187702ac6454SAndrew Thompson case KDSKBSTATE: /* set lock key state */
187802ac6454SAndrew Thompson if (*(int *)arg & ~LOCK_MASK) {
187902ac6454SAndrew Thompson return (EINVAL);
188002ac6454SAndrew Thompson }
188102ac6454SAndrew Thompson sc->sc_state &= ~LOCK_MASK;
188202ac6454SAndrew Thompson sc->sc_state |= *(int *)arg;
188302ac6454SAndrew Thompson
188402ac6454SAndrew Thompson /* set LEDs and quit */
188502ac6454SAndrew Thompson return (ukbd_ioctl(kbd, KDSETLED, arg));
188602ac6454SAndrew Thompson
188702ac6454SAndrew Thompson case KDSETREPEAT: /* set keyboard repeat rate (new
188802ac6454SAndrew Thompson * interface) */
188902ac6454SAndrew Thompson if (!KBD_HAS_DEVICE(kbd)) {
189002ac6454SAndrew Thompson return (0);
189102ac6454SAndrew Thompson }
1892e99472e0SBruce Evans /*
1893e99472e0SBruce Evans * Convert negative, zero and tiny args to the same limits
1894e99472e0SBruce Evans * as atkbd. We could support delays of 1 msec, but
1895e99472e0SBruce Evans * anything much shorter than the shortest atkbd value
1896e99472e0SBruce Evans * of 250.34 is almost unusable as well as incompatible.
1897e99472e0SBruce Evans */
1898e99472e0SBruce Evans kbd->kb_delay1 = imax(((int *)arg)[0], 250);
1899e99472e0SBruce Evans kbd->kb_delay2 = imax(((int *)arg)[1], 34);
1900a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
190110063b79SOleksandr Tymoshenko if (sc->sc_evdev != NULL)
190210063b79SOleksandr Tymoshenko evdev_push_repeats(sc->sc_evdev, kbd);
190310063b79SOleksandr Tymoshenko #endif
190402ac6454SAndrew Thompson return (0);
190502ac6454SAndrew Thompson
190602ac6454SAndrew Thompson #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
190702ac6454SAndrew Thompson defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
190802ac6454SAndrew Thompson case _IO('K', 67):
190902ac6454SAndrew Thompson ival = IOCPARM_IVAL(arg);
191002ac6454SAndrew Thompson arg = (caddr_t)&ival;
191102ac6454SAndrew Thompson /* FALLTHROUGH */
191202ac6454SAndrew Thompson #endif
191302ac6454SAndrew Thompson case KDSETRAD: /* set keyboard repeat rate (old
191402ac6454SAndrew Thompson * interface) */
191502ac6454SAndrew Thompson return (ukbd_set_typematic(kbd, *(int *)arg));
191602ac6454SAndrew Thompson
191702ac6454SAndrew Thompson case PIO_KEYMAP: /* set keyboard translation table */
191802ac6454SAndrew Thompson case PIO_KEYMAPENT: /* set keyboard translation table
191902ac6454SAndrew Thompson * entry */
192002ac6454SAndrew Thompson case PIO_DEADKEYMAP: /* set accent key translation table */
1921f2005895SStefan Eßer #ifdef COMPAT_FREEBSD13
1922f2005895SStefan Eßer case OPIO_KEYMAP: /* set keyboard translation table
1923f2005895SStefan Eßer * (compat) */
1924f2005895SStefan Eßer case OPIO_DEADKEYMAP: /* set accent key translation table
1925f2005895SStefan Eßer * (compat) */
1926f2005895SStefan Eßer #endif /* COMPAT_FREEBSD13 */
192702ac6454SAndrew Thompson sc->sc_accents = 0;
192802ac6454SAndrew Thompson /* FALLTHROUGH */
192902ac6454SAndrew Thompson default:
193002ac6454SAndrew Thompson return (genkbd_commonioctl(kbd, cmd, arg));
193102ac6454SAndrew Thompson }
193202ac6454SAndrew Thompson
193302ac6454SAndrew Thompson return (0);
193402ac6454SAndrew Thompson }
193502ac6454SAndrew Thompson
1936bc40a969SAndriy Gapon static int
ukbd_ioctl(keyboard_t * kbd,u_long cmd,caddr_t arg)1937bc40a969SAndriy Gapon ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
1938bc40a969SAndriy Gapon {
1939bc40a969SAndriy Gapon int result;
1940bc40a969SAndriy Gapon
1941bc40a969SAndriy Gapon /*
1942ed25d0e8SHans Petter Selasky * XXX Check if someone is calling us from a critical section:
1943cdea3befSHans Petter Selasky */
1944cdea3befSHans Petter Selasky if (curthread->td_critnest != 0)
1945cdea3befSHans Petter Selasky return (EDEADLK);
1946cdea3befSHans Petter Selasky
1947cdea3befSHans Petter Selasky /*
1948bc40a969SAndriy Gapon * XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any
1949bc40a969SAndriy Gapon * context where printf(9) can be called, which among other things
1950bc40a969SAndriy Gapon * includes interrupt filters and threads with any kinds of locks
1951bc40a969SAndriy Gapon * already held. For this reason it would be dangerous to acquire
1952bc40a969SAndriy Gapon * the Giant here unconditionally. On the other hand we have to
1953bc40a969SAndriy Gapon * have it to handle the ioctl.
1954bc40a969SAndriy Gapon * So we make our best effort to auto-detect whether we can grab
1955bc40a969SAndriy Gapon * the Giant or not. Blame syscons(4) for this.
1956bc40a969SAndriy Gapon */
1957bc40a969SAndriy Gapon switch (cmd) {
1958bc40a969SAndriy Gapon case KDGKBSTATE:
1959bc40a969SAndriy Gapon case KDSKBSTATE:
1960bc40a969SAndriy Gapon case KDSETLED:
19610eb8d462SHans Petter Selasky if (!mtx_owned(&Giant) && !USB_IN_POLLING_MODE_FUNC())
1962bc40a969SAndriy Gapon return (EDEADLK); /* best I could come up with */
1963bc40a969SAndriy Gapon /* FALLTHROUGH */
1964bc40a969SAndriy Gapon default:
1965bc40a969SAndriy Gapon UKBD_LOCK();
1966bc40a969SAndriy Gapon result = ukbd_ioctl_locked(kbd, cmd, arg);
1967bc40a969SAndriy Gapon UKBD_UNLOCK();
1968bc40a969SAndriy Gapon return (result);
1969bc40a969SAndriy Gapon }
1970bc40a969SAndriy Gapon }
1971bc40a969SAndriy Gapon
197202ac6454SAndrew Thompson /* clear the internal state of the keyboard */
197302ac6454SAndrew Thompson static void
ukbd_clear_state(keyboard_t * kbd)197402ac6454SAndrew Thompson ukbd_clear_state(keyboard_t *kbd)
197502ac6454SAndrew Thompson {
197602ac6454SAndrew Thompson struct ukbd_softc *sc = kbd->kb_data;
197702ac6454SAndrew Thompson
19780eb8d462SHans Petter Selasky UKBD_LOCK_ASSERT();
197902ac6454SAndrew Thompson
198002ac6454SAndrew Thompson sc->sc_flags &= ~(UKBD_FLAG_COMPOSE | UKBD_FLAG_POLLING);
198102ac6454SAndrew Thompson sc->sc_state &= LOCK_MASK; /* preserve locking key state */
198202ac6454SAndrew Thompson sc->sc_accents = 0;
198302ac6454SAndrew Thompson sc->sc_composed_char = 0;
198402ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
198502ac6454SAndrew Thompson sc->sc_buffered_char[0] = 0;
198602ac6454SAndrew Thompson sc->sc_buffered_char[1] = 0;
198702ac6454SAndrew Thompson #endif
19881bdb81f1SAndrew Thompson memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
19891bdb81f1SAndrew Thompson memset(&sc->sc_odata, 0, sizeof(sc->sc_odata));
1990d4028678SHans Petter Selasky sc->sc_repeat_time = 0;
1991d4028678SHans Petter Selasky sc->sc_repeat_key = 0;
199202ac6454SAndrew Thompson }
199302ac6454SAndrew Thompson
199402ac6454SAndrew Thompson /* save the internal state, not used */
199502ac6454SAndrew Thompson static int
ukbd_get_state(keyboard_t * kbd,void * buf,size_t len)199602ac6454SAndrew Thompson ukbd_get_state(keyboard_t *kbd, void *buf, size_t len)
199702ac6454SAndrew Thompson {
199802ac6454SAndrew Thompson return (len == 0) ? 1 : -1;
199902ac6454SAndrew Thompson }
200002ac6454SAndrew Thompson
200102ac6454SAndrew Thompson /* set the internal state, not used */
200202ac6454SAndrew Thompson static int
ukbd_set_state(keyboard_t * kbd,void * buf,size_t len)200302ac6454SAndrew Thompson ukbd_set_state(keyboard_t *kbd, void *buf, size_t len)
200402ac6454SAndrew Thompson {
200502ac6454SAndrew Thompson return (EINVAL);
200602ac6454SAndrew Thompson }
200702ac6454SAndrew Thompson
200802ac6454SAndrew Thompson static int
ukbd_poll(keyboard_t * kbd,int on)200902ac6454SAndrew Thompson ukbd_poll(keyboard_t *kbd, int on)
201002ac6454SAndrew Thompson {
201102ac6454SAndrew Thompson struct ukbd_softc *sc = kbd->kb_data;
201202ac6454SAndrew Thompson
2013bc40a969SAndriy Gapon UKBD_LOCK();
2014f87a304cSHans Petter Selasky /*
2015f87a304cSHans Petter Selasky * Keep a reference count on polling to allow recursive
2016f87a304cSHans Petter Selasky * cngrab() during a panic for example.
2017f87a304cSHans Petter Selasky */
2018f87a304cSHans Petter Selasky if (on)
2019f87a304cSHans Petter Selasky sc->sc_polling++;
2020a6ae9251SHans Petter Selasky else if (sc->sc_polling > 0)
2021f87a304cSHans Petter Selasky sc->sc_polling--;
2022f87a304cSHans Petter Selasky
2023f87a304cSHans Petter Selasky if (sc->sc_polling != 0) {
202402ac6454SAndrew Thompson sc->sc_flags |= UKBD_FLAG_POLLING;
20251bdb81f1SAndrew Thompson sc->sc_poll_thread = curthread;
202602ac6454SAndrew Thompson } else {
202702ac6454SAndrew Thompson sc->sc_flags &= ~UKBD_FLAG_POLLING;
20281a58327bSBruce Evans sc->sc_delay = 0;
202902ac6454SAndrew Thompson }
2030bc40a969SAndriy Gapon UKBD_UNLOCK();
2031bc40a969SAndriy Gapon
203202ac6454SAndrew Thompson return (0);
203302ac6454SAndrew Thompson }
203402ac6454SAndrew Thompson
203502ac6454SAndrew Thompson /* local functions */
203602ac6454SAndrew Thompson
203702ac6454SAndrew Thompson static void
ukbd_set_leds(struct ukbd_softc * sc,uint8_t leds)203802ac6454SAndrew Thompson ukbd_set_leds(struct ukbd_softc *sc, uint8_t leds)
203902ac6454SAndrew Thompson {
2040bc40a969SAndriy Gapon
2041bc40a969SAndriy Gapon UKBD_LOCK_ASSERT();
204202ac6454SAndrew Thompson DPRINTF("leds=0x%02x\n", leds);
204302ac6454SAndrew Thompson
204430f34a51SVladimir Kondratyev #ifdef EVDEV_SUPPORT
204530f34a51SVladimir Kondratyev if (sc->sc_evdev != NULL)
204630f34a51SVladimir Kondratyev evdev_push_leds(sc->sc_evdev, leds);
204730f34a51SVladimir Kondratyev #endif
204830f34a51SVladimir Kondratyev
204902ac6454SAndrew Thompson sc->sc_leds = leds;
205002ac6454SAndrew Thompson sc->sc_flags |= UKBD_FLAG_SET_LEDS;
205102ac6454SAndrew Thompson
205202ac6454SAndrew Thompson /* start transfer, if not already started */
205302ac6454SAndrew Thompson
2054a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UKBD_CTRL_LED]);
205502ac6454SAndrew Thompson }
205602ac6454SAndrew Thompson
205702ac6454SAndrew Thompson static int
ukbd_set_typematic(keyboard_t * kbd,int code)205802ac6454SAndrew Thompson ukbd_set_typematic(keyboard_t *kbd, int code)
205902ac6454SAndrew Thompson {
2060a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
206110063b79SOleksandr Tymoshenko struct ukbd_softc *sc = kbd->kb_data;
206210063b79SOleksandr Tymoshenko #endif
206302ac6454SAndrew Thompson if (code & ~0x7f) {
206402ac6454SAndrew Thompson return (EINVAL);
206502ac6454SAndrew Thompson }
2066971bac5aSMichael kbd->kb_delay1 = kbdelays[(code >> 5) & 3];
2067971bac5aSMichael kbd->kb_delay2 = kbrates[code & 0x1f];
2068a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
206910063b79SOleksandr Tymoshenko if (sc->sc_evdev != NULL)
207010063b79SOleksandr Tymoshenko evdev_push_repeats(sc->sc_evdev, kbd);
207110063b79SOleksandr Tymoshenko #endif
207202ac6454SAndrew Thompson return (0);
207302ac6454SAndrew Thompson }
207402ac6454SAndrew Thompson
207502ac6454SAndrew Thompson #ifdef UKBD_EMULATE_ATSCANCODE
2076ded67349SBruce Evans static uint32_t
ukbd_atkeycode(int usbcode,const uint64_t * bitmap)2077d4028678SHans Petter Selasky ukbd_atkeycode(int usbcode, const uint64_t *bitmap)
2078ded67349SBruce Evans {
2079ded67349SBruce Evans uint32_t keycode;
2080ded67349SBruce Evans
2081ded67349SBruce Evans keycode = ukbd_trtab[KEY_INDEX(usbcode)];
2082d4028678SHans Petter Selasky
2083ded67349SBruce Evans /*
2084ded67349SBruce Evans * Translate Alt-PrintScreen to SysRq.
2085ded67349SBruce Evans *
2086ded67349SBruce Evans * Some or all AT keyboards connected through USB have already
2087ded67349SBruce Evans * mapped Alted PrintScreens to an unusual usbcode (0x8a).
2088ded67349SBruce Evans * ukbd_trtab translates this to 0x7e, and key2scan() would
2089ded67349SBruce Evans * translate that to 0x79 (Intl' 4). Assume that if we have
2090ded67349SBruce Evans * an Alted 0x7e here then it actually is an Alted PrintScreen.
2091ded67349SBruce Evans *
2092ded67349SBruce Evans * The usual usbcode for all PrintScreens is 0x46. ukbd_trtab
2093ded67349SBruce Evans * translates this to 0x5c, so the Alt check to classify 0x5c
2094ded67349SBruce Evans * is routine.
2095ded67349SBruce Evans */
2096ded67349SBruce Evans if ((keycode == 0x5c || keycode == 0x7e) &&
2097d4028678SHans Petter Selasky (UKBD_KEY_PRESSED(bitmap, 0xe2 /* ALT-L */) ||
2098d4028678SHans Petter Selasky UKBD_KEY_PRESSED(bitmap, 0xe6 /* ALT-R */)))
2099ded67349SBruce Evans return (0x54);
2100ded67349SBruce Evans return (keycode);
2101ded67349SBruce Evans }
2102ded67349SBruce Evans
210302ac6454SAndrew Thompson static int
ukbd_key2scan(struct ukbd_softc * sc,int code,const uint64_t * bitmap,int up)2104d4028678SHans Petter Selasky ukbd_key2scan(struct ukbd_softc *sc, int code, const uint64_t *bitmap, int up)
210502ac6454SAndrew Thompson {
210602ac6454SAndrew Thompson static const int scan[] = {
2107aea0c7f3SHiroki Sato /* 89 */
2108aea0c7f3SHiroki Sato 0x11c, /* Enter */
2109aea0c7f3SHiroki Sato /* 90-99 */
2110aea0c7f3SHiroki Sato 0x11d, /* Ctrl-R */
2111aea0c7f3SHiroki Sato 0x135, /* Divide */
21123f7880e2SBruce Evans 0x137, /* PrintScreen */
2113aea0c7f3SHiroki Sato 0x138, /* Alt-R */
2114aea0c7f3SHiroki Sato 0x147, /* Home */
2115aea0c7f3SHiroki Sato 0x148, /* Up */
2116aea0c7f3SHiroki Sato 0x149, /* PageUp */
2117aea0c7f3SHiroki Sato 0x14b, /* Left */
2118aea0c7f3SHiroki Sato 0x14d, /* Right */
2119aea0c7f3SHiroki Sato 0x14f, /* End */
2120aea0c7f3SHiroki Sato /* 100-109 */
2121aea0c7f3SHiroki Sato 0x150, /* Down */
2122aea0c7f3SHiroki Sato 0x151, /* PageDown */
2123aea0c7f3SHiroki Sato 0x152, /* Insert */
2124aea0c7f3SHiroki Sato 0x153, /* Delete */
2125ded67349SBruce Evans 0x146, /* Pause/Break */
2126aea0c7f3SHiroki Sato 0x15b, /* Win_L(Super_L) */
2127aea0c7f3SHiroki Sato 0x15c, /* Win_R(Super_R) */
2128aea0c7f3SHiroki Sato 0x15d, /* Application(Menu) */
2129aea0c7f3SHiroki Sato
213002ac6454SAndrew Thompson /* SUN TYPE 6 USB KEYBOARD */
2131aea0c7f3SHiroki Sato 0x168, /* Sun Type 6 Help */
2132aea0c7f3SHiroki Sato 0x15e, /* Sun Type 6 Stop */
2133aea0c7f3SHiroki Sato /* 110 - 119 */
2134aea0c7f3SHiroki Sato 0x15f, /* Sun Type 6 Again */
2135aea0c7f3SHiroki Sato 0x160, /* Sun Type 6 Props */
2136aea0c7f3SHiroki Sato 0x161, /* Sun Type 6 Undo */
2137aea0c7f3SHiroki Sato 0x162, /* Sun Type 6 Front */
2138aea0c7f3SHiroki Sato 0x163, /* Sun Type 6 Copy */
2139aea0c7f3SHiroki Sato 0x164, /* Sun Type 6 Open */
2140aea0c7f3SHiroki Sato 0x165, /* Sun Type 6 Paste */
2141aea0c7f3SHiroki Sato 0x166, /* Sun Type 6 Find */
2142aea0c7f3SHiroki Sato 0x167, /* Sun Type 6 Cut */
2143aea0c7f3SHiroki Sato 0x125, /* Sun Type 6 Mute */
2144db002659SHans Petter Selasky /* 120 - 130 */
2145aea0c7f3SHiroki Sato 0x11f, /* Sun Type 6 VolumeDown */
2146aea0c7f3SHiroki Sato 0x11e, /* Sun Type 6 VolumeUp */
2147aea0c7f3SHiroki Sato 0x120, /* Sun Type 6 PowerDown */
2148aea0c7f3SHiroki Sato
2149aea0c7f3SHiroki Sato /* Japanese 106/109 keyboard */
2150aea0c7f3SHiroki Sato 0x73, /* Keyboard Intl' 1 (backslash / underscore) */
2151aea0c7f3SHiroki Sato 0x70, /* Keyboard Intl' 2 (Katakana / Hiragana) */
2152aea0c7f3SHiroki Sato 0x7d, /* Keyboard Intl' 3 (Yen sign) (Not using in jp106/109) */
2153aea0c7f3SHiroki Sato 0x79, /* Keyboard Intl' 4 (Henkan) */
2154aea0c7f3SHiroki Sato 0x7b, /* Keyboard Intl' 5 (Muhenkan) */
2155aea0c7f3SHiroki Sato 0x5c, /* Keyboard Intl' 6 (Keypad ,) (For PC-9821 layout) */
2156db002659SHans Petter Selasky 0x71, /* Apple Keyboard JIS (Kana) */
2157db002659SHans Petter Selasky 0x72, /* Apple Keyboard JIS (Eisu) */
215802ac6454SAndrew Thompson };
215902ac6454SAndrew Thompson
2160432157dcSPedro F. Giffuni if ((code >= 89) && (code < (int)(89 + nitems(scan)))) {
2161aea0c7f3SHiroki Sato code = scan[code - 89];
216202ac6454SAndrew Thompson }
21633f7880e2SBruce Evans /* PrintScreen */
2164d4028678SHans Petter Selasky if (code == 0x137 && (!(
2165d4028678SHans Petter Selasky UKBD_KEY_PRESSED(bitmap, 0xe0 /* CTRL-L */) ||
2166d4028678SHans Petter Selasky UKBD_KEY_PRESSED(bitmap, 0xe4 /* CTRL-R */) ||
2167d4028678SHans Petter Selasky UKBD_KEY_PRESSED(bitmap, 0xe1 /* SHIFT-L */) ||
2168d4028678SHans Petter Selasky UKBD_KEY_PRESSED(bitmap, 0xe5 /* SHIFT-R */)))) {
21693f7880e2SBruce Evans code |= SCAN_PREFIX_SHIFT;
217002ac6454SAndrew Thompson }
21713f7880e2SBruce Evans /* Pause/Break */
2172d4028678SHans Petter Selasky if ((code == 0x146) && (!(
2173d4028678SHans Petter Selasky UKBD_KEY_PRESSED(bitmap, 0xe0 /* CTRL-L */) ||
2174d4028678SHans Petter Selasky UKBD_KEY_PRESSED(bitmap, 0xe4 /* CTRL-R */)))) {
21753f7880e2SBruce Evans code = (0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL);
217602ac6454SAndrew Thompson }
217702ac6454SAndrew Thompson code |= (up ? SCAN_RELEASE : SCAN_PRESS);
217802ac6454SAndrew Thompson
217902ac6454SAndrew Thompson if (code & SCAN_PREFIX) {
218002ac6454SAndrew Thompson if (code & SCAN_PREFIX_CTL) {
218102ac6454SAndrew Thompson /* Ctrl */
218202ac6454SAndrew Thompson sc->sc_buffered_char[0] = (0x1d | (code & SCAN_RELEASE));
218302ac6454SAndrew Thompson sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX);
218402ac6454SAndrew Thompson } else if (code & SCAN_PREFIX_SHIFT) {
218502ac6454SAndrew Thompson /* Shift */
218602ac6454SAndrew Thompson sc->sc_buffered_char[0] = (0x2a | (code & SCAN_RELEASE));
218702ac6454SAndrew Thompson sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX_SHIFT);
218802ac6454SAndrew Thompson } else {
218902ac6454SAndrew Thompson sc->sc_buffered_char[0] = (code & ~SCAN_PREFIX);
219002ac6454SAndrew Thompson sc->sc_buffered_char[1] = 0;
219102ac6454SAndrew Thompson }
219202ac6454SAndrew Thompson return ((code & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
219302ac6454SAndrew Thompson }
219402ac6454SAndrew Thompson return (code);
219502ac6454SAndrew Thompson
219602ac6454SAndrew Thompson }
219702ac6454SAndrew Thompson
219802ac6454SAndrew Thompson #endif /* UKBD_EMULATE_ATSCANCODE */
219902ac6454SAndrew Thompson
2200a8675caeSAndrew Thompson static keyboard_switch_t ukbdsw = {
220102ac6454SAndrew Thompson .probe = &ukbd__probe,
220202ac6454SAndrew Thompson .init = &ukbd_init,
220302ac6454SAndrew Thompson .term = &ukbd_term,
220402ac6454SAndrew Thompson .intr = &ukbd_intr,
220502ac6454SAndrew Thompson .test_if = &ukbd_test_if,
220602ac6454SAndrew Thompson .enable = &ukbd_enable,
220702ac6454SAndrew Thompson .disable = &ukbd_disable,
220802ac6454SAndrew Thompson .read = &ukbd_read,
220902ac6454SAndrew Thompson .check = &ukbd_check,
221002ac6454SAndrew Thompson .read_char = &ukbd_read_char,
221102ac6454SAndrew Thompson .check_char = &ukbd_check_char,
221202ac6454SAndrew Thompson .ioctl = &ukbd_ioctl,
221302ac6454SAndrew Thompson .lock = &ukbd_lock,
221402ac6454SAndrew Thompson .clear_state = &ukbd_clear_state,
221502ac6454SAndrew Thompson .get_state = &ukbd_get_state,
221602ac6454SAndrew Thompson .set_state = &ukbd_set_state,
221702ac6454SAndrew Thompson .poll = &ukbd_poll,
221802ac6454SAndrew Thompson };
221902ac6454SAndrew Thompson
222002ac6454SAndrew Thompson KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure);
222102ac6454SAndrew Thompson
222202ac6454SAndrew Thompson static int
ukbd_driver_load(module_t mod,int what,void * arg)222302ac6454SAndrew Thompson ukbd_driver_load(module_t mod, int what, void *arg)
222402ac6454SAndrew Thompson {
222502ac6454SAndrew Thompson switch (what) {
222602ac6454SAndrew Thompson case MOD_LOAD:
222702ac6454SAndrew Thompson kbd_add_driver(&ukbd_kbd_driver);
222802ac6454SAndrew Thompson break;
222902ac6454SAndrew Thompson case MOD_UNLOAD:
223002ac6454SAndrew Thompson kbd_delete_driver(&ukbd_kbd_driver);
223102ac6454SAndrew Thompson break;
223202ac6454SAndrew Thompson }
223302ac6454SAndrew Thompson return (0);
223402ac6454SAndrew Thompson }
223502ac6454SAndrew Thompson
223602ac6454SAndrew Thompson static device_method_t ukbd_methods[] = {
223702ac6454SAndrew Thompson DEVMETHOD(device_probe, ukbd_probe),
223802ac6454SAndrew Thompson DEVMETHOD(device_attach, ukbd_attach),
223902ac6454SAndrew Thompson DEVMETHOD(device_detach, ukbd_detach),
224002ac6454SAndrew Thompson DEVMETHOD(device_resume, ukbd_resume),
224161bfd867SSofian Brabez
224261bfd867SSofian Brabez DEVMETHOD_END
224302ac6454SAndrew Thompson };
224402ac6454SAndrew Thompson
224502ac6454SAndrew Thompson static driver_t ukbd_driver = {
224602ac6454SAndrew Thompson .name = "ukbd",
224702ac6454SAndrew Thompson .methods = ukbd_methods,
224802ac6454SAndrew Thompson .size = sizeof(struct ukbd_softc),
224902ac6454SAndrew Thompson };
225002ac6454SAndrew Thompson
2251bc9372d7SJohn Baldwin DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_driver_load, NULL);
225202ac6454SAndrew Thompson MODULE_DEPEND(ukbd, usb, 1, 1, 1);
225367de2db2SVladimir Kondratyev MODULE_DEPEND(ukbd, hid, 1, 1, 1);
2254a6b15a34SOleksandr Tymoshenko #ifdef EVDEV_SUPPORT
2255fa26e8edSOleksandr Tymoshenko MODULE_DEPEND(ukbd, evdev, 1, 1, 1);
2256fa26e8edSOleksandr Tymoshenko #endif
2257910cb8feSAndrew Thompson MODULE_VERSION(ukbd, 1);
2258f809f280SWarner Losh USB_PNP_HOST_INFO(ukbd_devs);
2259