xref: /freebsd/sys/dev/usb/input/usbhid.c (revision 5b56413d04e608379c9a306373554a8e4d321bc0)
101f2e864SVladimir Kondratyev /*-
2b61a5730SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
301f2e864SVladimir Kondratyev  *
401f2e864SVladimir Kondratyev  * Copyright (c) 1998 The NetBSD Foundation, Inc.
501f2e864SVladimir Kondratyev  * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org>
601f2e864SVladimir Kondratyev  *
701f2e864SVladimir Kondratyev  * This code is derived from software contributed to The NetBSD Foundation
801f2e864SVladimir Kondratyev  * by Lennart Augustsson (lennart@augustsson.net) at
901f2e864SVladimir Kondratyev  * Carlstedt Research & Technology.
1001f2e864SVladimir Kondratyev  *
1101f2e864SVladimir Kondratyev  * Redistribution and use in source and binary forms, with or without
1201f2e864SVladimir Kondratyev  * modification, are permitted provided that the following conditions
1301f2e864SVladimir Kondratyev  * are met:
1401f2e864SVladimir Kondratyev  * 1. Redistributions of source code must retain the above copyright
1501f2e864SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer.
1601f2e864SVladimir Kondratyev  * 2. Redistributions in binary form must reproduce the above copyright
1701f2e864SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer in the
1801f2e864SVladimir Kondratyev  *    documentation and/or other materials provided with the distribution.
1901f2e864SVladimir Kondratyev  *
2001f2e864SVladimir Kondratyev  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2101f2e864SVladimir Kondratyev  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2201f2e864SVladimir Kondratyev  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2301f2e864SVladimir Kondratyev  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2401f2e864SVladimir Kondratyev  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2501f2e864SVladimir Kondratyev  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2601f2e864SVladimir Kondratyev  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2701f2e864SVladimir Kondratyev  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2801f2e864SVladimir Kondratyev  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2901f2e864SVladimir Kondratyev  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3001f2e864SVladimir Kondratyev  * POSSIBILITY OF SUCH DAMAGE.
3101f2e864SVladimir Kondratyev  */
3201f2e864SVladimir Kondratyev 
3301f2e864SVladimir Kondratyev #include <sys/cdefs.h>
3401f2e864SVladimir Kondratyev /*
3501f2e864SVladimir Kondratyev  * HID spec: https://www.usb.org/sites/default/files/documents/hid1_11.pdf
3601f2e864SVladimir Kondratyev  */
3701f2e864SVladimir Kondratyev 
3801f2e864SVladimir Kondratyev #include <sys/stdint.h>
3901f2e864SVladimir Kondratyev #include <sys/stddef.h>
4001f2e864SVladimir Kondratyev #include <sys/param.h>
4101f2e864SVladimir Kondratyev #include <sys/queue.h>
4201f2e864SVladimir Kondratyev #include <sys/types.h>
4301f2e864SVladimir Kondratyev #include <sys/systm.h>
4401f2e864SVladimir Kondratyev #include <sys/kernel.h>
4501f2e864SVladimir Kondratyev #include <sys/bus.h>
4601f2e864SVladimir Kondratyev #include <sys/module.h>
4701f2e864SVladimir Kondratyev #include <sys/lock.h>
4801f2e864SVladimir Kondratyev #include <sys/mutex.h>
4901f2e864SVladimir Kondratyev #include <sys/condvar.h>
5001f2e864SVladimir Kondratyev #include <sys/sysctl.h>
5101f2e864SVladimir Kondratyev #include <sys/sx.h>
5201f2e864SVladimir Kondratyev #include <sys/unistd.h>
5301f2e864SVladimir Kondratyev #include <sys/callout.h>
5401f2e864SVladimir Kondratyev #include <sys/malloc.h>
5501f2e864SVladimir Kondratyev #include <sys/priv.h>
5601f2e864SVladimir Kondratyev #include <sys/conf.h>
5701f2e864SVladimir Kondratyev #include <sys/fcntl.h>
5801f2e864SVladimir Kondratyev 
5901f2e864SVladimir Kondratyev #include <dev/evdev/input.h>
6001f2e864SVladimir Kondratyev 
6101f2e864SVladimir Kondratyev #include <dev/hid/hid.h>
6201f2e864SVladimir Kondratyev #include <dev/hid/hidquirk.h>
6301f2e864SVladimir Kondratyev 
6401f2e864SVladimir Kondratyev #include <dev/usb/usb.h>
6501f2e864SVladimir Kondratyev #include <dev/usb/usbdi.h>
6601f2e864SVladimir Kondratyev #include <dev/usb/usbdi_util.h>
6701f2e864SVladimir Kondratyev #include <dev/usb/usbhid.h>
689aa0e5afSVladimir Kondratyev #include <dev/usb/usb_core.h>
6982e38b01SVladimir Kondratyev #include <dev/usb/usb_ioctl.h>
70fcca9fd9SHans Petter Selasky #include <dev/usb/usb_util.h>
7101f2e864SVladimir Kondratyev 
7201f2e864SVladimir Kondratyev #define	USB_DEBUG_VAR usbhid_debug
7301f2e864SVladimir Kondratyev #include <dev/usb/usb_debug.h>
7401f2e864SVladimir Kondratyev 
7501f2e864SVladimir Kondratyev #include <dev/usb/quirk/usb_quirk.h>
7601f2e864SVladimir Kondratyev 
7701f2e864SVladimir Kondratyev #include "hid_if.h"
7801f2e864SVladimir Kondratyev 
79b62f6dfaSVladimir Kondratyev static SYSCTL_NODE(_hw_usb, OID_AUTO, usbhid, CTLFLAG_RW, 0, "USB usbhid");
80b62f6dfaSVladimir Kondratyev static int usbhid_enable = 0;
81b62f6dfaSVladimir Kondratyev SYSCTL_INT(_hw_usb_usbhid, OID_AUTO, enable, CTLFLAG_RWTUN,
82b62f6dfaSVladimir Kondratyev     &usbhid_enable, 0, "Enable usbhid and prefer it to other USB HID drivers");
8301f2e864SVladimir Kondratyev #ifdef USB_DEBUG
8401f2e864SVladimir Kondratyev static int usbhid_debug = 0;
8501f2e864SVladimir Kondratyev SYSCTL_INT(_hw_usb_usbhid, OID_AUTO, debug, CTLFLAG_RWTUN,
8601f2e864SVladimir Kondratyev     &usbhid_debug, 0, "Debug level");
8701f2e864SVladimir Kondratyev #endif
8801f2e864SVladimir Kondratyev 
899aa0e5afSVladimir Kondratyev /* Second set of USB transfers for polling mode */
909aa0e5afSVladimir Kondratyev #define	POLL_XFER(xfer)	((xfer) + USBHID_N_TRANSFER)
9101f2e864SVladimir Kondratyev enum {
9201f2e864SVladimir Kondratyev 	USBHID_INTR_OUT_DT,
9301f2e864SVladimir Kondratyev 	USBHID_INTR_IN_DT,
9401f2e864SVladimir Kondratyev 	USBHID_CTRL_DT,
9501f2e864SVladimir Kondratyev 	USBHID_N_TRANSFER,
9601f2e864SVladimir Kondratyev };
9701f2e864SVladimir Kondratyev 
9801f2e864SVladimir Kondratyev struct usbhid_xfer_ctx;
9901f2e864SVladimir Kondratyev typedef int usbhid_callback_t(struct usbhid_xfer_ctx *xfer_ctx);
10001f2e864SVladimir Kondratyev 
10101f2e864SVladimir Kondratyev union usbhid_device_request {
10201f2e864SVladimir Kondratyev 	struct {			/* INTR xfers */
10301f2e864SVladimir Kondratyev 		uint16_t maxlen;
10401f2e864SVladimir Kondratyev 		uint16_t actlen;
10501f2e864SVladimir Kondratyev 	} intr;
10601f2e864SVladimir Kondratyev 	struct usb_device_request ctrl;	/* CTRL xfers */
10701f2e864SVladimir Kondratyev };
10801f2e864SVladimir Kondratyev 
10901f2e864SVladimir Kondratyev /* Syncronous USB transfer context */
11001f2e864SVladimir Kondratyev struct usbhid_xfer_ctx {
11101f2e864SVladimir Kondratyev 	union usbhid_device_request req;
11201f2e864SVladimir Kondratyev 	uint8_t *buf;
11301f2e864SVladimir Kondratyev 	int error;
11401f2e864SVladimir Kondratyev 	usbhid_callback_t *cb;
11501f2e864SVladimir Kondratyev 	void *cb_ctx;
11601f2e864SVladimir Kondratyev 	int waiters;
11701f2e864SVladimir Kondratyev 	bool influx;
11801f2e864SVladimir Kondratyev };
11901f2e864SVladimir Kondratyev 
12001f2e864SVladimir Kondratyev struct usbhid_softc {
12101f2e864SVladimir Kondratyev 	hid_intr_t *sc_intr_handler;
12201f2e864SVladimir Kondratyev 	void *sc_intr_ctx;
12301f2e864SVladimir Kondratyev 	void *sc_intr_buf;
12401f2e864SVladimir Kondratyev 
12501f2e864SVladimir Kondratyev 	struct hid_device_info sc_hw;
12601f2e864SVladimir Kondratyev 
12701f2e864SVladimir Kondratyev 	struct mtx sc_mtx;
12801f2e864SVladimir Kondratyev 	struct usb_config sc_config[USBHID_N_TRANSFER];
1299aa0e5afSVladimir Kondratyev 	struct usb_xfer *sc_xfer[POLL_XFER(USBHID_N_TRANSFER)];
1309aa0e5afSVladimir Kondratyev 	struct usbhid_xfer_ctx sc_xfer_ctx[POLL_XFER(USBHID_N_TRANSFER)];
1319aa0e5afSVladimir Kondratyev 	bool sc_can_poll;
13201f2e864SVladimir Kondratyev 
13301f2e864SVladimir Kondratyev 	struct usb_device *sc_udev;
13401f2e864SVladimir Kondratyev 	uint8_t	sc_iface_no;
13501f2e864SVladimir Kondratyev 	uint8_t	sc_iface_index;
13601f2e864SVladimir Kondratyev };
13701f2e864SVladimir Kondratyev 
13801f2e864SVladimir Kondratyev /* prototypes */
13901f2e864SVladimir Kondratyev 
14001f2e864SVladimir Kondratyev static device_probe_t usbhid_probe;
14101f2e864SVladimir Kondratyev static device_attach_t usbhid_attach;
14201f2e864SVladimir Kondratyev static device_detach_t usbhid_detach;
14301f2e864SVladimir Kondratyev 
14401f2e864SVladimir Kondratyev static usb_callback_t usbhid_intr_out_callback;
14501f2e864SVladimir Kondratyev static usb_callback_t usbhid_intr_in_callback;
14601f2e864SVladimir Kondratyev static usb_callback_t usbhid_ctrl_callback;
14701f2e864SVladimir Kondratyev 
14801f2e864SVladimir Kondratyev static usbhid_callback_t usbhid_intr_handler_cb;
14901f2e864SVladimir Kondratyev static usbhid_callback_t usbhid_sync_wakeup_cb;
15001f2e864SVladimir Kondratyev 
15101f2e864SVladimir Kondratyev static void
15201f2e864SVladimir Kondratyev usbhid_intr_out_callback(struct usb_xfer *xfer, usb_error_t error)
15301f2e864SVladimir Kondratyev {
15401f2e864SVladimir Kondratyev 	struct usbhid_xfer_ctx *xfer_ctx = usbd_xfer_softc(xfer);
15501f2e864SVladimir Kondratyev 	struct usb_page_cache *pc;
15601f2e864SVladimir Kondratyev 	int len;
15701f2e864SVladimir Kondratyev 
15801f2e864SVladimir Kondratyev 	switch (USB_GET_STATE(xfer)) {
15901f2e864SVladimir Kondratyev 	case USB_ST_TRANSFERRED:
16001f2e864SVladimir Kondratyev 	case USB_ST_SETUP:
16101f2e864SVladimir Kondratyev tr_setup:
16201f2e864SVladimir Kondratyev 		len = xfer_ctx->req.intr.maxlen;
16301f2e864SVladimir Kondratyev 		if (len == 0) {
16401f2e864SVladimir Kondratyev 			if (USB_IN_POLLING_MODE_FUNC())
16501f2e864SVladimir Kondratyev 				xfer_ctx->error = 0;
16601f2e864SVladimir Kondratyev 			return;
16701f2e864SVladimir Kondratyev 		}
16801f2e864SVladimir Kondratyev 		pc = usbd_xfer_get_frame(xfer, 0);
16901f2e864SVladimir Kondratyev 		usbd_copy_in(pc, 0, xfer_ctx->buf, len);
17001f2e864SVladimir Kondratyev 		usbd_xfer_set_frame_len(xfer, 0, len);
17101f2e864SVladimir Kondratyev 		usbd_transfer_submit(xfer);
17201f2e864SVladimir Kondratyev 		xfer_ctx->req.intr.maxlen = 0;
17301f2e864SVladimir Kondratyev 		if (USB_IN_POLLING_MODE_FUNC())
17401f2e864SVladimir Kondratyev 			return;
17501f2e864SVladimir Kondratyev 		xfer_ctx->error = 0;
17601f2e864SVladimir Kondratyev 		goto tr_exit;
17701f2e864SVladimir Kondratyev 
17801f2e864SVladimir Kondratyev 	default:			/* Error */
17901f2e864SVladimir Kondratyev 		if (error != USB_ERR_CANCELLED) {
18001f2e864SVladimir Kondratyev 			/* try to clear stall first */
18101f2e864SVladimir Kondratyev 			usbd_xfer_set_stall(xfer);
18201f2e864SVladimir Kondratyev 			goto tr_setup;
18301f2e864SVladimir Kondratyev 		}
18401f2e864SVladimir Kondratyev 		xfer_ctx->error = EIO;
18501f2e864SVladimir Kondratyev tr_exit:
18601f2e864SVladimir Kondratyev 		(void)xfer_ctx->cb(xfer_ctx);
18701f2e864SVladimir Kondratyev 		return;
18801f2e864SVladimir Kondratyev 	}
18901f2e864SVladimir Kondratyev }
19001f2e864SVladimir Kondratyev 
19101f2e864SVladimir Kondratyev static void
19201f2e864SVladimir Kondratyev usbhid_intr_in_callback(struct usb_xfer *xfer, usb_error_t error)
19301f2e864SVladimir Kondratyev {
19401f2e864SVladimir Kondratyev 	struct usbhid_xfer_ctx *xfer_ctx = usbd_xfer_softc(xfer);
19501f2e864SVladimir Kondratyev 	struct usb_page_cache *pc;
19601f2e864SVladimir Kondratyev 	int actlen;
19701f2e864SVladimir Kondratyev 
19801f2e864SVladimir Kondratyev 	switch (USB_GET_STATE(xfer)) {
19901f2e864SVladimir Kondratyev 	case USB_ST_TRANSFERRED:
20001f2e864SVladimir Kondratyev 		DPRINTF("transferred!\n");
20101f2e864SVladimir Kondratyev 
20201f2e864SVladimir Kondratyev 		usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
20301f2e864SVladimir Kondratyev 		pc = usbd_xfer_get_frame(xfer, 0);
20401f2e864SVladimir Kondratyev 		usbd_copy_out(pc, 0, xfer_ctx->buf, actlen);
20501f2e864SVladimir Kondratyev 		xfer_ctx->req.intr.actlen = actlen;
20601f2e864SVladimir Kondratyev 		if (xfer_ctx->cb(xfer_ctx) != 0)
20701f2e864SVladimir Kondratyev 			return;
20801f2e864SVladimir Kondratyev 
20901f2e864SVladimir Kondratyev 	case USB_ST_SETUP:
21001f2e864SVladimir Kondratyev re_submit:
21101f2e864SVladimir Kondratyev 		usbd_xfer_set_frame_len(xfer, 0, xfer_ctx->req.intr.maxlen);
21201f2e864SVladimir Kondratyev 		usbd_transfer_submit(xfer);
21301f2e864SVladimir Kondratyev 		return;
21401f2e864SVladimir Kondratyev 
21501f2e864SVladimir Kondratyev 	default:			/* Error */
21601f2e864SVladimir Kondratyev 		if (error != USB_ERR_CANCELLED) {
21701f2e864SVladimir Kondratyev 			/* try to clear stall first */
21801f2e864SVladimir Kondratyev 			usbd_xfer_set_stall(xfer);
21901f2e864SVladimir Kondratyev 			goto re_submit;
22001f2e864SVladimir Kondratyev 		}
22101f2e864SVladimir Kondratyev 		return;
22201f2e864SVladimir Kondratyev 	}
22301f2e864SVladimir Kondratyev }
22401f2e864SVladimir Kondratyev 
22501f2e864SVladimir Kondratyev static void
22601f2e864SVladimir Kondratyev usbhid_ctrl_callback(struct usb_xfer *xfer, usb_error_t error)
22701f2e864SVladimir Kondratyev {
22801f2e864SVladimir Kondratyev 	struct usbhid_xfer_ctx *xfer_ctx = usbd_xfer_softc(xfer);
22901f2e864SVladimir Kondratyev 	struct usb_device_request *req = &xfer_ctx->req.ctrl;
23001f2e864SVladimir Kondratyev 	struct usb_page_cache *pc;
23101f2e864SVladimir Kondratyev 	int len = UGETW(req->wLength);
23201f2e864SVladimir Kondratyev 	bool is_rd = (req->bmRequestType & UT_READ) != 0;
23301f2e864SVladimir Kondratyev 
23401f2e864SVladimir Kondratyev 	switch (USB_GET_STATE(xfer)) {
23501f2e864SVladimir Kondratyev 	case USB_ST_SETUP:
23601f2e864SVladimir Kondratyev 		if (!is_rd && len != 0) {
23701f2e864SVladimir Kondratyev 			pc = usbd_xfer_get_frame(xfer, 1);
23801f2e864SVladimir Kondratyev 			usbd_copy_in(pc, 0, xfer_ctx->buf, len);
23901f2e864SVladimir Kondratyev 		}
24001f2e864SVladimir Kondratyev 
24101f2e864SVladimir Kondratyev 		pc = usbd_xfer_get_frame(xfer, 0);
24201f2e864SVladimir Kondratyev 		usbd_copy_in(pc, 0, req, sizeof(*req));
24301f2e864SVladimir Kondratyev 		usbd_xfer_set_frame_len(xfer, 0, sizeof(*req));
24401f2e864SVladimir Kondratyev 		if (len != 0)
24501f2e864SVladimir Kondratyev 			usbd_xfer_set_frame_len(xfer, 1, len);
24601f2e864SVladimir Kondratyev 		usbd_xfer_set_frames(xfer, len != 0 ? 2 : 1);
24701f2e864SVladimir Kondratyev 		usbd_transfer_submit(xfer);
24801f2e864SVladimir Kondratyev 		return;
24901f2e864SVladimir Kondratyev 
25001f2e864SVladimir Kondratyev 	case USB_ST_TRANSFERRED:
25101f2e864SVladimir Kondratyev 		if (is_rd && len != 0) {
25201f2e864SVladimir Kondratyev 			pc = usbd_xfer_get_frame(xfer, 0);
25301f2e864SVladimir Kondratyev 			usbd_copy_out(pc, sizeof(*req), xfer_ctx->buf, len);
25401f2e864SVladimir Kondratyev 		}
25501f2e864SVladimir Kondratyev 		xfer_ctx->error = 0;
25601f2e864SVladimir Kondratyev 		goto tr_exit;
25701f2e864SVladimir Kondratyev 
25801f2e864SVladimir Kondratyev 	default:			/* Error */
25901f2e864SVladimir Kondratyev 		/* bomb out */
26001f2e864SVladimir Kondratyev 		DPRINTFN(1, "error=%s\n", usbd_errstr(error));
26101f2e864SVladimir Kondratyev 		xfer_ctx->error = EIO;
26201f2e864SVladimir Kondratyev tr_exit:
26301f2e864SVladimir Kondratyev 		(void)xfer_ctx->cb(xfer_ctx);
26401f2e864SVladimir Kondratyev 		return;
26501f2e864SVladimir Kondratyev 	}
26601f2e864SVladimir Kondratyev }
26701f2e864SVladimir Kondratyev 
26801f2e864SVladimir Kondratyev static int
26901f2e864SVladimir Kondratyev usbhid_intr_handler_cb(struct usbhid_xfer_ctx *xfer_ctx)
27001f2e864SVladimir Kondratyev {
27101f2e864SVladimir Kondratyev 	struct usbhid_softc *sc = xfer_ctx->cb_ctx;
27201f2e864SVladimir Kondratyev 
27301f2e864SVladimir Kondratyev 	sc->sc_intr_handler(sc->sc_intr_ctx, xfer_ctx->buf,
27401f2e864SVladimir Kondratyev 	    xfer_ctx->req.intr.actlen);
27501f2e864SVladimir Kondratyev 
27601f2e864SVladimir Kondratyev 	return (0);
27701f2e864SVladimir Kondratyev }
27801f2e864SVladimir Kondratyev 
27901f2e864SVladimir Kondratyev static int
28001f2e864SVladimir Kondratyev usbhid_sync_wakeup_cb(struct usbhid_xfer_ctx *xfer_ctx)
28101f2e864SVladimir Kondratyev {
28201f2e864SVladimir Kondratyev 
28301f2e864SVladimir Kondratyev 	if (!USB_IN_POLLING_MODE_FUNC())
28401f2e864SVladimir Kondratyev 		wakeup(xfer_ctx->cb_ctx);
28501f2e864SVladimir Kondratyev 
28601f2e864SVladimir Kondratyev 	return (ECANCELED);
28701f2e864SVladimir Kondratyev }
28801f2e864SVladimir Kondratyev 
28901f2e864SVladimir Kondratyev static const struct usb_config usbhid_config[USBHID_N_TRANSFER] = {
29001f2e864SVladimir Kondratyev 
29101f2e864SVladimir Kondratyev 	[USBHID_INTR_OUT_DT] = {
29201f2e864SVladimir Kondratyev 		.type = UE_INTERRUPT,
29301f2e864SVladimir Kondratyev 		.endpoint = UE_ADDR_ANY,
29401f2e864SVladimir Kondratyev 		.direction = UE_DIR_OUT,
29501f2e864SVladimir Kondratyev 		.flags = {.pipe_bof = 1,.proxy_buffer = 1},
29601f2e864SVladimir Kondratyev 		.callback = &usbhid_intr_out_callback,
29701f2e864SVladimir Kondratyev 	},
29801f2e864SVladimir Kondratyev 	[USBHID_INTR_IN_DT] = {
29901f2e864SVladimir Kondratyev 		.type = UE_INTERRUPT,
30001f2e864SVladimir Kondratyev 		.endpoint = UE_ADDR_ANY,
30101f2e864SVladimir Kondratyev 		.direction = UE_DIR_IN,
30201f2e864SVladimir Kondratyev 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1},
30301f2e864SVladimir Kondratyev 		.callback = &usbhid_intr_in_callback,
30401f2e864SVladimir Kondratyev 	},
30501f2e864SVladimir Kondratyev 	[USBHID_CTRL_DT] = {
30601f2e864SVladimir Kondratyev 		.type = UE_CONTROL,
30701f2e864SVladimir Kondratyev 		.endpoint = 0x00,	/* Control pipe */
30801f2e864SVladimir Kondratyev 		.direction = UE_DIR_ANY,
30901f2e864SVladimir Kondratyev 		.flags = {.proxy_buffer = 1},
31001f2e864SVladimir Kondratyev 		.callback = &usbhid_ctrl_callback,
31101f2e864SVladimir Kondratyev 		.timeout = 1000,	/* 1 second */
31201f2e864SVladimir Kondratyev 	},
31301f2e864SVladimir Kondratyev };
31401f2e864SVladimir Kondratyev 
315e889a462SVladimir Kondratyev static inline usb_frlength_t
316e889a462SVladimir Kondratyev usbhid_xfer_max_len(struct usb_xfer *xfer)
317e889a462SVladimir Kondratyev {
318e889a462SVladimir Kondratyev 	return (xfer == NULL ? 0 : usbd_xfer_max_len(xfer));
319e889a462SVladimir Kondratyev }
320e889a462SVladimir Kondratyev 
321e889a462SVladimir Kondratyev static inline int
322e889a462SVladimir Kondratyev usbhid_xfer_check_len(struct usbhid_softc* sc, int xfer_idx, hid_size_t len)
323e889a462SVladimir Kondratyev {
3249aa0e5afSVladimir Kondratyev 	if (USB_IN_POLLING_MODE_FUNC())
3259aa0e5afSVladimir Kondratyev 		xfer_idx = POLL_XFER(xfer_idx);
326e889a462SVladimir Kondratyev 	if (sc->sc_xfer[xfer_idx] == NULL)
327e889a462SVladimir Kondratyev 		return (ENODEV);
328e889a462SVladimir Kondratyev 	if (len > usbd_xfer_max_len(sc->sc_xfer[xfer_idx]))
329e889a462SVladimir Kondratyev 		return (ENOBUFS);
330e889a462SVladimir Kondratyev 	return (0);
331e889a462SVladimir Kondratyev }
332e889a462SVladimir Kondratyev 
33301f2e864SVladimir Kondratyev static void
3344b171281SVladimir Kondratyev usbhid_intr_setup(device_t dev, device_t child __unused, hid_intr_t intr,
3354b171281SVladimir Kondratyev     void *context, struct hid_rdesc_info *rdesc)
33601f2e864SVladimir Kondratyev {
33701f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
33801f2e864SVladimir Kondratyev 	uint16_t n;
33901f2e864SVladimir Kondratyev 	bool nowrite;
34001f2e864SVladimir Kondratyev 	int error;
34101f2e864SVladimir Kondratyev 
3429aa0e5afSVladimir Kondratyev 	nowrite = hid_test_quirk(&sc->sc_hw, HQ_NOWRITE);
3439aa0e5afSVladimir Kondratyev 
3449aa0e5afSVladimir Kondratyev 	/*
3459aa0e5afSVladimir Kondratyev 	 * Setup the USB transfers one by one, so they are memory independent
3469aa0e5afSVladimir Kondratyev 	 * which allows for handling panics triggered by the HID drivers
3479aa0e5afSVladimir Kondratyev 	 * itself, typically by hkbd via CTRL+ALT+ESC sequences. Or if the HID
3489aa0e5afSVladimir Kondratyev 	 * keyboard driver was processing a key at the moment of panic.
3499aa0e5afSVladimir Kondratyev 	 */
3509aa0e5afSVladimir Kondratyev 	if (intr == NULL) {
3519aa0e5afSVladimir Kondratyev 		if (sc->sc_can_poll)
3529aa0e5afSVladimir Kondratyev 			return;
3539aa0e5afSVladimir Kondratyev 		for (n = 0; n != USBHID_N_TRANSFER; n++) {
3549aa0e5afSVladimir Kondratyev 			if (nowrite && n == USBHID_INTR_OUT_DT)
3559aa0e5afSVladimir Kondratyev 				continue;
3569aa0e5afSVladimir Kondratyev 			error = usbd_transfer_setup(sc->sc_udev,
3579aa0e5afSVladimir Kondratyev 			    &sc->sc_iface_index, sc->sc_xfer + POLL_XFER(n),
3589aa0e5afSVladimir Kondratyev 			    sc->sc_config + n, 1,
3599aa0e5afSVladimir Kondratyev 			    (void *)(sc->sc_xfer_ctx + POLL_XFER(n)),
3609aa0e5afSVladimir Kondratyev 			    &sc->sc_mtx);
3619aa0e5afSVladimir Kondratyev 			if (error)
3629aa0e5afSVladimir Kondratyev 				DPRINTF("xfer %d setup error=%s\n", n,
3639aa0e5afSVladimir Kondratyev 				    usbd_errstr(error));
3649aa0e5afSVladimir Kondratyev 		}
3659aa0e5afSVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
3669aa0e5afSVladimir Kondratyev 		if (sc->sc_xfer[USBHID_INTR_IN_DT] != NULL &&
3679aa0e5afSVladimir Kondratyev 		    sc->sc_xfer[USBHID_INTR_IN_DT]->flags_int.started)
3689aa0e5afSVladimir Kondratyev 			usbd_transfer_start(
3699aa0e5afSVladimir Kondratyev 			    sc->sc_xfer[POLL_XFER(USBHID_INTR_IN_DT)]);
3709aa0e5afSVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
3719aa0e5afSVladimir Kondratyev 		sc->sc_can_poll = true;
3729aa0e5afSVladimir Kondratyev 		return;
3739aa0e5afSVladimir Kondratyev 	}
3749aa0e5afSVladimir Kondratyev 
37501f2e864SVladimir Kondratyev 	sc->sc_intr_handler = intr;
37601f2e864SVladimir Kondratyev 	sc->sc_intr_ctx = context;
37701f2e864SVladimir Kondratyev 	bcopy(usbhid_config, sc->sc_config, sizeof(usbhid_config));
378e889a462SVladimir Kondratyev 	bzero(sc->sc_xfer, sizeof(sc->sc_xfer));
37901f2e864SVladimir Kondratyev 
38001f2e864SVladimir Kondratyev 	/* Set buffer sizes to match HID report sizes */
38101f2e864SVladimir Kondratyev 	sc->sc_config[USBHID_INTR_OUT_DT].bufsize = rdesc->osize;
38201f2e864SVladimir Kondratyev 	sc->sc_config[USBHID_INTR_IN_DT].bufsize = rdesc->isize;
38301f2e864SVladimir Kondratyev 	sc->sc_config[USBHID_CTRL_DT].bufsize =
38401f2e864SVladimir Kondratyev 	    MAX(rdesc->isize, MAX(rdesc->osize, rdesc->fsize));
38501f2e864SVladimir Kondratyev 
38601f2e864SVladimir Kondratyev 	for (n = 0; n != USBHID_N_TRANSFER; n++) {
38701f2e864SVladimir Kondratyev 		if (nowrite && n == USBHID_INTR_OUT_DT)
38801f2e864SVladimir Kondratyev 			continue;
38901f2e864SVladimir Kondratyev 		error = usbd_transfer_setup(sc->sc_udev, &sc->sc_iface_index,
39001f2e864SVladimir Kondratyev 		    sc->sc_xfer + n, sc->sc_config + n, 1,
39101f2e864SVladimir Kondratyev 		    (void *)(sc->sc_xfer_ctx + n), &sc->sc_mtx);
39201f2e864SVladimir Kondratyev 		if (error)
393e889a462SVladimir Kondratyev 			DPRINTF("xfer %d setup error=%s\n", n,
394e889a462SVladimir Kondratyev 			    usbd_errstr(error));
39501f2e864SVladimir Kondratyev 	}
39601f2e864SVladimir Kondratyev 
397e889a462SVladimir Kondratyev 	rdesc->rdsize = usbhid_xfer_max_len(sc->sc_xfer[USBHID_INTR_IN_DT]);
398e889a462SVladimir Kondratyev 	rdesc->grsize = usbhid_xfer_max_len(sc->sc_xfer[USBHID_CTRL_DT]);
39901f2e864SVladimir Kondratyev 	rdesc->srsize = rdesc->grsize;
40001f2e864SVladimir Kondratyev 	rdesc->wrsize = nowrite ? rdesc->srsize :
401e889a462SVladimir Kondratyev 	    usbhid_xfer_max_len(sc->sc_xfer[USBHID_INTR_OUT_DT]);
40201f2e864SVladimir Kondratyev 
40301f2e864SVladimir Kondratyev 	sc->sc_intr_buf = malloc(rdesc->rdsize, M_USBDEV, M_ZERO | M_WAITOK);
40401f2e864SVladimir Kondratyev }
40501f2e864SVladimir Kondratyev 
40601f2e864SVladimir Kondratyev static void
4074b171281SVladimir Kondratyev usbhid_intr_unsetup(device_t dev, device_t child __unused)
40801f2e864SVladimir Kondratyev {
40901f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
41001f2e864SVladimir Kondratyev 
41101f2e864SVladimir Kondratyev 	usbd_transfer_unsetup(sc->sc_xfer, USBHID_N_TRANSFER);
4129aa0e5afSVladimir Kondratyev 	if (sc->sc_can_poll)
4139aa0e5afSVladimir Kondratyev 		usbd_transfer_unsetup(
4149aa0e5afSVladimir Kondratyev 		    sc->sc_xfer, POLL_XFER(USBHID_N_TRANSFER));
4159aa0e5afSVladimir Kondratyev 	sc->sc_can_poll = false;
41601f2e864SVladimir Kondratyev 	free(sc->sc_intr_buf, M_USBDEV);
41701f2e864SVladimir Kondratyev }
41801f2e864SVladimir Kondratyev 
41901f2e864SVladimir Kondratyev static int
4204b171281SVladimir Kondratyev usbhid_intr_start(device_t dev, device_t child __unused)
42101f2e864SVladimir Kondratyev {
42201f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
42301f2e864SVladimir Kondratyev 
424e889a462SVladimir Kondratyev 	if (sc->sc_xfer[USBHID_INTR_IN_DT] == NULL)
425e889a462SVladimir Kondratyev 		return (ENODEV);
426e889a462SVladimir Kondratyev 
42701f2e864SVladimir Kondratyev 	mtx_lock(&sc->sc_mtx);
42801f2e864SVladimir Kondratyev 	sc->sc_xfer_ctx[USBHID_INTR_IN_DT] = (struct usbhid_xfer_ctx) {
42901f2e864SVladimir Kondratyev 		.req.intr.maxlen =
43001f2e864SVladimir Kondratyev 		    usbd_xfer_max_len(sc->sc_xfer[USBHID_INTR_IN_DT]),
43101f2e864SVladimir Kondratyev 		.cb = usbhid_intr_handler_cb,
43201f2e864SVladimir Kondratyev 		.cb_ctx = sc,
43301f2e864SVladimir Kondratyev 		.buf = sc->sc_intr_buf,
43401f2e864SVladimir Kondratyev 	};
4359aa0e5afSVladimir Kondratyev 	sc->sc_xfer_ctx[POLL_XFER(USBHID_INTR_IN_DT)] = (struct usbhid_xfer_ctx) {
4369aa0e5afSVladimir Kondratyev 		.req.intr.maxlen =
4379aa0e5afSVladimir Kondratyev 		    usbd_xfer_max_len(sc->sc_xfer[USBHID_INTR_IN_DT]),
4389aa0e5afSVladimir Kondratyev 		.cb = usbhid_intr_handler_cb,
4399aa0e5afSVladimir Kondratyev 		.cb_ctx = sc,
4409aa0e5afSVladimir Kondratyev 		.buf = sc->sc_intr_buf,
4419aa0e5afSVladimir Kondratyev 	};
44201f2e864SVladimir Kondratyev 	usbd_transfer_start(sc->sc_xfer[USBHID_INTR_IN_DT]);
4439aa0e5afSVladimir Kondratyev 	if (sc->sc_can_poll)
4449aa0e5afSVladimir Kondratyev 		usbd_transfer_start(sc->sc_xfer[POLL_XFER(USBHID_INTR_IN_DT)]);
44501f2e864SVladimir Kondratyev 	mtx_unlock(&sc->sc_mtx);
44601f2e864SVladimir Kondratyev 
44701f2e864SVladimir Kondratyev 	return (0);
44801f2e864SVladimir Kondratyev }
44901f2e864SVladimir Kondratyev 
45001f2e864SVladimir Kondratyev static int
4514b171281SVladimir Kondratyev usbhid_intr_stop(device_t dev, device_t child __unused)
45201f2e864SVladimir Kondratyev {
45301f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
45401f2e864SVladimir Kondratyev 
45501f2e864SVladimir Kondratyev 	usbd_transfer_drain(sc->sc_xfer[USBHID_INTR_IN_DT]);
45601f2e864SVladimir Kondratyev 	usbd_transfer_drain(sc->sc_xfer[USBHID_INTR_OUT_DT]);
4579aa0e5afSVladimir Kondratyev 	if (sc->sc_can_poll)
4589aa0e5afSVladimir Kondratyev 		usbd_transfer_drain(sc->sc_xfer[POLL_XFER(USBHID_INTR_IN_DT)]);
45901f2e864SVladimir Kondratyev 
46001f2e864SVladimir Kondratyev 	return (0);
46101f2e864SVladimir Kondratyev }
46201f2e864SVladimir Kondratyev 
46301f2e864SVladimir Kondratyev static void
4644b171281SVladimir Kondratyev usbhid_intr_poll(device_t dev, device_t child __unused)
46501f2e864SVladimir Kondratyev {
46601f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
46701f2e864SVladimir Kondratyev 
4689aa0e5afSVladimir Kondratyev 	MPASS(sc->sc_can_poll);
46901f2e864SVladimir Kondratyev 	usbd_transfer_poll(sc->sc_xfer + USBHID_INTR_IN_DT, 1);
4709aa0e5afSVladimir Kondratyev 	usbd_transfer_poll(sc->sc_xfer + POLL_XFER(USBHID_INTR_IN_DT), 1);
47101f2e864SVladimir Kondratyev }
47201f2e864SVladimir Kondratyev 
47301f2e864SVladimir Kondratyev /*
47401f2e864SVladimir Kondratyev  * HID interface
47501f2e864SVladimir Kondratyev  */
47601f2e864SVladimir Kondratyev static int
47701f2e864SVladimir Kondratyev usbhid_sync_xfer(struct usbhid_softc* sc, int xfer_idx,
47801f2e864SVladimir Kondratyev     union usbhid_device_request *req, void *buf)
47901f2e864SVladimir Kondratyev {
48001f2e864SVladimir Kondratyev 	int error, timeout;
4819aa0e5afSVladimir Kondratyev 	struct usbhid_xfer_ctx *xfer_ctx;
48201f2e864SVladimir Kondratyev 
48301f2e864SVladimir Kondratyev 	xfer_ctx = sc->sc_xfer_ctx + xfer_idx;
48401f2e864SVladimir Kondratyev 
48501f2e864SVladimir Kondratyev 	if (USB_IN_POLLING_MODE_FUNC()) {
4869aa0e5afSVladimir Kondratyev 		xfer_ctx = POLL_XFER(xfer_ctx);
4879aa0e5afSVladimir Kondratyev 		xfer_idx = POLL_XFER(xfer_idx);
48801f2e864SVladimir Kondratyev 	} else {
48901f2e864SVladimir Kondratyev 		mtx_lock(&sc->sc_mtx);
49001f2e864SVladimir Kondratyev 		++xfer_ctx->waiters;
49101f2e864SVladimir Kondratyev 		while (xfer_ctx->influx)
49201f2e864SVladimir Kondratyev 			mtx_sleep(&xfer_ctx->waiters, &sc->sc_mtx, 0,
49301f2e864SVladimir Kondratyev 			    "usbhid wt", 0);
49401f2e864SVladimir Kondratyev 		--xfer_ctx->waiters;
49501f2e864SVladimir Kondratyev 		xfer_ctx->influx = true;
49601f2e864SVladimir Kondratyev 	}
49701f2e864SVladimir Kondratyev 
49801f2e864SVladimir Kondratyev 	xfer_ctx->buf = buf;
49901f2e864SVladimir Kondratyev 	xfer_ctx->req = *req;
50001f2e864SVladimir Kondratyev 	xfer_ctx->error = ETIMEDOUT;
50101f2e864SVladimir Kondratyev 	xfer_ctx->cb = &usbhid_sync_wakeup_cb;
50201f2e864SVladimir Kondratyev 	xfer_ctx->cb_ctx = xfer_ctx;
50301f2e864SVladimir Kondratyev 	timeout = USB_DEFAULT_TIMEOUT;
50401f2e864SVladimir Kondratyev 	usbd_transfer_start(sc->sc_xfer[xfer_idx]);
50501f2e864SVladimir Kondratyev 
50601f2e864SVladimir Kondratyev 	if (USB_IN_POLLING_MODE_FUNC())
50701f2e864SVladimir Kondratyev 		while (timeout > 0 && xfer_ctx->error == ETIMEDOUT) {
50801f2e864SVladimir Kondratyev 			usbd_transfer_poll(sc->sc_xfer + xfer_idx, 1);
50901f2e864SVladimir Kondratyev 			DELAY(1000);
51001f2e864SVladimir Kondratyev 			timeout--;
51101f2e864SVladimir Kondratyev 		}
51201f2e864SVladimir Kondratyev 	 else
51301f2e864SVladimir Kondratyev 		msleep_sbt(xfer_ctx, &sc->sc_mtx, 0, "usbhid io",
51401f2e864SVladimir Kondratyev 		    SBT_1MS * timeout, 0, C_HARDCLOCK);
51501f2e864SVladimir Kondratyev 
51601f2e864SVladimir Kondratyev 	/* Perform usbhid_write() asyncronously to improve pipelining */
51701f2e864SVladimir Kondratyev 	if (USB_IN_POLLING_MODE_FUNC() || xfer_ctx->error != 0 ||
51801f2e864SVladimir Kondratyev 	    sc->sc_config[xfer_idx].type != UE_INTERRUPT ||
51901f2e864SVladimir Kondratyev 	    sc->sc_config[xfer_idx].direction != UE_DIR_OUT)
52001f2e864SVladimir Kondratyev 		usbd_transfer_stop(sc->sc_xfer[xfer_idx]);
52101f2e864SVladimir Kondratyev 	error = xfer_ctx->error;
52201f2e864SVladimir Kondratyev 	if (error == 0)
52301f2e864SVladimir Kondratyev 		*req = xfer_ctx->req;
52401f2e864SVladimir Kondratyev 
5259aa0e5afSVladimir Kondratyev 	if (!USB_IN_POLLING_MODE_FUNC()) {
52601f2e864SVladimir Kondratyev 		xfer_ctx->influx = false;
52701f2e864SVladimir Kondratyev 		if (xfer_ctx->waiters != 0)
52801f2e864SVladimir Kondratyev 			wakeup_one(&xfer_ctx->waiters);
52901f2e864SVladimir Kondratyev 		mtx_unlock(&sc->sc_mtx);
53001f2e864SVladimir Kondratyev 	}
53101f2e864SVladimir Kondratyev 
53201f2e864SVladimir Kondratyev 	if (error)
53301f2e864SVladimir Kondratyev 		DPRINTF("USB IO error:%d\n", error);
53401f2e864SVladimir Kondratyev 
53501f2e864SVladimir Kondratyev 	return (error);
53601f2e864SVladimir Kondratyev }
53701f2e864SVladimir Kondratyev 
53801f2e864SVladimir Kondratyev static int
5394b171281SVladimir Kondratyev usbhid_get_rdesc(device_t dev, device_t child __unused, void *buf,
5404b171281SVladimir Kondratyev     hid_size_t len)
54101f2e864SVladimir Kondratyev {
54201f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
54301f2e864SVladimir Kondratyev 	int error;
54401f2e864SVladimir Kondratyev 
54501f2e864SVladimir Kondratyev 	error = usbd_req_get_report_descriptor(sc->sc_udev, NULL,
54601f2e864SVladimir Kondratyev 	    buf, len, sc->sc_iface_index);
54701f2e864SVladimir Kondratyev 
54801f2e864SVladimir Kondratyev 	if (error)
54901f2e864SVladimir Kondratyev 		DPRINTF("no report descriptor: %s\n", usbd_errstr(error));
55001f2e864SVladimir Kondratyev 
55101f2e864SVladimir Kondratyev 	return (error == 0 ? 0 : ENXIO);
55201f2e864SVladimir Kondratyev }
55301f2e864SVladimir Kondratyev 
55401f2e864SVladimir Kondratyev static int
5554b171281SVladimir Kondratyev usbhid_get_report(device_t dev, device_t child __unused, void *buf,
5564b171281SVladimir Kondratyev     hid_size_t maxlen, hid_size_t *actlen, uint8_t type, uint8_t id)
55701f2e864SVladimir Kondratyev {
55801f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
55901f2e864SVladimir Kondratyev 	union usbhid_device_request req;
56001f2e864SVladimir Kondratyev 	int error;
56101f2e864SVladimir Kondratyev 
562e889a462SVladimir Kondratyev 	error = usbhid_xfer_check_len(sc, USBHID_CTRL_DT, maxlen);
563e889a462SVladimir Kondratyev 	if (error)
564e889a462SVladimir Kondratyev 		return (error);
56501f2e864SVladimir Kondratyev 
56601f2e864SVladimir Kondratyev 	req.ctrl.bmRequestType = UT_READ_CLASS_INTERFACE;
56701f2e864SVladimir Kondratyev 	req.ctrl.bRequest = UR_GET_REPORT;
56801f2e864SVladimir Kondratyev 	USETW2(req.ctrl.wValue, type, id);
56901f2e864SVladimir Kondratyev 	req.ctrl.wIndex[0] = sc->sc_iface_no;
57001f2e864SVladimir Kondratyev 	req.ctrl.wIndex[1] = 0;
57101f2e864SVladimir Kondratyev 	USETW(req.ctrl.wLength, maxlen);
57201f2e864SVladimir Kondratyev 
57301f2e864SVladimir Kondratyev 	error = usbhid_sync_xfer(sc, USBHID_CTRL_DT, &req, buf);
57401f2e864SVladimir Kondratyev 	if (!error && actlen != NULL)
57501f2e864SVladimir Kondratyev 		*actlen = maxlen;
57601f2e864SVladimir Kondratyev 
57701f2e864SVladimir Kondratyev 	return (error);
57801f2e864SVladimir Kondratyev }
57901f2e864SVladimir Kondratyev 
58001f2e864SVladimir Kondratyev static int
5814b171281SVladimir Kondratyev usbhid_set_report(device_t dev, device_t child __unused, const void *buf,
5824b171281SVladimir Kondratyev     hid_size_t len, uint8_t type, uint8_t id)
58301f2e864SVladimir Kondratyev {
58401f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
58501f2e864SVladimir Kondratyev 	union usbhid_device_request req;
586e889a462SVladimir Kondratyev 	int error;
58701f2e864SVladimir Kondratyev 
588e889a462SVladimir Kondratyev 	error = usbhid_xfer_check_len(sc, USBHID_CTRL_DT, len);
589e889a462SVladimir Kondratyev 	if (error)
590e889a462SVladimir Kondratyev 		return (error);
59101f2e864SVladimir Kondratyev 
59201f2e864SVladimir Kondratyev 	req.ctrl.bmRequestType = UT_WRITE_CLASS_INTERFACE;
59301f2e864SVladimir Kondratyev 	req.ctrl.bRequest = UR_SET_REPORT;
59401f2e864SVladimir Kondratyev 	USETW2(req.ctrl.wValue, type, id);
59501f2e864SVladimir Kondratyev 	req.ctrl.wIndex[0] = sc->sc_iface_no;
59601f2e864SVladimir Kondratyev 	req.ctrl.wIndex[1] = 0;
59701f2e864SVladimir Kondratyev 	USETW(req.ctrl.wLength, len);
59801f2e864SVladimir Kondratyev 
59901f2e864SVladimir Kondratyev 	return (usbhid_sync_xfer(sc, USBHID_CTRL_DT, &req,
60001f2e864SVladimir Kondratyev 	    __DECONST(void *, buf)));
60101f2e864SVladimir Kondratyev }
60201f2e864SVladimir Kondratyev 
60301f2e864SVladimir Kondratyev static int
6044b171281SVladimir Kondratyev usbhid_read(device_t dev, device_t child __unused, void *buf,
6054b171281SVladimir Kondratyev     hid_size_t maxlen, hid_size_t *actlen)
60601f2e864SVladimir Kondratyev {
60701f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
60801f2e864SVladimir Kondratyev 	union usbhid_device_request req;
60901f2e864SVladimir Kondratyev 	int error;
61001f2e864SVladimir Kondratyev 
611e889a462SVladimir Kondratyev 	error = usbhid_xfer_check_len(sc, USBHID_INTR_IN_DT, maxlen);
612e889a462SVladimir Kondratyev 	if (error)
613e889a462SVladimir Kondratyev 		return (error);
61401f2e864SVladimir Kondratyev 
61501f2e864SVladimir Kondratyev 	req.intr.maxlen = maxlen;
61601f2e864SVladimir Kondratyev 	error = usbhid_sync_xfer(sc, USBHID_INTR_IN_DT, &req, buf);
61701f2e864SVladimir Kondratyev 	if (error == 0 && actlen != NULL)
61801f2e864SVladimir Kondratyev 		*actlen = req.intr.actlen;
61901f2e864SVladimir Kondratyev 
62001f2e864SVladimir Kondratyev 	return (error);
62101f2e864SVladimir Kondratyev }
62201f2e864SVladimir Kondratyev 
62301f2e864SVladimir Kondratyev static int
6244b171281SVladimir Kondratyev usbhid_write(device_t dev, device_t child __unused, const void *buf,
6254b171281SVladimir Kondratyev     hid_size_t len)
62601f2e864SVladimir Kondratyev {
62701f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
62801f2e864SVladimir Kondratyev 	union usbhid_device_request req;
629e889a462SVladimir Kondratyev 	int error;
63001f2e864SVladimir Kondratyev 
631e889a462SVladimir Kondratyev 	error = usbhid_xfer_check_len(sc, USBHID_INTR_OUT_DT, len);
632e889a462SVladimir Kondratyev 	if (error)
633e889a462SVladimir Kondratyev 		return (error);
63401f2e864SVladimir Kondratyev 
63501f2e864SVladimir Kondratyev 	req.intr.maxlen = len;
63601f2e864SVladimir Kondratyev 	return (usbhid_sync_xfer(sc, USBHID_INTR_OUT_DT, &req,
63701f2e864SVladimir Kondratyev 	    __DECONST(void *, buf)));
63801f2e864SVladimir Kondratyev }
63901f2e864SVladimir Kondratyev 
64001f2e864SVladimir Kondratyev static int
6414b171281SVladimir Kondratyev usbhid_set_idle(device_t dev, device_t child __unused, uint16_t duration,
6424b171281SVladimir Kondratyev     uint8_t id)
64301f2e864SVladimir Kondratyev {
64401f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
64501f2e864SVladimir Kondratyev 	union usbhid_device_request req;
646e889a462SVladimir Kondratyev 	int error;
647e889a462SVladimir Kondratyev 
648e889a462SVladimir Kondratyev 	error = usbhid_xfer_check_len(sc, USBHID_CTRL_DT, 0);
649e889a462SVladimir Kondratyev 	if (error)
650e889a462SVladimir Kondratyev 		return (error);
65101f2e864SVladimir Kondratyev 
65201f2e864SVladimir Kondratyev 	/* Duration is measured in 4 milliseconds per unit. */
65301f2e864SVladimir Kondratyev 	req.ctrl.bmRequestType = UT_WRITE_CLASS_INTERFACE;
65401f2e864SVladimir Kondratyev 	req.ctrl.bRequest = UR_SET_IDLE;
65501f2e864SVladimir Kondratyev 	USETW2(req.ctrl.wValue, (duration + 3) / 4, id);
65601f2e864SVladimir Kondratyev 	req.ctrl.wIndex[0] = sc->sc_iface_no;
65701f2e864SVladimir Kondratyev 	req.ctrl.wIndex[1] = 0;
65801f2e864SVladimir Kondratyev 	USETW(req.ctrl.wLength, 0);
65901f2e864SVladimir Kondratyev 
66001f2e864SVladimir Kondratyev 	return (usbhid_sync_xfer(sc, USBHID_CTRL_DT, &req, NULL));
66101f2e864SVladimir Kondratyev }
66201f2e864SVladimir Kondratyev 
66301f2e864SVladimir Kondratyev static int
6644b171281SVladimir Kondratyev usbhid_set_protocol(device_t dev, device_t child __unused, uint16_t protocol)
66501f2e864SVladimir Kondratyev {
66601f2e864SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
66701f2e864SVladimir Kondratyev 	union usbhid_device_request req;
668e889a462SVladimir Kondratyev 	int error;
669e889a462SVladimir Kondratyev 
670e889a462SVladimir Kondratyev 	error = usbhid_xfer_check_len(sc, USBHID_CTRL_DT, 0);
671e889a462SVladimir Kondratyev 	if (error)
672e889a462SVladimir Kondratyev 		return (error);
67301f2e864SVladimir Kondratyev 
67401f2e864SVladimir Kondratyev 	req.ctrl.bmRequestType = UT_WRITE_CLASS_INTERFACE;
67501f2e864SVladimir Kondratyev 	req.ctrl.bRequest = UR_SET_PROTOCOL;
67601f2e864SVladimir Kondratyev 	USETW(req.ctrl.wValue, protocol);
67701f2e864SVladimir Kondratyev 	req.ctrl.wIndex[0] = sc->sc_iface_no;
67801f2e864SVladimir Kondratyev 	req.ctrl.wIndex[1] = 0;
67901f2e864SVladimir Kondratyev 	USETW(req.ctrl.wLength, 0);
68001f2e864SVladimir Kondratyev 
68101f2e864SVladimir Kondratyev 	return (usbhid_sync_xfer(sc, USBHID_CTRL_DT, &req, NULL));
68201f2e864SVladimir Kondratyev }
68301f2e864SVladimir Kondratyev 
68482e38b01SVladimir Kondratyev static int
6854b171281SVladimir Kondratyev usbhid_ioctl(device_t dev, device_t child __unused, unsigned long cmd,
6864b171281SVladimir Kondratyev     uintptr_t data)
68782e38b01SVladimir Kondratyev {
68882e38b01SVladimir Kondratyev 	struct usbhid_softc* sc = device_get_softc(dev);
68982e38b01SVladimir Kondratyev 	struct usb_ctl_request *ucr;
69082e38b01SVladimir Kondratyev 	union usbhid_device_request req;
69182e38b01SVladimir Kondratyev 	int error;
69282e38b01SVladimir Kondratyev 
69382e38b01SVladimir Kondratyev 	switch (cmd) {
69482e38b01SVladimir Kondratyev 	case USB_REQUEST:
69582e38b01SVladimir Kondratyev 		ucr = (struct usb_ctl_request *)data;
69682e38b01SVladimir Kondratyev 		req.ctrl = ucr->ucr_request;
69782e38b01SVladimir Kondratyev 		error = usbhid_xfer_check_len(
69882e38b01SVladimir Kondratyev 		    sc, USBHID_CTRL_DT, UGETW(req.ctrl.wLength));
69982e38b01SVladimir Kondratyev 		if (error)
70082e38b01SVladimir Kondratyev 			break;
701fcca9fd9SHans Petter Selasky 		error = usb_check_request(sc->sc_udev, &req.ctrl);
702fcca9fd9SHans Petter Selasky 		if (error)
703fcca9fd9SHans Petter Selasky 			break;
70482e38b01SVladimir Kondratyev 		error = usbhid_sync_xfer(
70582e38b01SVladimir Kondratyev 		    sc, USBHID_CTRL_DT, &req, ucr->ucr_data);
70682e38b01SVladimir Kondratyev 		if (error == 0)
70782e38b01SVladimir Kondratyev 			ucr->ucr_actlen = UGETW(req.ctrl.wLength);
70882e38b01SVladimir Kondratyev 		break;
70982e38b01SVladimir Kondratyev 	default:
71082e38b01SVladimir Kondratyev 		error = EINVAL;
71182e38b01SVladimir Kondratyev 	}
71282e38b01SVladimir Kondratyev 
71382e38b01SVladimir Kondratyev 	return (error);
71482e38b01SVladimir Kondratyev }
71582e38b01SVladimir Kondratyev 
71601f2e864SVladimir Kondratyev static void
71701f2e864SVladimir Kondratyev usbhid_init_device_info(struct usb_attach_arg *uaa, struct hid_device_info *hw)
71801f2e864SVladimir Kondratyev {
71901f2e864SVladimir Kondratyev 
72001f2e864SVladimir Kondratyev 	hw->idBus = BUS_USB;
72101f2e864SVladimir Kondratyev 	hw->idVendor = uaa->info.idVendor;
72201f2e864SVladimir Kondratyev 	hw->idProduct = uaa->info.idProduct;
72301f2e864SVladimir Kondratyev 	hw->idVersion = uaa->info.bcdDevice;
72401f2e864SVladimir Kondratyev 
72501f2e864SVladimir Kondratyev 	/* Set various quirks based on usb_attach_arg */
72601f2e864SVladimir Kondratyev 	hid_add_dynamic_quirk(hw, USB_GET_DRIVER_INFO(uaa));
72701f2e864SVladimir Kondratyev }
72801f2e864SVladimir Kondratyev 
72901f2e864SVladimir Kondratyev static void
73001f2e864SVladimir Kondratyev usbhid_fill_device_info(struct usb_attach_arg *uaa, struct hid_device_info *hw)
73101f2e864SVladimir Kondratyev {
73201f2e864SVladimir Kondratyev 	struct usb_device *udev = uaa->device;
73301f2e864SVladimir Kondratyev 	struct usb_interface *iface = uaa->iface;
73401f2e864SVladimir Kondratyev 	struct usb_hid_descriptor *hid;
73501f2e864SVladimir Kondratyev 	struct usb_endpoint *ep;
73601f2e864SVladimir Kondratyev 
73701f2e864SVladimir Kondratyev 	snprintf(hw->name, sizeof(hw->name), "%s %s",
73801f2e864SVladimir Kondratyev 	    usb_get_manufacturer(udev), usb_get_product(udev));
73901f2e864SVladimir Kondratyev 	strlcpy(hw->serial, usb_get_serial(udev), sizeof(hw->serial));
74001f2e864SVladimir Kondratyev 
74101f2e864SVladimir Kondratyev 	if (uaa->info.bInterfaceClass == UICLASS_HID &&
74201f2e864SVladimir Kondratyev 	    iface != NULL && iface->idesc != NULL) {
74301f2e864SVladimir Kondratyev 		hid = hid_get_descriptor_from_usb(
74401f2e864SVladimir Kondratyev 		    usbd_get_config_descriptor(udev), iface->idesc);
74501f2e864SVladimir Kondratyev 		if (hid != NULL)
74601f2e864SVladimir Kondratyev 			hw->rdescsize =
74701f2e864SVladimir Kondratyev 			    UGETW(hid->descrs[0].wDescriptorLength);
74801f2e864SVladimir Kondratyev 	}
74901f2e864SVladimir Kondratyev 
75001f2e864SVladimir Kondratyev 	/* See if there is a interrupt out endpoint. */
75101f2e864SVladimir Kondratyev 	ep = usbd_get_endpoint(udev, uaa->info.bIfaceIndex,
75201f2e864SVladimir Kondratyev 	    usbhid_config + USBHID_INTR_OUT_DT);
75301f2e864SVladimir Kondratyev 	if (ep == NULL || ep->methods == NULL)
75401f2e864SVladimir Kondratyev 		hid_add_dynamic_quirk(hw, HQ_NOWRITE);
75501f2e864SVladimir Kondratyev }
75601f2e864SVladimir Kondratyev 
75701f2e864SVladimir Kondratyev static const STRUCT_USB_HOST_ID usbhid_devs[] = {
75801f2e864SVladimir Kondratyev 	/* the Xbox 360 gamepad doesn't use the HID class */
75901f2e864SVladimir Kondratyev 	{USB_IFACE_CLASS(UICLASS_VENDOR),
76001f2e864SVladimir Kondratyev 	 USB_IFACE_SUBCLASS(UISUBCLASS_XBOX360_CONTROLLER),
76101f2e864SVladimir Kondratyev 	 USB_IFACE_PROTOCOL(UIPROTO_XBOX360_GAMEPAD),
76201f2e864SVladimir Kondratyev 	 USB_DRIVER_INFO(HQ_IS_XBOX360GP)},
76301f2e864SVladimir Kondratyev 	/* HID keyboard with boot protocol support */
76401f2e864SVladimir Kondratyev 	{USB_IFACE_CLASS(UICLASS_HID),
76501f2e864SVladimir Kondratyev 	 USB_IFACE_SUBCLASS(UISUBCLASS_BOOT),
76601f2e864SVladimir Kondratyev 	 USB_IFACE_PROTOCOL(UIPROTO_BOOT_KEYBOARD),
76701f2e864SVladimir Kondratyev 	 USB_DRIVER_INFO(HQ_HAS_KBD_BOOTPROTO)},
76801f2e864SVladimir Kondratyev 	/* HID mouse with boot protocol support */
76901f2e864SVladimir Kondratyev 	{USB_IFACE_CLASS(UICLASS_HID),
77001f2e864SVladimir Kondratyev 	 USB_IFACE_SUBCLASS(UISUBCLASS_BOOT),
77101f2e864SVladimir Kondratyev 	 USB_IFACE_PROTOCOL(UIPROTO_MOUSE),
77201f2e864SVladimir Kondratyev 	 USB_DRIVER_INFO(HQ_HAS_MS_BOOTPROTO)},
77301f2e864SVladimir Kondratyev 	/* generic HID class */
77401f2e864SVladimir Kondratyev 	{USB_IFACE_CLASS(UICLASS_HID), USB_DRIVER_INFO(HQ_NONE)},
77501f2e864SVladimir Kondratyev };
77601f2e864SVladimir Kondratyev 
77701f2e864SVladimir Kondratyev static int
77801f2e864SVladimir Kondratyev usbhid_probe(device_t dev)
77901f2e864SVladimir Kondratyev {
78001f2e864SVladimir Kondratyev 	struct usb_attach_arg *uaa = device_get_ivars(dev);
78101f2e864SVladimir Kondratyev 	struct usbhid_softc *sc = device_get_softc(dev);
78201f2e864SVladimir Kondratyev 	int error;
78301f2e864SVladimir Kondratyev 
78401f2e864SVladimir Kondratyev 	DPRINTFN(11, "\n");
78501f2e864SVladimir Kondratyev 
786b62f6dfaSVladimir Kondratyev 	if (usbhid_enable == 0)
787b62f6dfaSVladimir Kondratyev 		return (ENXIO);
788b62f6dfaSVladimir Kondratyev 
78901f2e864SVladimir Kondratyev 	if (uaa->usb_mode != USB_MODE_HOST)
79001f2e864SVladimir Kondratyev 		return (ENXIO);
79101f2e864SVladimir Kondratyev 
79201f2e864SVladimir Kondratyev 	error = usbd_lookup_id_by_uaa(usbhid_devs, sizeof(usbhid_devs), uaa);
79301f2e864SVladimir Kondratyev 	if (error)
79401f2e864SVladimir Kondratyev 		return (error);
79501f2e864SVladimir Kondratyev 
79601f2e864SVladimir Kondratyev 	if (usb_test_quirk(uaa, UQ_HID_IGNORE))
79701f2e864SVladimir Kondratyev 		return (ENXIO);
79801f2e864SVladimir Kondratyev 
79901f2e864SVladimir Kondratyev 	/*
80001f2e864SVladimir Kondratyev 	 * Setup temporary hid_device_info so that we can figure out some
80101f2e864SVladimir Kondratyev 	 * basic quirks for this device.
80201f2e864SVladimir Kondratyev 	 */
80301f2e864SVladimir Kondratyev 	usbhid_init_device_info(uaa, &sc->sc_hw);
80401f2e864SVladimir Kondratyev 
80501f2e864SVladimir Kondratyev 	if (hid_test_quirk(&sc->sc_hw, HQ_HID_IGNORE))
80601f2e864SVladimir Kondratyev 		return (ENXIO);
80701f2e864SVladimir Kondratyev 
808975407b1SVladimir Kondratyev 	return (BUS_PROBE_DEFAULT + 1);
80901f2e864SVladimir Kondratyev }
81001f2e864SVladimir Kondratyev 
81101f2e864SVladimir Kondratyev static int
81201f2e864SVladimir Kondratyev usbhid_attach(device_t dev)
81301f2e864SVladimir Kondratyev {
81401f2e864SVladimir Kondratyev 	struct usb_attach_arg *uaa = device_get_ivars(dev);
81501f2e864SVladimir Kondratyev 	struct usbhid_softc *sc = device_get_softc(dev);
81601f2e864SVladimir Kondratyev 	device_t child;
81701f2e864SVladimir Kondratyev 	int error = 0;
81801f2e864SVladimir Kondratyev 
81901f2e864SVladimir Kondratyev 	DPRINTFN(10, "sc=%p\n", sc);
82001f2e864SVladimir Kondratyev 
82101f2e864SVladimir Kondratyev 	device_set_usb_desc(dev);
82201f2e864SVladimir Kondratyev 
82301f2e864SVladimir Kondratyev 	sc->sc_udev = uaa->device;
82401f2e864SVladimir Kondratyev 	sc->sc_iface_no = uaa->info.bIfaceNum;
82501f2e864SVladimir Kondratyev 	sc->sc_iface_index = uaa->info.bIfaceIndex;
82601f2e864SVladimir Kondratyev 
82701f2e864SVladimir Kondratyev 	usbhid_fill_device_info(uaa, &sc->sc_hw);
82801f2e864SVladimir Kondratyev 
82901f2e864SVladimir Kondratyev 	error = usbd_req_set_idle(uaa->device, NULL,
83001f2e864SVladimir Kondratyev 	    uaa->info.bIfaceIndex, 0, 0);
83101f2e864SVladimir Kondratyev 	if (error)
83201f2e864SVladimir Kondratyev 		DPRINTF("set idle failed, error=%s (ignored)\n",
83301f2e864SVladimir Kondratyev 		    usbd_errstr(error));
83401f2e864SVladimir Kondratyev 
83501f2e864SVladimir Kondratyev 	mtx_init(&sc->sc_mtx, "usbhid lock", NULL, MTX_DEF);
83601f2e864SVladimir Kondratyev 
837*5b56413dSWarner Losh 	child = device_add_child(dev, "hidbus", DEVICE_UNIT_ANY);
83801f2e864SVladimir Kondratyev 	if (child == NULL) {
83901f2e864SVladimir Kondratyev 		device_printf(dev, "Could not add hidbus device\n");
84001f2e864SVladimir Kondratyev 		usbhid_detach(dev);
84101f2e864SVladimir Kondratyev 		return (ENOMEM);
84201f2e864SVladimir Kondratyev 	}
84301f2e864SVladimir Kondratyev 
84401f2e864SVladimir Kondratyev 	device_set_ivars(child, &sc->sc_hw);
84501f2e864SVladimir Kondratyev 	error = bus_generic_attach(dev);
84601f2e864SVladimir Kondratyev 	if (error) {
84701f2e864SVladimir Kondratyev 		device_printf(dev, "failed to attach child: %d\n", error);
84801f2e864SVladimir Kondratyev 		usbhid_detach(dev);
84901f2e864SVladimir Kondratyev 		return (error);
85001f2e864SVladimir Kondratyev 	}
85101f2e864SVladimir Kondratyev 
85201f2e864SVladimir Kondratyev 	return (0);			/* success */
85301f2e864SVladimir Kondratyev }
85401f2e864SVladimir Kondratyev 
85501f2e864SVladimir Kondratyev static int
85601f2e864SVladimir Kondratyev usbhid_detach(device_t dev)
85701f2e864SVladimir Kondratyev {
85801f2e864SVladimir Kondratyev 	struct usbhid_softc *sc = device_get_softc(dev);
85901f2e864SVladimir Kondratyev 
86001f2e864SVladimir Kondratyev 	device_delete_children(dev);
86101f2e864SVladimir Kondratyev 	mtx_destroy(&sc->sc_mtx);
86201f2e864SVladimir Kondratyev 
86301f2e864SVladimir Kondratyev 	return (0);
86401f2e864SVladimir Kondratyev }
86501f2e864SVladimir Kondratyev 
86601f2e864SVladimir Kondratyev static device_method_t usbhid_methods[] = {
86701f2e864SVladimir Kondratyev 	DEVMETHOD(device_probe,		usbhid_probe),
86801f2e864SVladimir Kondratyev 	DEVMETHOD(device_attach,	usbhid_attach),
86901f2e864SVladimir Kondratyev 	DEVMETHOD(device_detach,	usbhid_detach),
87001f2e864SVladimir Kondratyev 
87101f2e864SVladimir Kondratyev 	DEVMETHOD(hid_intr_setup,	usbhid_intr_setup),
87201f2e864SVladimir Kondratyev 	DEVMETHOD(hid_intr_unsetup,	usbhid_intr_unsetup),
87301f2e864SVladimir Kondratyev 	DEVMETHOD(hid_intr_start,	usbhid_intr_start),
87401f2e864SVladimir Kondratyev 	DEVMETHOD(hid_intr_stop,	usbhid_intr_stop),
87501f2e864SVladimir Kondratyev 	DEVMETHOD(hid_intr_poll,	usbhid_intr_poll),
87601f2e864SVladimir Kondratyev 
87701f2e864SVladimir Kondratyev 	/* HID interface */
87801f2e864SVladimir Kondratyev 	DEVMETHOD(hid_get_rdesc,	usbhid_get_rdesc),
87901f2e864SVladimir Kondratyev 	DEVMETHOD(hid_read,		usbhid_read),
88001f2e864SVladimir Kondratyev 	DEVMETHOD(hid_write,		usbhid_write),
88101f2e864SVladimir Kondratyev 	DEVMETHOD(hid_get_report,	usbhid_get_report),
88201f2e864SVladimir Kondratyev 	DEVMETHOD(hid_set_report,	usbhid_set_report),
88301f2e864SVladimir Kondratyev 	DEVMETHOD(hid_set_idle,		usbhid_set_idle),
88401f2e864SVladimir Kondratyev 	DEVMETHOD(hid_set_protocol,	usbhid_set_protocol),
88582e38b01SVladimir Kondratyev 	DEVMETHOD(hid_ioctl,		usbhid_ioctl),
88601f2e864SVladimir Kondratyev 
88701f2e864SVladimir Kondratyev 	DEVMETHOD_END
88801f2e864SVladimir Kondratyev };
88901f2e864SVladimir Kondratyev 
89001f2e864SVladimir Kondratyev static driver_t usbhid_driver = {
89101f2e864SVladimir Kondratyev 	.name = "usbhid",
89201f2e864SVladimir Kondratyev 	.methods = usbhid_methods,
89301f2e864SVladimir Kondratyev 	.size = sizeof(struct usbhid_softc),
89401f2e864SVladimir Kondratyev };
89501f2e864SVladimir Kondratyev 
896bc9372d7SJohn Baldwin DRIVER_MODULE(usbhid, uhub, usbhid_driver, NULL, NULL);
89701f2e864SVladimir Kondratyev MODULE_DEPEND(usbhid, usb, 1, 1, 1);
89801f2e864SVladimir Kondratyev MODULE_DEPEND(usbhid, hid, 1, 1, 1);
89901f2e864SVladimir Kondratyev MODULE_DEPEND(usbhid, hidbus, 1, 1, 1);
90001f2e864SVladimir Kondratyev MODULE_VERSION(usbhid, 1);
90101f2e864SVladimir Kondratyev USB_PNP_HOST_INFO(usbhid_devs);
902