xref: /freebsd/sys/dev/usb/gadget/g_keyboard.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
146902503SHans Petter Selasky /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
446902503SHans Petter Selasky  * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
546902503SHans Petter Selasky  *
646902503SHans Petter Selasky  * Redistribution and use in source and binary forms, with or without
746902503SHans Petter Selasky  * modification, are permitted provided that the following conditions
846902503SHans Petter Selasky  * are met:
946902503SHans Petter Selasky  * 1. Redistributions of source code must retain the above copyright
1046902503SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer.
1146902503SHans Petter Selasky  * 2. Redistributions in binary form must reproduce the above copyright
1246902503SHans Petter Selasky  *    notice, this list of conditions and the following disclaimer in the
1346902503SHans Petter Selasky  *    documentation and/or other materials provided with the distribution.
1446902503SHans Petter Selasky  *
1546902503SHans Petter Selasky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1646902503SHans Petter Selasky  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1746902503SHans Petter Selasky  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1846902503SHans Petter Selasky  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1946902503SHans Petter Selasky  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2046902503SHans Petter Selasky  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2146902503SHans Petter Selasky  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2246902503SHans Petter Selasky  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2346902503SHans Petter Selasky  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2446902503SHans Petter Selasky  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2546902503SHans Petter Selasky  * SUCH DAMAGE.
2646902503SHans Petter Selasky  */
2746902503SHans Petter Selasky 
2846902503SHans Petter Selasky /*
2946902503SHans Petter Selasky  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
3046902503SHans Petter Selasky  */
3146902503SHans Petter Selasky 
321ae56a1cSDavid E. O'Brien #include <sys/param.h>
3346902503SHans Petter Selasky #include <sys/stdint.h>
3446902503SHans Petter Selasky #include <sys/stddef.h>
3546902503SHans Petter Selasky #include <sys/queue.h>
3646902503SHans Petter Selasky #include <sys/systm.h>
3746902503SHans Petter Selasky #include <sys/kernel.h>
3846902503SHans Petter Selasky #include <sys/bus.h>
3946902503SHans Petter Selasky #include <sys/linker_set.h>
4046902503SHans Petter Selasky #include <sys/module.h>
4146902503SHans Petter Selasky #include <sys/lock.h>
4246902503SHans Petter Selasky #include <sys/mutex.h>
4346902503SHans Petter Selasky #include <sys/condvar.h>
4446902503SHans Petter Selasky #include <sys/sysctl.h>
4546902503SHans Petter Selasky #include <sys/sx.h>
4646902503SHans Petter Selasky #include <sys/unistd.h>
4746902503SHans Petter Selasky #include <sys/callout.h>
4846902503SHans Petter Selasky #include <sys/malloc.h>
4946902503SHans Petter Selasky #include <sys/priv.h>
5046902503SHans Petter Selasky 
5146902503SHans Petter Selasky #include <dev/usb/usb.h>
5246902503SHans Petter Selasky #include <dev/usb/usbdi.h>
5346902503SHans Petter Selasky #include <dev/usb/usbdi_util.h>
5446902503SHans Petter Selasky #include <dev/usb/usbhid.h>
5546902503SHans Petter Selasky #include "usb_if.h"
5646902503SHans Petter Selasky 
5746902503SHans Petter Selasky #define	USB_DEBUG_VAR g_keyboard_debug
5846902503SHans Petter Selasky #include <dev/usb/usb_debug.h>
5946902503SHans Petter Selasky 
6046902503SHans Petter Selasky #include <dev/usb/gadget/g_keyboard.h>
6146902503SHans Petter Selasky 
62f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, g_keyboard,
63f8d2b1f3SPawel Biernacki     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
64f8d2b1f3SPawel Biernacki     "USB keyboard gadget");
6546902503SHans Petter Selasky 
6646902503SHans Petter Selasky #ifdef USB_DEBUG
6746902503SHans Petter Selasky static int g_keyboard_debug = 0;
6846902503SHans Petter Selasky 
69ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_g_keyboard, OID_AUTO, debug, CTLFLAG_RWTUN,
7046902503SHans Petter Selasky     &g_keyboard_debug, 0, "Debug level");
7146902503SHans Petter Selasky #endif
7246902503SHans Petter Selasky 
7346902503SHans Petter Selasky static int g_keyboard_mode = 0;
7446902503SHans Petter Selasky 
75ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_g_keyboard, OID_AUTO, mode, CTLFLAG_RWTUN,
7646902503SHans Petter Selasky     &g_keyboard_mode, 0, "Mode selection");
7746902503SHans Petter Selasky 
7846902503SHans Petter Selasky static int g_keyboard_key_press_interval = 1000;
7946902503SHans Petter Selasky 
80ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_g_keyboard, OID_AUTO, key_press_interval, CTLFLAG_RWTUN,
8146902503SHans Petter Selasky     &g_keyboard_key_press_interval, 0, "Key Press Interval in milliseconds");
8246902503SHans Petter Selasky 
8346902503SHans Petter Selasky static char g_keyboard_key_press_pattern[G_KEYBOARD_MAX_STRLEN];
8446902503SHans Petter Selasky 
8546902503SHans Petter Selasky SYSCTL_STRING(_hw_usb_g_keyboard, OID_AUTO, key_press_pattern, CTLFLAG_RW,
8646902503SHans Petter Selasky     g_keyboard_key_press_pattern, sizeof(g_keyboard_key_press_pattern),
8746902503SHans Petter Selasky     "Key Press Patterns");
8846902503SHans Petter Selasky 
8946902503SHans Petter Selasky #define	UPROTO_BOOT_KEYBOARD 1
9046902503SHans Petter Selasky 
9146902503SHans Petter Selasky #define	G_KEYBOARD_NMOD                     8	/* units */
9246902503SHans Petter Selasky #define	G_KEYBOARD_NKEYCODE                 6	/* units */
9346902503SHans Petter Selasky 
9446902503SHans Petter Selasky struct g_keyboard_data {
9546902503SHans Petter Selasky 	uint8_t	modifiers;
9646902503SHans Petter Selasky #define	MOD_CONTROL_L	0x01
9746902503SHans Petter Selasky #define	MOD_CONTROL_R	0x10
9846902503SHans Petter Selasky #define	MOD_SHIFT_L	0x02
9946902503SHans Petter Selasky #define	MOD_SHIFT_R	0x20
10046902503SHans Petter Selasky #define	MOD_ALT_L	0x04
10146902503SHans Petter Selasky #define	MOD_ALT_R	0x40
10246902503SHans Petter Selasky #define	MOD_WIN_L	0x08
10346902503SHans Petter Selasky #define	MOD_WIN_R	0x80
10446902503SHans Petter Selasky 	uint8_t	reserved;
10546902503SHans Petter Selasky 	uint8_t	keycode[G_KEYBOARD_NKEYCODE];
10646902503SHans Petter Selasky };
10746902503SHans Petter Selasky 
10846902503SHans Petter Selasky enum {
10946902503SHans Petter Selasky 	G_KEYBOARD_INTR_DT,
11046902503SHans Petter Selasky 	G_KEYBOARD_N_TRANSFER,
11146902503SHans Petter Selasky };
11246902503SHans Petter Selasky 
11346902503SHans Petter Selasky struct g_keyboard_softc {
11446902503SHans Petter Selasky 	struct mtx sc_mtx;
11546902503SHans Petter Selasky 	struct usb_callout sc_callout;
11646902503SHans Petter Selasky 	struct g_keyboard_data sc_data[2];
11746902503SHans Petter Selasky 	struct usb_xfer *sc_xfer[G_KEYBOARD_N_TRANSFER];
11846902503SHans Petter Selasky 
11946902503SHans Petter Selasky 	int	sc_mode;
12046902503SHans Petter Selasky 	int	sc_state;
12146902503SHans Petter Selasky 	int	sc_pattern_len;
12246902503SHans Petter Selasky 
12346902503SHans Petter Selasky 	char	sc_pattern[G_KEYBOARD_MAX_STRLEN];
12446902503SHans Petter Selasky 
12546902503SHans Petter Selasky 	uint8_t	sc_led_state[4];
12646902503SHans Petter Selasky };
12746902503SHans Petter Selasky 
12846902503SHans Petter Selasky static device_probe_t g_keyboard_probe;
12946902503SHans Petter Selasky static device_attach_t g_keyboard_attach;
13046902503SHans Petter Selasky static device_detach_t g_keyboard_detach;
13146902503SHans Petter Selasky static usb_handle_request_t g_keyboard_handle_request;
13246902503SHans Petter Selasky static usb_callback_t g_keyboard_intr_callback;
13346902503SHans Petter Selasky 
13446902503SHans Petter Selasky static device_method_t g_keyboard_methods[] = {
13546902503SHans Petter Selasky 	/* USB interface */
13646902503SHans Petter Selasky 	DEVMETHOD(usb_handle_request, g_keyboard_handle_request),
13746902503SHans Petter Selasky 
13846902503SHans Petter Selasky 	/* Device interface */
13946902503SHans Petter Selasky 	DEVMETHOD(device_probe, g_keyboard_probe),
14046902503SHans Petter Selasky 	DEVMETHOD(device_attach, g_keyboard_attach),
14146902503SHans Petter Selasky 	DEVMETHOD(device_detach, g_keyboard_detach),
14246902503SHans Petter Selasky 
14346902503SHans Petter Selasky 	DEVMETHOD_END
14446902503SHans Petter Selasky };
14546902503SHans Petter Selasky 
14646902503SHans Petter Selasky static driver_t g_keyboard_driver = {
14746902503SHans Petter Selasky 	.name = "g_keyboard",
14846902503SHans Petter Selasky 	.methods = g_keyboard_methods,
14946902503SHans Petter Selasky 	.size = sizeof(struct g_keyboard_softc),
15046902503SHans Petter Selasky };
15146902503SHans Petter Selasky 
152bc9372d7SJohn Baldwin DRIVER_MODULE(g_keyboard, uhub, g_keyboard_driver, 0, 0);
15346902503SHans Petter Selasky MODULE_DEPEND(g_keyboard, usb, 1, 1, 1);
15446902503SHans Petter Selasky 
15546902503SHans Petter Selasky static const struct usb_config g_keyboard_config[G_KEYBOARD_N_TRANSFER] = {
15646902503SHans Petter Selasky 	[G_KEYBOARD_INTR_DT] = {
15746902503SHans Petter Selasky 		.type = UE_INTERRUPT,
15846902503SHans Petter Selasky 		.endpoint = UE_ADDR_ANY,
15946902503SHans Petter Selasky 		.direction = UE_DIR_IN,
16046902503SHans Petter Selasky 		.flags = {.ext_buffer = 1,.pipe_bof = 1,},
16146902503SHans Petter Selasky 		.bufsize = sizeof(struct g_keyboard_data),
16246902503SHans Petter Selasky 		.callback = &g_keyboard_intr_callback,
16346902503SHans Petter Selasky 		.frames = 2,
16446902503SHans Petter Selasky 		.usb_mode = USB_MODE_DEVICE,
16546902503SHans Petter Selasky 	},
16646902503SHans Petter Selasky };
16746902503SHans Petter Selasky 
16846902503SHans Petter Selasky static void g_keyboard_timeout(void *arg);
16946902503SHans Petter Selasky 
17046902503SHans Petter Selasky static void
g_keyboard_timeout_reset(struct g_keyboard_softc * sc)17146902503SHans Petter Selasky g_keyboard_timeout_reset(struct g_keyboard_softc *sc)
17246902503SHans Petter Selasky {
17346902503SHans Petter Selasky 	int i = g_keyboard_key_press_interval;
17446902503SHans Petter Selasky 
17546902503SHans Petter Selasky 	if (i <= 0)
17646902503SHans Petter Selasky 		i = 1;
17746902503SHans Petter Selasky 	else if (i > 1023)
17846902503SHans Petter Selasky 		i = 1023;
17946902503SHans Petter Selasky 
18046902503SHans Petter Selasky 	i = USB_MS_TO_TICKS(i);
18146902503SHans Petter Selasky 
18246902503SHans Petter Selasky 	usb_callout_reset(&sc->sc_callout, i, &g_keyboard_timeout, sc);
18346902503SHans Petter Selasky }
18446902503SHans Petter Selasky 
18546902503SHans Petter Selasky static void
g_keyboard_timeout(void * arg)18646902503SHans Petter Selasky g_keyboard_timeout(void *arg)
18746902503SHans Petter Selasky {
18846902503SHans Petter Selasky 	struct g_keyboard_softc *sc = arg;
18946902503SHans Petter Selasky 
19046902503SHans Petter Selasky 	sc->sc_mode = g_keyboard_mode;
19146902503SHans Petter Selasky 
19246902503SHans Petter Selasky 	memcpy(sc->sc_pattern, g_keyboard_key_press_pattern, sizeof(sc->sc_pattern));
19346902503SHans Petter Selasky 
19446902503SHans Petter Selasky 	sc->sc_pattern[G_KEYBOARD_MAX_STRLEN - 1] = 0;
19546902503SHans Petter Selasky 
19646902503SHans Petter Selasky 	sc->sc_pattern_len = strlen(sc->sc_pattern);
19746902503SHans Petter Selasky 
19846902503SHans Petter Selasky 	DPRINTFN(11, "Timeout %p\n", sc->sc_xfer[G_KEYBOARD_INTR_DT]);
19946902503SHans Petter Selasky 
20046902503SHans Petter Selasky 	usbd_transfer_start(sc->sc_xfer[G_KEYBOARD_INTR_DT]);
20146902503SHans Petter Selasky 
20246902503SHans Petter Selasky 	g_keyboard_timeout_reset(sc);
20346902503SHans Petter Selasky }
20446902503SHans Petter Selasky 
20546902503SHans Petter Selasky static int
g_keyboard_probe(device_t dev)20646902503SHans Petter Selasky g_keyboard_probe(device_t dev)
20746902503SHans Petter Selasky {
20846902503SHans Petter Selasky 	struct usb_attach_arg *uaa = device_get_ivars(dev);
20946902503SHans Petter Selasky 
21046902503SHans Petter Selasky 	DPRINTFN(11, "\n");
21146902503SHans Petter Selasky 
21246902503SHans Petter Selasky 	if (uaa->usb_mode != USB_MODE_DEVICE)
21346902503SHans Petter Selasky 		return (ENXIO);
21446902503SHans Petter Selasky 
21546902503SHans Petter Selasky 	if ((uaa->info.bInterfaceClass == UICLASS_HID) &&
21646902503SHans Petter Selasky 	    (uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
21746902503SHans Petter Selasky 	    (uaa->info.bInterfaceProtocol == UPROTO_BOOT_KEYBOARD))
21846902503SHans Petter Selasky 		return (0);
21946902503SHans Petter Selasky 
22046902503SHans Petter Selasky 	return (ENXIO);
22146902503SHans Petter Selasky }
22246902503SHans Petter Selasky 
22346902503SHans Petter Selasky static int
g_keyboard_attach(device_t dev)22446902503SHans Petter Selasky g_keyboard_attach(device_t dev)
22546902503SHans Petter Selasky {
22646902503SHans Petter Selasky 	struct g_keyboard_softc *sc = device_get_softc(dev);
22746902503SHans Petter Selasky 	struct usb_attach_arg *uaa = device_get_ivars(dev);
22846902503SHans Petter Selasky 	int error;
22946902503SHans Petter Selasky 
23046902503SHans Petter Selasky 	DPRINTFN(11, "\n");
23146902503SHans Petter Selasky 
23246902503SHans Petter Selasky 	device_set_usb_desc(dev);
23346902503SHans Petter Selasky 
23446902503SHans Petter Selasky 	mtx_init(&sc->sc_mtx, "g_keyboard", NULL, MTX_DEF);
23546902503SHans Petter Selasky 
23646902503SHans Petter Selasky 	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
23746902503SHans Petter Selasky 
23846902503SHans Petter Selasky 	sc->sc_mode = G_KEYBOARD_MODE_SILENT;
23946902503SHans Petter Selasky 
24046902503SHans Petter Selasky 	error = usbd_transfer_setup(uaa->device,
24146902503SHans Petter Selasky 	    &uaa->info.bIfaceIndex, sc->sc_xfer, g_keyboard_config,
24246902503SHans Petter Selasky 	    G_KEYBOARD_N_TRANSFER, sc, &sc->sc_mtx);
24346902503SHans Petter Selasky 
24446902503SHans Petter Selasky 	if (error) {
24546902503SHans Petter Selasky 		DPRINTF("error=%s\n", usbd_errstr(error));
24646902503SHans Petter Selasky 		goto detach;
24746902503SHans Petter Selasky 	}
24846902503SHans Petter Selasky 	mtx_lock(&sc->sc_mtx);
24946902503SHans Petter Selasky 	g_keyboard_timeout_reset(sc);
25046902503SHans Petter Selasky 	mtx_unlock(&sc->sc_mtx);
25146902503SHans Petter Selasky 
25246902503SHans Petter Selasky 	return (0);			/* success */
25346902503SHans Petter Selasky 
25446902503SHans Petter Selasky detach:
25546902503SHans Petter Selasky 	g_keyboard_detach(dev);
25646902503SHans Petter Selasky 
25746902503SHans Petter Selasky 	return (ENXIO);			/* error */
25846902503SHans Petter Selasky }
25946902503SHans Petter Selasky 
26046902503SHans Petter Selasky static int
g_keyboard_detach(device_t dev)26146902503SHans Petter Selasky g_keyboard_detach(device_t dev)
26246902503SHans Petter Selasky {
26346902503SHans Petter Selasky 	struct g_keyboard_softc *sc = device_get_softc(dev);
26446902503SHans Petter Selasky 
26546902503SHans Petter Selasky 	DPRINTF("\n");
26646902503SHans Petter Selasky 
26746902503SHans Petter Selasky 	mtx_lock(&sc->sc_mtx);
26846902503SHans Petter Selasky 	usb_callout_stop(&sc->sc_callout);
26946902503SHans Petter Selasky 	mtx_unlock(&sc->sc_mtx);
27046902503SHans Petter Selasky 
27146902503SHans Petter Selasky 	usbd_transfer_unsetup(sc->sc_xfer, G_KEYBOARD_N_TRANSFER);
27246902503SHans Petter Selasky 
27346902503SHans Petter Selasky 	usb_callout_drain(&sc->sc_callout);
27446902503SHans Petter Selasky 
27546902503SHans Petter Selasky 	mtx_destroy(&sc->sc_mtx);
27646902503SHans Petter Selasky 
27746902503SHans Petter Selasky 	return (0);
27846902503SHans Petter Selasky }
27946902503SHans Petter Selasky 
28046902503SHans Petter Selasky static uint8_t
g_keyboard_get_keycode(struct g_keyboard_softc * sc,int index)28146902503SHans Petter Selasky g_keyboard_get_keycode(struct g_keyboard_softc *sc, int index)
28246902503SHans Petter Selasky {
28346902503SHans Petter Selasky 	int key;
28446902503SHans Petter Selasky 	int mod = sc->sc_pattern_len;
28546902503SHans Petter Selasky 
28646902503SHans Petter Selasky 	if (mod == 0)
28746902503SHans Petter Selasky 		index = 0;
28846902503SHans Petter Selasky 	else
28946902503SHans Petter Selasky 		index %= mod;
29046902503SHans Petter Selasky 
29146902503SHans Petter Selasky 	if ((index >= 0) && (index < sc->sc_pattern_len))
29246902503SHans Petter Selasky 		key = sc->sc_pattern[index];
29346902503SHans Petter Selasky 	else
29446902503SHans Petter Selasky 		key = 'a';
29546902503SHans Petter Selasky 
29646902503SHans Petter Selasky 	if (key >= 'a' && key <= 'z')
29746902503SHans Petter Selasky 		return (key - 'a' + 0x04);
29846902503SHans Petter Selasky 	else
29946902503SHans Petter Selasky 		return (0x04);
30046902503SHans Petter Selasky }
30146902503SHans Petter Selasky 
30246902503SHans Petter Selasky static void
g_keyboard_intr_callback(struct usb_xfer * xfer,usb_error_t error)30346902503SHans Petter Selasky g_keyboard_intr_callback(struct usb_xfer *xfer, usb_error_t error)
30446902503SHans Petter Selasky {
30546902503SHans Petter Selasky 	struct g_keyboard_softc *sc = usbd_xfer_softc(xfer);
30646902503SHans Petter Selasky 	int actlen;
30746902503SHans Petter Selasky 	int aframes;
30846902503SHans Petter Selasky 
30946902503SHans Petter Selasky 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
31046902503SHans Petter Selasky 
31146902503SHans Petter Selasky 	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
31246902503SHans Petter Selasky 	    USB_GET_STATE(xfer), aframes, actlen);
31346902503SHans Petter Selasky 
31446902503SHans Petter Selasky 	switch (USB_GET_STATE(xfer)) {
31546902503SHans Petter Selasky 	case USB_ST_TRANSFERRED:
31646902503SHans Petter Selasky 		break;
31746902503SHans Petter Selasky 
31846902503SHans Petter Selasky 	case USB_ST_SETUP:
31946902503SHans Petter Selasky tr_setup:
32046902503SHans Petter Selasky 		if (sc->sc_mode == G_KEYBOARD_MODE_SILENT) {
32146902503SHans Petter Selasky 			memset(&sc->sc_data, 0, sizeof(sc->sc_data));
32246902503SHans Petter Selasky 			usbd_xfer_set_frame_data(xfer, 0, &sc->sc_data[0], sizeof(sc->sc_data[0]));
32346902503SHans Petter Selasky 			usbd_xfer_set_frame_data(xfer, 1, &sc->sc_data[1], sizeof(sc->sc_data[1]));
32446902503SHans Petter Selasky 			usbd_xfer_set_frames(xfer, 2);
32546902503SHans Petter Selasky 			usbd_transfer_submit(xfer);
32646902503SHans Petter Selasky 
32746902503SHans Petter Selasky 		} else if (sc->sc_mode == G_KEYBOARD_MODE_PATTERN) {
32846902503SHans Petter Selasky 			memset(&sc->sc_data, 0, sizeof(sc->sc_data));
32946902503SHans Petter Selasky 
33046902503SHans Petter Selasky 			if ((sc->sc_state < 0) || (sc->sc_state >= G_KEYBOARD_MAX_STRLEN))
33146902503SHans Petter Selasky 				sc->sc_state = 0;
33246902503SHans Petter Selasky 
33346902503SHans Petter Selasky 			switch (sc->sc_state % 6) {
33446902503SHans Petter Selasky 			case 0:
33546902503SHans Petter Selasky 				sc->sc_data[0].keycode[0] =
33646902503SHans Petter Selasky 				    g_keyboard_get_keycode(sc, sc->sc_state + 0);
33746902503SHans Petter Selasky 			case 1:
33846902503SHans Petter Selasky 				sc->sc_data[0].keycode[1] =
33946902503SHans Petter Selasky 				    g_keyboard_get_keycode(sc, sc->sc_state + 1);
34046902503SHans Petter Selasky 			case 2:
34146902503SHans Petter Selasky 				sc->sc_data[0].keycode[2] =
34246902503SHans Petter Selasky 				    g_keyboard_get_keycode(sc, sc->sc_state + 2);
34346902503SHans Petter Selasky 			case 3:
34446902503SHans Petter Selasky 				sc->sc_data[0].keycode[3] =
34546902503SHans Petter Selasky 				    g_keyboard_get_keycode(sc, sc->sc_state + 3);
34646902503SHans Petter Selasky 			case 4:
34746902503SHans Petter Selasky 				sc->sc_data[0].keycode[4] =
34846902503SHans Petter Selasky 				    g_keyboard_get_keycode(sc, sc->sc_state + 4);
34946902503SHans Petter Selasky 			default:
35046902503SHans Petter Selasky 				sc->sc_data[0].keycode[5] =
35146902503SHans Petter Selasky 				    g_keyboard_get_keycode(sc, sc->sc_state + 5);
35246902503SHans Petter Selasky 			}
35346902503SHans Petter Selasky 
35446902503SHans Petter Selasky 			sc->sc_state++;
35546902503SHans Petter Selasky 
35646902503SHans Petter Selasky 			usbd_xfer_set_frame_data(xfer, 0, &sc->sc_data[0], sizeof(sc->sc_data[0]));
35746902503SHans Petter Selasky 			usbd_xfer_set_frame_data(xfer, 1, &sc->sc_data[1], sizeof(sc->sc_data[1]));
35846902503SHans Petter Selasky 			usbd_xfer_set_frames(xfer, 2);
35946902503SHans Petter Selasky 			usbd_transfer_submit(xfer);
36046902503SHans Petter Selasky 		}
36146902503SHans Petter Selasky 		break;
36246902503SHans Petter Selasky 
36346902503SHans Petter Selasky 	default:			/* Error */
36446902503SHans Petter Selasky 		DPRINTF("error=%s\n", usbd_errstr(error));
36546902503SHans Petter Selasky 
36646902503SHans Petter Selasky 		if (error != USB_ERR_CANCELLED) {
36746902503SHans Petter Selasky 			/* try to clear stall first */
36846902503SHans Petter Selasky 			usbd_xfer_set_stall(xfer);
36946902503SHans Petter Selasky 			goto tr_setup;
37046902503SHans Petter Selasky 		}
37146902503SHans Petter Selasky 		break;
37246902503SHans Petter Selasky 	}
37346902503SHans Petter Selasky }
37446902503SHans Petter Selasky 
37546902503SHans Petter Selasky static int
g_keyboard_handle_request(device_t dev,const void * preq,void ** pptr,uint16_t * plen,uint16_t offset,uint8_t * pstate)37646902503SHans Petter Selasky g_keyboard_handle_request(device_t dev,
37746902503SHans Petter Selasky     const void *preq, void **pptr, uint16_t *plen,
37846902503SHans Petter Selasky     uint16_t offset, uint8_t *pstate)
37946902503SHans Petter Selasky {
38046902503SHans Petter Selasky 	struct g_keyboard_softc *sc = device_get_softc(dev);
38146902503SHans Petter Selasky 	const struct usb_device_request *req = preq;
38246902503SHans Petter Selasky 	uint8_t is_complete = *pstate;
38346902503SHans Petter Selasky 
38446902503SHans Petter Selasky 	if (!is_complete) {
38546902503SHans Petter Selasky 		if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
38646902503SHans Petter Selasky 		    (req->bRequest == UR_SET_REPORT) &&
38746902503SHans Petter Selasky 		    (req->wValue[0] == 0x00) &&
38846902503SHans Petter Selasky 		    (req->wValue[1] == 0x02)) {
38946902503SHans Petter Selasky 			if (offset == 0) {
39046902503SHans Petter Selasky 				*plen = sizeof(sc->sc_led_state);
39146902503SHans Petter Selasky 				*pptr = &sc->sc_led_state;
39246902503SHans Petter Selasky 			} else {
39346902503SHans Petter Selasky 				*plen = 0;
39446902503SHans Petter Selasky 			}
39546902503SHans Petter Selasky 			return (0);
39646902503SHans Petter Selasky 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
39746902503SHans Petter Selasky 			    (req->bRequest == UR_SET_PROTOCOL) &&
39846902503SHans Petter Selasky 			    (req->wValue[0] == 0x00) &&
39946902503SHans Petter Selasky 		    (req->wValue[1] == 0x00)) {
40046902503SHans Petter Selasky 			*plen = 0;
40146902503SHans Petter Selasky 			return (0);
40246902503SHans Petter Selasky 		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
40346902503SHans Petter Selasky 		    (req->bRequest == UR_SET_IDLE)) {
40446902503SHans Petter Selasky 			*plen = 0;
40546902503SHans Petter Selasky 			return (0);
40646902503SHans Petter Selasky 		}
40746902503SHans Petter Selasky 	}
40846902503SHans Petter Selasky 	return (ENXIO);			/* use builtin handler */
40946902503SHans Petter Selasky }
410