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