xref: /freebsd/sys/dev/usb/input/ukbd.c (revision 541e7a98b762916cd951a64dd7e77efd51dfa8dc)
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