1253544Shselasky/*-
2253544Shselasky * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3253544Shselasky *
4253544Shselasky * Redistribution and use in source and binary forms, with or without
5253544Shselasky * modification, are permitted provided that the following conditions
6253544Shselasky * are met:
7253544Shselasky * 1. Redistributions of source code must retain the above copyright
8253544Shselasky *    notice, this list of conditions and the following disclaimer.
9253544Shselasky * 2. Redistributions in binary form must reproduce the above copyright
10253544Shselasky *    notice, this list of conditions and the following disclaimer in the
11253544Shselasky *    documentation and/or other materials provided with the distribution.
12253544Shselasky *
13253544Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14253544Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15253544Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16253544Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17253544Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18253544Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19253544Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20253544Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21253544Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22253544Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23253544Shselasky * SUCH DAMAGE.
24253544Shselasky */
25253544Shselasky
26253544Shselasky/*
27253544Shselasky * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
28253544Shselasky */
29253544Shselasky
30253618Sobrien#include <sys/param.h>
31253618Sobrien__FBSDID("$FreeBSD$");
32253618Sobrien
33253544Shselasky#include <sys/stdint.h>
34253544Shselasky#include <sys/stddef.h>
35253544Shselasky#include <sys/queue.h>
36253544Shselasky#include <sys/systm.h>
37253544Shselasky#include <sys/kernel.h>
38253544Shselasky#include <sys/bus.h>
39253544Shselasky#include <sys/linker_set.h>
40253544Shselasky#include <sys/module.h>
41253544Shselasky#include <sys/lock.h>
42253544Shselasky#include <sys/mutex.h>
43253544Shselasky#include <sys/condvar.h>
44253544Shselasky#include <sys/sysctl.h>
45253544Shselasky#include <sys/sx.h>
46253544Shselasky#include <sys/unistd.h>
47253544Shselasky#include <sys/callout.h>
48253544Shselasky#include <sys/malloc.h>
49253544Shselasky#include <sys/priv.h>
50253544Shselasky
51253544Shselasky#include <dev/usb/usb.h>
52253544Shselasky#include <dev/usb/usbdi.h>
53253544Shselasky#include <dev/usb/usbdi_util.h>
54253544Shselasky#include <dev/usb/usbhid.h>
55253544Shselasky#include "usb_if.h"
56253544Shselasky
57253544Shselasky#define	USB_DEBUG_VAR g_keyboard_debug
58253544Shselasky#include <dev/usb/usb_debug.h>
59253544Shselasky
60253544Shselasky#include <dev/usb/gadget/g_keyboard.h>
61253544Shselasky
62253544Shselaskystatic SYSCTL_NODE(_hw_usb, OID_AUTO, g_keyboard, CTLFLAG_RW, 0, "USB keyboard gadget");
63253544Shselasky
64253544Shselasky#ifdef USB_DEBUG
65253544Shselaskystatic int g_keyboard_debug = 0;
66253544Shselasky
67276701ShselaskySYSCTL_INT(_hw_usb_g_keyboard, OID_AUTO, debug, CTLFLAG_RWTUN,
68253544Shselasky    &g_keyboard_debug, 0, "Debug level");
69253544Shselasky#endif
70253544Shselasky
71253544Shselaskystatic int g_keyboard_mode = 0;
72253544Shselasky
73276701ShselaskySYSCTL_INT(_hw_usb_g_keyboard, OID_AUTO, mode, CTLFLAG_RWTUN,
74253544Shselasky    &g_keyboard_mode, 0, "Mode selection");
75253544Shselasky
76253544Shselaskystatic int g_keyboard_key_press_interval = 1000;
77253544Shselasky
78276701ShselaskySYSCTL_INT(_hw_usb_g_keyboard, OID_AUTO, key_press_interval, CTLFLAG_RWTUN,
79253544Shselasky    &g_keyboard_key_press_interval, 0, "Key Press Interval in milliseconds");
80253544Shselasky
81253544Shselaskystatic char g_keyboard_key_press_pattern[G_KEYBOARD_MAX_STRLEN];
82253544Shselasky
83253544ShselaskySYSCTL_STRING(_hw_usb_g_keyboard, OID_AUTO, key_press_pattern, CTLFLAG_RW,
84253544Shselasky    g_keyboard_key_press_pattern, sizeof(g_keyboard_key_press_pattern),
85253544Shselasky    "Key Press Patterns");
86253544Shselasky
87253544Shselasky#define	UPROTO_BOOT_KEYBOARD 1
88253544Shselasky
89253544Shselasky#define	G_KEYBOARD_NMOD                     8	/* units */
90253544Shselasky#define	G_KEYBOARD_NKEYCODE                 6	/* units */
91253544Shselasky
92253544Shselaskystruct g_keyboard_data {
93253544Shselasky	uint8_t	modifiers;
94253544Shselasky#define	MOD_CONTROL_L	0x01
95253544Shselasky#define	MOD_CONTROL_R	0x10
96253544Shselasky#define	MOD_SHIFT_L	0x02
97253544Shselasky#define	MOD_SHIFT_R	0x20
98253544Shselasky#define	MOD_ALT_L	0x04
99253544Shselasky#define	MOD_ALT_R	0x40
100253544Shselasky#define	MOD_WIN_L	0x08
101253544Shselasky#define	MOD_WIN_R	0x80
102253544Shselasky	uint8_t	reserved;
103253544Shselasky	uint8_t	keycode[G_KEYBOARD_NKEYCODE];
104253544Shselasky};
105253544Shselasky
106253544Shselaskyenum {
107253544Shselasky	G_KEYBOARD_INTR_DT,
108253544Shselasky	G_KEYBOARD_N_TRANSFER,
109253544Shselasky};
110253544Shselasky
111253544Shselaskystruct g_keyboard_softc {
112253544Shselasky	struct mtx sc_mtx;
113253544Shselasky	struct usb_callout sc_callout;
114253544Shselasky	struct g_keyboard_data sc_data[2];
115253544Shselasky	struct usb_xfer *sc_xfer[G_KEYBOARD_N_TRANSFER];
116253544Shselasky
117253544Shselasky	int	sc_mode;
118253544Shselasky	int	sc_state;
119253544Shselasky	int	sc_pattern_len;
120253544Shselasky
121253544Shselasky	char	sc_pattern[G_KEYBOARD_MAX_STRLEN];
122253544Shselasky
123253544Shselasky	uint8_t	sc_led_state[4];
124253544Shselasky};
125253544Shselasky
126253544Shselaskystatic device_probe_t g_keyboard_probe;
127253544Shselaskystatic device_attach_t g_keyboard_attach;
128253544Shselaskystatic device_detach_t g_keyboard_detach;
129253544Shselaskystatic usb_handle_request_t g_keyboard_handle_request;
130253544Shselaskystatic usb_callback_t g_keyboard_intr_callback;
131253544Shselasky
132253544Shselaskystatic devclass_t g_keyboard_devclass;
133253544Shselasky
134253544Shselaskystatic device_method_t g_keyboard_methods[] = {
135253544Shselasky	/* USB interface */
136253544Shselasky	DEVMETHOD(usb_handle_request, g_keyboard_handle_request),
137253544Shselasky
138253544Shselasky	/* Device interface */
139253544Shselasky	DEVMETHOD(device_probe, g_keyboard_probe),
140253544Shselasky	DEVMETHOD(device_attach, g_keyboard_attach),
141253544Shselasky	DEVMETHOD(device_detach, g_keyboard_detach),
142253544Shselasky
143253544Shselasky	DEVMETHOD_END
144253544Shselasky};
145253544Shselasky
146253544Shselaskystatic driver_t g_keyboard_driver = {
147253544Shselasky	.name = "g_keyboard",
148253544Shselasky	.methods = g_keyboard_methods,
149253544Shselasky	.size = sizeof(struct g_keyboard_softc),
150253544Shselasky};
151253544Shselasky
152253544ShselaskyDRIVER_MODULE(g_keyboard, uhub, g_keyboard_driver, g_keyboard_devclass, 0, 0);
153253544ShselaskyMODULE_DEPEND(g_keyboard, usb, 1, 1, 1);
154253544Shselasky
155253544Shselaskystatic const struct usb_config g_keyboard_config[G_KEYBOARD_N_TRANSFER] = {
156253544Shselasky	[G_KEYBOARD_INTR_DT] = {
157253544Shselasky		.type = UE_INTERRUPT,
158253544Shselasky		.endpoint = UE_ADDR_ANY,
159253544Shselasky		.direction = UE_DIR_IN,
160253544Shselasky		.flags = {.ext_buffer = 1,.pipe_bof = 1,},
161253544Shselasky		.bufsize = sizeof(struct g_keyboard_data),
162253544Shselasky		.callback = &g_keyboard_intr_callback,
163253544Shselasky		.frames = 2,
164253544Shselasky		.usb_mode = USB_MODE_DEVICE,
165253544Shselasky	},
166253544Shselasky};
167253544Shselasky
168253544Shselaskystatic void g_keyboard_timeout(void *arg);
169253544Shselasky
170253544Shselaskystatic void
171253544Shselaskyg_keyboard_timeout_reset(struct g_keyboard_softc *sc)
172253544Shselasky{
173253544Shselasky	int i = g_keyboard_key_press_interval;
174253544Shselasky
175253544Shselasky	if (i <= 0)
176253544Shselasky		i = 1;
177253544Shselasky	else if (i > 1023)
178253544Shselasky		i = 1023;
179253544Shselasky
180253544Shselasky	i = USB_MS_TO_TICKS(i);
181253544Shselasky
182253544Shselasky	usb_callout_reset(&sc->sc_callout, i, &g_keyboard_timeout, sc);
183253544Shselasky}
184253544Shselasky
185253544Shselaskystatic void
186253544Shselaskyg_keyboard_timeout(void *arg)
187253544Shselasky{
188253544Shselasky	struct g_keyboard_softc *sc = arg;
189253544Shselasky
190253544Shselasky	sc->sc_mode = g_keyboard_mode;
191253544Shselasky
192253544Shselasky	memcpy(sc->sc_pattern, g_keyboard_key_press_pattern, sizeof(sc->sc_pattern));
193253544Shselasky
194253544Shselasky	sc->sc_pattern[G_KEYBOARD_MAX_STRLEN - 1] = 0;
195253544Shselasky
196253544Shselasky	sc->sc_pattern_len = strlen(sc->sc_pattern);
197253544Shselasky
198253544Shselasky	DPRINTFN(11, "Timeout %p\n", sc->sc_xfer[G_KEYBOARD_INTR_DT]);
199253544Shselasky
200253544Shselasky	usbd_transfer_start(sc->sc_xfer[G_KEYBOARD_INTR_DT]);
201253544Shselasky
202253544Shselasky	g_keyboard_timeout_reset(sc);
203253544Shselasky}
204253544Shselasky
205253544Shselaskystatic int
206253544Shselaskyg_keyboard_probe(device_t dev)
207253544Shselasky{
208253544Shselasky	struct usb_attach_arg *uaa = device_get_ivars(dev);
209253544Shselasky
210253544Shselasky	DPRINTFN(11, "\n");
211253544Shselasky
212253544Shselasky	if (uaa->usb_mode != USB_MODE_DEVICE)
213253544Shselasky		return (ENXIO);
214253544Shselasky
215253544Shselasky	if ((uaa->info.bInterfaceClass == UICLASS_HID) &&
216253544Shselasky	    (uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
217253544Shselasky	    (uaa->info.bInterfaceProtocol == UPROTO_BOOT_KEYBOARD))
218253544Shselasky		return (0);
219253544Shselasky
220253544Shselasky	return (ENXIO);
221253544Shselasky}
222253544Shselasky
223253544Shselaskystatic int
224253544Shselaskyg_keyboard_attach(device_t dev)
225253544Shselasky{
226253544Shselasky	struct g_keyboard_softc *sc = device_get_softc(dev);
227253544Shselasky	struct usb_attach_arg *uaa = device_get_ivars(dev);
228253544Shselasky	int error;
229253544Shselasky
230253544Shselasky	DPRINTFN(11, "\n");
231253544Shselasky
232253544Shselasky	device_set_usb_desc(dev);
233253544Shselasky
234253544Shselasky	mtx_init(&sc->sc_mtx, "g_keyboard", NULL, MTX_DEF);
235253544Shselasky
236253544Shselasky	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
237253544Shselasky
238253544Shselasky	sc->sc_mode = G_KEYBOARD_MODE_SILENT;
239253544Shselasky
240253544Shselasky	error = usbd_transfer_setup(uaa->device,
241253544Shselasky	    &uaa->info.bIfaceIndex, sc->sc_xfer, g_keyboard_config,
242253544Shselasky	    G_KEYBOARD_N_TRANSFER, sc, &sc->sc_mtx);
243253544Shselasky
244253544Shselasky	if (error) {
245253544Shselasky		DPRINTF("error=%s\n", usbd_errstr(error));
246253544Shselasky		goto detach;
247253544Shselasky	}
248253544Shselasky	mtx_lock(&sc->sc_mtx);
249253544Shselasky	g_keyboard_timeout_reset(sc);
250253544Shselasky	mtx_unlock(&sc->sc_mtx);
251253544Shselasky
252253544Shselasky	return (0);			/* success */
253253544Shselasky
254253544Shselaskydetach:
255253544Shselasky	g_keyboard_detach(dev);
256253544Shselasky
257253544Shselasky	return (ENXIO);			/* error */
258253544Shselasky}
259253544Shselasky
260253544Shselaskystatic int
261253544Shselaskyg_keyboard_detach(device_t dev)
262253544Shselasky{
263253544Shselasky	struct g_keyboard_softc *sc = device_get_softc(dev);
264253544Shselasky
265253544Shselasky	DPRINTF("\n");
266253544Shselasky
267253544Shselasky	mtx_lock(&sc->sc_mtx);
268253544Shselasky	usb_callout_stop(&sc->sc_callout);
269253544Shselasky	mtx_unlock(&sc->sc_mtx);
270253544Shselasky
271253544Shselasky	usbd_transfer_unsetup(sc->sc_xfer, G_KEYBOARD_N_TRANSFER);
272253544Shselasky
273253544Shselasky	usb_callout_drain(&sc->sc_callout);
274253544Shselasky
275253544Shselasky	mtx_destroy(&sc->sc_mtx);
276253544Shselasky
277253544Shselasky	return (0);
278253544Shselasky}
279253544Shselasky
280253544Shselaskystatic uint8_t
281253544Shselaskyg_keyboard_get_keycode(struct g_keyboard_softc *sc, int index)
282253544Shselasky{
283253544Shselasky	int key;
284253544Shselasky	int mod = sc->sc_pattern_len;
285253544Shselasky
286253544Shselasky	if (mod == 0)
287253544Shselasky		index = 0;
288253544Shselasky	else
289253544Shselasky		index %= mod;
290253544Shselasky
291253544Shselasky	if ((index >= 0) && (index < sc->sc_pattern_len))
292253544Shselasky		key = sc->sc_pattern[index];
293253544Shselasky	else
294253544Shselasky		key = 'a';
295253544Shselasky
296253544Shselasky	if (key >= 'a' && key <= 'z')
297253544Shselasky		return (key - 'a' + 0x04);
298253544Shselasky	else
299253544Shselasky		return (0x04);
300253544Shselasky}
301253544Shselasky
302253544Shselaskystatic void
303253544Shselaskyg_keyboard_intr_callback(struct usb_xfer *xfer, usb_error_t error)
304253544Shselasky{
305253544Shselasky	struct g_keyboard_softc *sc = usbd_xfer_softc(xfer);
306253544Shselasky	int actlen;
307253544Shselasky	int aframes;
308253544Shselasky
309253544Shselasky	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
310253544Shselasky
311253544Shselasky	DPRINTF("st=%d aframes=%d actlen=%d bytes\n",
312253544Shselasky	    USB_GET_STATE(xfer), aframes, actlen);
313253544Shselasky
314253544Shselasky	switch (USB_GET_STATE(xfer)) {
315253544Shselasky	case USB_ST_TRANSFERRED:
316253544Shselasky		break;
317253544Shselasky
318253544Shselasky	case USB_ST_SETUP:
319253544Shselaskytr_setup:
320253544Shselasky		if (sc->sc_mode == G_KEYBOARD_MODE_SILENT) {
321253544Shselasky			memset(&sc->sc_data, 0, sizeof(sc->sc_data));
322253544Shselasky			usbd_xfer_set_frame_data(xfer, 0, &sc->sc_data[0], sizeof(sc->sc_data[0]));
323253544Shselasky			usbd_xfer_set_frame_data(xfer, 1, &sc->sc_data[1], sizeof(sc->sc_data[1]));
324253544Shselasky			usbd_xfer_set_frames(xfer, 2);
325253544Shselasky			usbd_transfer_submit(xfer);
326253544Shselasky
327253544Shselasky		} else if (sc->sc_mode == G_KEYBOARD_MODE_PATTERN) {
328253544Shselasky
329253544Shselasky			memset(&sc->sc_data, 0, sizeof(sc->sc_data));
330253544Shselasky
331253544Shselasky			if ((sc->sc_state < 0) || (sc->sc_state >= G_KEYBOARD_MAX_STRLEN))
332253544Shselasky				sc->sc_state = 0;
333253544Shselasky
334253544Shselasky			switch (sc->sc_state % 6) {
335253544Shselasky			case 0:
336253544Shselasky				sc->sc_data[0].keycode[0] =
337253544Shselasky				    g_keyboard_get_keycode(sc, sc->sc_state + 0);
338253544Shselasky			case 1:
339253544Shselasky				sc->sc_data[0].keycode[1] =
340253544Shselasky				    g_keyboard_get_keycode(sc, sc->sc_state + 1);
341253544Shselasky			case 2:
342253544Shselasky				sc->sc_data[0].keycode[2] =
343253544Shselasky				    g_keyboard_get_keycode(sc, sc->sc_state + 2);
344253544Shselasky			case 3:
345253544Shselasky				sc->sc_data[0].keycode[3] =
346253544Shselasky				    g_keyboard_get_keycode(sc, sc->sc_state + 3);
347253544Shselasky			case 4:
348253544Shselasky				sc->sc_data[0].keycode[4] =
349253544Shselasky				    g_keyboard_get_keycode(sc, sc->sc_state + 4);
350253544Shselasky			default:
351253544Shselasky				sc->sc_data[0].keycode[5] =
352253544Shselasky				    g_keyboard_get_keycode(sc, sc->sc_state + 5);
353253544Shselasky			}
354253544Shselasky
355253544Shselasky			sc->sc_state++;
356253544Shselasky
357253544Shselasky			usbd_xfer_set_frame_data(xfer, 0, &sc->sc_data[0], sizeof(sc->sc_data[0]));
358253544Shselasky			usbd_xfer_set_frame_data(xfer, 1, &sc->sc_data[1], sizeof(sc->sc_data[1]));
359253544Shselasky			usbd_xfer_set_frames(xfer, 2);
360253544Shselasky			usbd_transfer_submit(xfer);
361253544Shselasky		}
362253544Shselasky		break;
363253544Shselasky
364253544Shselasky	default:			/* Error */
365253544Shselasky		DPRINTF("error=%s\n", usbd_errstr(error));
366253544Shselasky
367253544Shselasky		if (error != USB_ERR_CANCELLED) {
368253544Shselasky			/* try to clear stall first */
369253544Shselasky			usbd_xfer_set_stall(xfer);
370253544Shselasky			goto tr_setup;
371253544Shselasky		}
372253544Shselasky		break;
373253544Shselasky	}
374253544Shselasky}
375253544Shselasky
376253544Shselaskystatic int
377253544Shselaskyg_keyboard_handle_request(device_t dev,
378253544Shselasky    const void *preq, void **pptr, uint16_t *plen,
379253544Shselasky    uint16_t offset, uint8_t *pstate)
380253544Shselasky{
381253544Shselasky	struct g_keyboard_softc *sc = device_get_softc(dev);
382253544Shselasky	const struct usb_device_request *req = preq;
383253544Shselasky	uint8_t is_complete = *pstate;
384253544Shselasky
385253544Shselasky	if (!is_complete) {
386253544Shselasky		if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
387253544Shselasky		    (req->bRequest == UR_SET_REPORT) &&
388253544Shselasky		    (req->wValue[0] == 0x00) &&
389253544Shselasky		    (req->wValue[1] == 0x02)) {
390253544Shselasky
391253544Shselasky			if (offset == 0) {
392253544Shselasky				*plen = sizeof(sc->sc_led_state);
393253544Shselasky				*pptr = &sc->sc_led_state;
394253544Shselasky			} else {
395253544Shselasky				*plen = 0;
396253544Shselasky			}
397253544Shselasky			return (0);
398253544Shselasky		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
399253544Shselasky			    (req->bRequest == UR_SET_PROTOCOL) &&
400253544Shselasky			    (req->wValue[0] == 0x00) &&
401253544Shselasky		    (req->wValue[1] == 0x00)) {
402253544Shselasky			*plen = 0;
403253544Shselasky			return (0);
404253544Shselasky		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
405253544Shselasky		    (req->bRequest == UR_SET_IDLE)) {
406253544Shselasky			*plen = 0;
407253544Shselasky			return (0);
408253544Shselasky		}
409253544Shselasky	}
410253544Shselasky	return (ENXIO);			/* use builtin handler */
411253544Shselasky}
412