1f9f848faSopenharmony_ci/* $NetBSD: uhid.c,v 1.46 2001/11/13 06:24:55 lukem Exp $ */ 2f9f848faSopenharmony_ci 3f9f848faSopenharmony_ci/* Also already merged from NetBSD: 4f9f848faSopenharmony_ci * $NetBSD: uhid.c,v 1.54 2002/09/23 05:51:21 simonb Exp $ 5f9f848faSopenharmony_ci */ 6f9f848faSopenharmony_ci 7f9f848faSopenharmony_ci#include <sys/cdefs.h> 8f9f848faSopenharmony_ci/*- 9f9f848faSopenharmony_ci * SPDX-License-Identifier: BSD-2-Clause 10f9f848faSopenharmony_ci * 11f9f848faSopenharmony_ci * Copyright (c) 1998 The NetBSD Foundation, Inc. 12f9f848faSopenharmony_ci * All rights reserved. 13f9f848faSopenharmony_ci * 14f9f848faSopenharmony_ci * This code is derived from software contributed to The NetBSD Foundation 15f9f848faSopenharmony_ci * by Lennart Augustsson (lennart@augustsson.net) at 16f9f848faSopenharmony_ci * Carlstedt Research & Technology. 17f9f848faSopenharmony_ci * 18f9f848faSopenharmony_ci * Redistribution and use in source and binary forms, with or without 19f9f848faSopenharmony_ci * modification, are permitted provided that the following conditions 20f9f848faSopenharmony_ci * are met: 21f9f848faSopenharmony_ci * 1. Redistributions of source code must retain the above copyright 22f9f848faSopenharmony_ci * notice, this list of conditions and the following disclaimer. 23f9f848faSopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 24f9f848faSopenharmony_ci * notice, this list of conditions and the following disclaimer in the 25f9f848faSopenharmony_ci * documentation and/or other materials provided with the distribution. 26f9f848faSopenharmony_ci * 27f9f848faSopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28f9f848faSopenharmony_ci * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29f9f848faSopenharmony_ci * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30f9f848faSopenharmony_ci * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31f9f848faSopenharmony_ci * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32f9f848faSopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33f9f848faSopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34f9f848faSopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35f9f848faSopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36f9f848faSopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37f9f848faSopenharmony_ci * POSSIBILITY OF SUCH DAMAGE. 38f9f848faSopenharmony_ci */ 39f9f848faSopenharmony_ci 40f9f848faSopenharmony_ci/* 41f9f848faSopenharmony_ci * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 42f9f848faSopenharmony_ci */ 43f9f848faSopenharmony_ci 44f9f848faSopenharmony_ci#include "implementation/global_implementation.h" 45f9f848faSopenharmony_ci#include "input/usb_rdesc.h" 46f9f848faSopenharmony_ci#include "implementation/usbdevs.h" 47f9f848faSopenharmony_ci#include "event_hub.h" 48f9f848faSopenharmony_ci#include <dev/usb/usb_generic.h> 49f9f848faSopenharmony_ci 50f9f848faSopenharmony_ci#undef USB_DEBUG_VAR 51f9f848faSopenharmony_ci#define USB_DEBUG_VAR uhid_debug 52f9f848faSopenharmony_ci 53f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG 54f9f848faSopenharmony_cistatic int uhid_debug = 0; 55f9f848faSopenharmony_ci#endif 56f9f848faSopenharmony_ci 57f9f848faSopenharmony_ci#define UHID_BSIZE 1024 /* bytes, buffer size */ 58f9f848faSopenharmony_ci#define UHID_FRAME_NUM 50 /* bytes, frame number */ 59f9f848faSopenharmony_ci 60f9f848faSopenharmony_ci#define MOUSE_DATA_LEN 4 61f9f848faSopenharmony_ci#define BTN_LEFT_VALUE(v) ((v) & 0x01) 62f9f848faSopenharmony_ci#define BTN_RIGHT_VALUE(v) (((v) & 0x02)>>1) 63f9f848faSopenharmony_ci#define BTN_MIDDLE_VALUE(v) (((v) & 0x04)>>2) 64f9f848faSopenharmony_ci 65f9f848faSopenharmony_cienum { 66f9f848faSopenharmony_ci UHID_INTR_DT_WR, 67f9f848faSopenharmony_ci UHID_INTR_DT_RD, 68f9f848faSopenharmony_ci UHID_CTRL_DT_WR, 69f9f848faSopenharmony_ci UHID_CTRL_DT_RD, 70f9f848faSopenharmony_ci UHID_N_TRANSFER, 71f9f848faSopenharmony_ci}; 72f9f848faSopenharmony_ci 73f9f848faSopenharmony_cistruct uhid_softc { 74f9f848faSopenharmony_ci struct usb_fifo_sc sc_fifo; 75f9f848faSopenharmony_ci struct mtx sc_mtx; 76f9f848faSopenharmony_ci 77f9f848faSopenharmony_ci struct usb_xfer *sc_xfer[UHID_N_TRANSFER]; 78f9f848faSopenharmony_ci struct usb_device *sc_udev; 79f9f848faSopenharmony_ci void *sc_repdesc_ptr; 80f9f848faSopenharmony_ci 81f9f848faSopenharmony_ci uint32_t sc_isize; 82f9f848faSopenharmony_ci uint32_t sc_osize; 83f9f848faSopenharmony_ci uint32_t sc_fsize; 84f9f848faSopenharmony_ci 85f9f848faSopenharmony_ci InputDevice *input_dev; 86f9f848faSopenharmony_ci 87f9f848faSopenharmony_ci uint16_t sc_repdesc_size; 88f9f848faSopenharmony_ci 89f9f848faSopenharmony_ci uint8_t sc_iface_no; 90f9f848faSopenharmony_ci uint8_t sc_iface_index; 91f9f848faSopenharmony_ci uint8_t sc_iid; 92f9f848faSopenharmony_ci uint8_t sc_oid; 93f9f848faSopenharmony_ci uint8_t sc_fid; 94f9f848faSopenharmony_ci uint8_t sc_flags; 95f9f848faSopenharmony_ci#define UHID_FLAG_IMMED 0x01 /* set if read should be immediate */ 96f9f848faSopenharmony_ci#define UHID_FLAG_STATIC_DESC 0x04 /* set if report descriptors are 97f9f848faSopenharmony_ci * static */ 98f9f848faSopenharmony_ci}; 99f9f848faSopenharmony_ci 100f9f848faSopenharmony_cistatic const uint8_t uhid_xb360gp_report_descr[] = {UHID_XB360GP_REPORT_DESCR()}; 101f9f848faSopenharmony_cistatic const uint8_t uhid_graphire_report_descr[] = {UHID_GRAPHIRE_REPORT_DESCR()}; 102f9f848faSopenharmony_cistatic const uint8_t uhid_graphire3_4x5_report_descr[] = {UHID_GRAPHIRE3_4X5_REPORT_DESCR()}; 103f9f848faSopenharmony_ci 104f9f848faSopenharmony_ci/* prototypes */ 105f9f848faSopenharmony_ci 106f9f848faSopenharmony_cistatic device_probe_t uhid_probe; 107f9f848faSopenharmony_cistatic device_attach_t uhid_attach; 108f9f848faSopenharmony_cistatic device_detach_t uhid_detach; 109f9f848faSopenharmony_ci 110f9f848faSopenharmony_cistatic usb_callback_t uhid_intr_write_callback; 111f9f848faSopenharmony_cistatic usb_callback_t uhid_intr_read_callback; 112f9f848faSopenharmony_cistatic usb_callback_t uhid_write_callback; 113f9f848faSopenharmony_cistatic usb_callback_t uhid_read_callback; 114f9f848faSopenharmony_ci 115f9f848faSopenharmony_cistatic usb_fifo_cmd_t uhid_start_read; 116f9f848faSopenharmony_cistatic usb_fifo_cmd_t uhid_stop_read; 117f9f848faSopenharmony_cistatic usb_fifo_cmd_t uhid_start_write; 118f9f848faSopenharmony_cistatic usb_fifo_cmd_t uhid_stop_write; 119f9f848faSopenharmony_cistatic usb_fifo_open_t uhid_open; 120f9f848faSopenharmony_cistatic usb_fifo_close_t uhid_close; 121f9f848faSopenharmony_cistatic usb_fifo_ioctl_t uhid_ioctl; 122f9f848faSopenharmony_cistatic usb_fifo_ioctl_t uhid_ioctl_post; 123f9f848faSopenharmony_ci 124f9f848faSopenharmony_cistatic struct usb_fifo_methods uhid_fifo_methods = { 125f9f848faSopenharmony_ci .f_open = &uhid_open, 126f9f848faSopenharmony_ci .f_close = &uhid_close, 127f9f848faSopenharmony_ci .f_ioctl = &uhid_ioctl, 128f9f848faSopenharmony_ci .f_ioctl_post = &uhid_ioctl_post, 129f9f848faSopenharmony_ci .f_start_read = &uhid_start_read, 130f9f848faSopenharmony_ci .f_stop_read = &uhid_stop_read, 131f9f848faSopenharmony_ci .f_start_write = &uhid_start_write, 132f9f848faSopenharmony_ci .f_stop_write = &uhid_stop_write, 133f9f848faSopenharmony_ci .basename[0] = "uhid", 134f9f848faSopenharmony_ci}; 135f9f848faSopenharmony_ci 136f9f848faSopenharmony_cistatic void 137f9f848faSopenharmony_ciuhid_intr_write_callback(struct usb_xfer *xfer, usb_error_t error) 138f9f848faSopenharmony_ci{ 139f9f848faSopenharmony_ci struct uhid_softc *sc = usbd_xfer_softc(xfer); 140f9f848faSopenharmony_ci struct usb_page_cache *pc; 141f9f848faSopenharmony_ci usb_frlength_t actlen; 142f9f848faSopenharmony_ci 143f9f848faSopenharmony_ci switch (USB_GET_STATE(xfer)) { 144f9f848faSopenharmony_ci case USB_ST_TRANSFERRED: 145f9f848faSopenharmony_ci case USB_ST_SETUP: 146f9f848faSopenharmony_citr_setup: 147f9f848faSopenharmony_ci pc = usbd_xfer_get_frame(xfer, 0); 148f9f848faSopenharmony_ci if (usb_fifo_get_data(sc->sc_fifo.fp[USB_FIFO_TX], pc, 149f9f848faSopenharmony_ci 0, usbd_xfer_max_len(xfer), &actlen, 0)) { 150f9f848faSopenharmony_ci usbd_xfer_set_frame_len(xfer, 0, actlen); 151f9f848faSopenharmony_ci usbd_transfer_submit(xfer); 152f9f848faSopenharmony_ci } 153f9f848faSopenharmony_ci return; 154f9f848faSopenharmony_ci 155f9f848faSopenharmony_ci default: /* Error */ 156f9f848faSopenharmony_ci if (error != USB_ERR_CANCELLED) { 157f9f848faSopenharmony_ci /* try to clear stall first */ 158f9f848faSopenharmony_ci usbd_xfer_set_stall(xfer); 159f9f848faSopenharmony_ci goto tr_setup; 160f9f848faSopenharmony_ci } 161f9f848faSopenharmony_ci return; 162f9f848faSopenharmony_ci } 163f9f848faSopenharmony_ci} 164f9f848faSopenharmony_ci 165f9f848faSopenharmony_civoid report_event(InputDevice *input_dev, uint32_t type, uint32_t code, int32_t value) 166f9f848faSopenharmony_ci{ 167f9f848faSopenharmony_ci DPRINTF("%s type = %u, code = %u, value = %d\n", input_dev->devName, type, code, value); 168f9f848faSopenharmony_ci if (type == EV_SYN || type == EV_KEY) { 169f9f848faSopenharmony_ci PushOnePackage(input_dev, type, code, value); 170f9f848faSopenharmony_ci } else if (value) { 171f9f848faSopenharmony_ci PushOnePackage(input_dev, type, code, value); 172f9f848faSopenharmony_ci } 173f9f848faSopenharmony_ci} 174f9f848faSopenharmony_ci 175f9f848faSopenharmony_civoid mouse_report_events(InputDevice *input_dev, void *buffer, int len) 176f9f848faSopenharmony_ci{ 177f9f848faSopenharmony_ci if (len != MOUSE_DATA_LEN) { 178f9f848faSopenharmony_ci DPRINTF("%s: invalid data len = %d\n", __func__, len); 179f9f848faSopenharmony_ci return; 180f9f848faSopenharmony_ci } 181f9f848faSopenharmony_ci 182f9f848faSopenharmony_ci const char *buf = buffer; 183f9f848faSopenharmony_ci report_event(input_dev, EV_KEY, BTN_LEFT, BTN_LEFT_VALUE((unsigned char)buf[0])); 184f9f848faSopenharmony_ci report_event(input_dev, EV_KEY, BTN_RIGHT, BTN_RIGHT_VALUE((unsigned char)buf[0])); 185f9f848faSopenharmony_ci report_event(input_dev, EV_KEY, BTN_MIDDLE, BTN_MIDDLE_VALUE((unsigned char)buf[0])); 186f9f848faSopenharmony_ci report_event(input_dev, EV_REL, REL_X, buf[1]); 187f9f848faSopenharmony_ci report_event(input_dev, EV_REL, REL_Y, buf[2]); 188f9f848faSopenharmony_ci report_event(input_dev, EV_REL, REL_WHEEL, buf[3]); 189f9f848faSopenharmony_ci report_event(input_dev, EV_SYN, SYN_REPORT, 0); 190f9f848faSopenharmony_ci} 191f9f848faSopenharmony_ci 192f9f848faSopenharmony_cistatic void 193f9f848faSopenharmony_ciuhid_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) 194f9f848faSopenharmony_ci{ 195f9f848faSopenharmony_ci struct uhid_softc *sc = usbd_xfer_softc(xfer); 196f9f848faSopenharmony_ci struct usb_page_cache *pc; 197f9f848faSopenharmony_ci int actlen; 198f9f848faSopenharmony_ci 199f9f848faSopenharmony_ci DPRINTF("enter state of xfer is %u!\n", USB_GET_STATE(xfer)); 200f9f848faSopenharmony_ci 201f9f848faSopenharmony_ci usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 202f9f848faSopenharmony_ci 203f9f848faSopenharmony_ci switch (USB_GET_STATE(xfer)) { 204f9f848faSopenharmony_ci case USB_ST_TRANSFERRED: 205f9f848faSopenharmony_ci pc = usbd_xfer_get_frame(xfer, 0); 206f9f848faSopenharmony_ci if (sc->input_dev && sc->input_dev->devType == INDEV_TYPE_MOUSE) { 207f9f848faSopenharmony_ci mouse_report_events(sc->input_dev, pc->buffer, actlen); 208f9f848faSopenharmony_ci } 209f9f848faSopenharmony_ci 210f9f848faSopenharmony_ci case USB_ST_SETUP: 211f9f848faSopenharmony_ci usbd_xfer_set_frame_len(xfer, 0, sc->sc_isize); 212f9f848faSopenharmony_ci usbd_transfer_submit(xfer); 213f9f848faSopenharmony_ci return; 214f9f848faSopenharmony_ci 215f9f848faSopenharmony_ci default: 216f9f848faSopenharmony_ci if (error != USB_ERR_CANCELLED) { 217f9f848faSopenharmony_ci usbd_xfer_set_stall(xfer); 218f9f848faSopenharmony_ci usbd_xfer_set_frame_len(xfer, 0, sc->sc_isize); 219f9f848faSopenharmony_ci usbd_transfer_submit(xfer); 220f9f848faSopenharmony_ci } 221f9f848faSopenharmony_ci return; 222f9f848faSopenharmony_ci } 223f9f848faSopenharmony_ci} 224f9f848faSopenharmony_ci 225f9f848faSopenharmony_cistatic void 226f9f848faSopenharmony_ciuhid_fill_set_report(struct usb_device_request *req, uint8_t iface_no, 227f9f848faSopenharmony_ci uint8_t type, uint8_t id, uint16_t size) 228f9f848faSopenharmony_ci{ 229f9f848faSopenharmony_ci req->bmRequestType = UT_WRITE_CLASS_INTERFACE; 230f9f848faSopenharmony_ci req->bRequest = UR_SET_REPORT; 231f9f848faSopenharmony_ci USETW2(req->wValue, type, id); 232f9f848faSopenharmony_ci req->wIndex[0] = iface_no; 233f9f848faSopenharmony_ci req->wIndex[1] = 0; 234f9f848faSopenharmony_ci USETW(req->wLength, size); 235f9f848faSopenharmony_ci} 236f9f848faSopenharmony_ci 237f9f848faSopenharmony_cistatic void 238f9f848faSopenharmony_ciuhid_fill_get_report(struct usb_device_request *req, uint8_t iface_no, 239f9f848faSopenharmony_ci uint8_t type, uint8_t id, uint16_t size) 240f9f848faSopenharmony_ci{ 241f9f848faSopenharmony_ci req->bmRequestType = UT_READ_CLASS_INTERFACE; 242f9f848faSopenharmony_ci req->bRequest = UR_GET_REPORT; 243f9f848faSopenharmony_ci USETW2(req->wValue, type, id); 244f9f848faSopenharmony_ci req->wIndex[0] = iface_no; 245f9f848faSopenharmony_ci req->wIndex[1] = 0; 246f9f848faSopenharmony_ci USETW(req->wLength, size); 247f9f848faSopenharmony_ci} 248f9f848faSopenharmony_ci 249f9f848faSopenharmony_cistatic void 250f9f848faSopenharmony_ciuhid_write_callback(struct usb_xfer *xfer, usb_error_t error) 251f9f848faSopenharmony_ci{ 252f9f848faSopenharmony_ci struct uhid_softc *sc = usbd_xfer_softc(xfer); 253f9f848faSopenharmony_ci struct usb_device_request req; 254f9f848faSopenharmony_ci struct usb_page_cache *pc; 255f9f848faSopenharmony_ci uint32_t size = sc->sc_osize; 256f9f848faSopenharmony_ci uint32_t actlen; 257f9f848faSopenharmony_ci uint8_t id; 258f9f848faSopenharmony_ci 259f9f848faSopenharmony_ci switch (USB_GET_STATE(xfer)) { 260f9f848faSopenharmony_ci case USB_ST_TRANSFERRED: 261f9f848faSopenharmony_ci case USB_ST_SETUP: 262f9f848faSopenharmony_ci /* try to extract the ID byte */ 263f9f848faSopenharmony_ci if (sc->sc_oid) { 264f9f848faSopenharmony_ci pc = usbd_xfer_get_frame(xfer, 0); 265f9f848faSopenharmony_ci if (usb_fifo_get_data(sc->sc_fifo.fp[USB_FIFO_TX], pc, 266f9f848faSopenharmony_ci 0, 1, &actlen, 0)) { 267f9f848faSopenharmony_ci if (actlen != 1) { 268f9f848faSopenharmony_ci goto tr_error; 269f9f848faSopenharmony_ci } 270f9f848faSopenharmony_ci usbd_copy_out(pc, 0, &id, 1); 271f9f848faSopenharmony_ci 272f9f848faSopenharmony_ci } else { 273f9f848faSopenharmony_ci return; 274f9f848faSopenharmony_ci } 275f9f848faSopenharmony_ci if (size) { 276f9f848faSopenharmony_ci size--; 277f9f848faSopenharmony_ci } 278f9f848faSopenharmony_ci } else { 279f9f848faSopenharmony_ci id = 0; 280f9f848faSopenharmony_ci } 281f9f848faSopenharmony_ci 282f9f848faSopenharmony_ci pc = usbd_xfer_get_frame(xfer, 1); 283f9f848faSopenharmony_ci if (usb_fifo_get_data(sc->sc_fifo.fp[USB_FIFO_TX], pc, 284f9f848faSopenharmony_ci 0, UHID_BSIZE, &actlen, 1)) { 285f9f848faSopenharmony_ci if (actlen != size) { 286f9f848faSopenharmony_ci goto tr_error; 287f9f848faSopenharmony_ci } 288f9f848faSopenharmony_ci uhid_fill_set_report 289f9f848faSopenharmony_ci (&req, sc->sc_iface_no, 290f9f848faSopenharmony_ci UHID_OUTPUT_REPORT, id, size); 291f9f848faSopenharmony_ci 292f9f848faSopenharmony_ci pc = usbd_xfer_get_frame(xfer, 0); 293f9f848faSopenharmony_ci usbd_copy_in(pc, 0, &req, sizeof(req)); 294f9f848faSopenharmony_ci 295f9f848faSopenharmony_ci usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 296f9f848faSopenharmony_ci usbd_xfer_set_frame_len(xfer, 1, size); 297f9f848faSopenharmony_ci usbd_xfer_set_frames(xfer, size ? 2 : 1); 298f9f848faSopenharmony_ci usbd_transfer_submit(xfer); 299f9f848faSopenharmony_ci } 300f9f848faSopenharmony_ci return; 301f9f848faSopenharmony_ci 302f9f848faSopenharmony_ci default: 303f9f848faSopenharmony_citr_error: 304f9f848faSopenharmony_ci /* bomb out */ 305f9f848faSopenharmony_ci usb_fifo_get_data_error(sc->sc_fifo.fp[USB_FIFO_TX]); 306f9f848faSopenharmony_ci return; 307f9f848faSopenharmony_ci } 308f9f848faSopenharmony_ci} 309f9f848faSopenharmony_ci 310f9f848faSopenharmony_cistatic void 311f9f848faSopenharmony_ciuhid_read_callback(struct usb_xfer *xfer, usb_error_t error) 312f9f848faSopenharmony_ci{ 313f9f848faSopenharmony_ci struct uhid_softc *sc = usbd_xfer_softc(xfer); 314f9f848faSopenharmony_ci struct usb_device_request req; 315f9f848faSopenharmony_ci struct usb_page_cache *pc; 316f9f848faSopenharmony_ci 317f9f848faSopenharmony_ci DPRINTF("enter state of xfer is %u!\n", USB_GET_STATE(xfer)); 318f9f848faSopenharmony_ci 319f9f848faSopenharmony_ci pc = usbd_xfer_get_frame(xfer, 0); 320f9f848faSopenharmony_ci 321f9f848faSopenharmony_ci switch (USB_GET_STATE(xfer)) { 322f9f848faSopenharmony_ci case USB_ST_TRANSFERRED: 323f9f848faSopenharmony_ci usb_fifo_put_data(sc->sc_fifo.fp[USB_FIFO_RX], pc, sizeof(req), 324f9f848faSopenharmony_ci sc->sc_isize, 1); 325f9f848faSopenharmony_ci return; 326f9f848faSopenharmony_ci 327f9f848faSopenharmony_ci case USB_ST_SETUP: 328f9f848faSopenharmony_ci 329f9f848faSopenharmony_ci if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) > 0) { 330f9f848faSopenharmony_ci uhid_fill_get_report 331f9f848faSopenharmony_ci (&req, sc->sc_iface_no, UHID_INPUT_REPORT, 332f9f848faSopenharmony_ci sc->sc_iid, sc->sc_isize); 333f9f848faSopenharmony_ci 334f9f848faSopenharmony_ci usbd_copy_in(pc, 0, &req, sizeof(req)); 335f9f848faSopenharmony_ci 336f9f848faSopenharmony_ci usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 337f9f848faSopenharmony_ci usbd_xfer_set_frame_len(xfer, 1, sc->sc_isize); 338f9f848faSopenharmony_ci usbd_xfer_set_frames(xfer, sc->sc_isize ? 2 : 1); 339f9f848faSopenharmony_ci usbd_transfer_submit(xfer); 340f9f848faSopenharmony_ci } 341f9f848faSopenharmony_ci return; 342f9f848faSopenharmony_ci 343f9f848faSopenharmony_ci default: /* Error */ 344f9f848faSopenharmony_ci /* bomb out */ 345f9f848faSopenharmony_ci usb_fifo_put_data_error(sc->sc_fifo.fp[USB_FIFO_RX]); 346f9f848faSopenharmony_ci return; 347f9f848faSopenharmony_ci } 348f9f848faSopenharmony_ci} 349f9f848faSopenharmony_ci 350f9f848faSopenharmony_cistatic const struct usb_config uhid_config[UHID_N_TRANSFER] = { 351f9f848faSopenharmony_ci [UHID_INTR_DT_WR] = { 352f9f848faSopenharmony_ci .type = UE_INTERRUPT, 353f9f848faSopenharmony_ci .endpoint = UE_ADDR_ANY, 354f9f848faSopenharmony_ci .direction = UE_DIR_OUT, 355f9f848faSopenharmony_ci .flags = {.pipe_bof = 1,.no_pipe_ok = 1, }, 356f9f848faSopenharmony_ci .bufsize = UHID_BSIZE, 357f9f848faSopenharmony_ci .callback = &uhid_intr_write_callback, 358f9f848faSopenharmony_ci }, 359f9f848faSopenharmony_ci 360f9f848faSopenharmony_ci [UHID_INTR_DT_RD] = { 361f9f848faSopenharmony_ci .type = UE_INTERRUPT, 362f9f848faSopenharmony_ci .endpoint = UE_ADDR_ANY, 363f9f848faSopenharmony_ci .direction = UE_DIR_IN, 364f9f848faSopenharmony_ci .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 365f9f848faSopenharmony_ci .bufsize = UHID_BSIZE, 366f9f848faSopenharmony_ci .callback = &uhid_intr_read_callback, 367f9f848faSopenharmony_ci }, 368f9f848faSopenharmony_ci 369f9f848faSopenharmony_ci [UHID_CTRL_DT_WR] = { 370f9f848faSopenharmony_ci .type = UE_CONTROL, 371f9f848faSopenharmony_ci .endpoint = 0x00, /* Control pipe */ 372f9f848faSopenharmony_ci .direction = UE_DIR_ANY, 373f9f848faSopenharmony_ci .bufsize = sizeof(struct usb_device_request) + UHID_BSIZE, 374f9f848faSopenharmony_ci .callback = &uhid_write_callback, 375f9f848faSopenharmony_ci .timeout = 1000, /* 1 second */ 376f9f848faSopenharmony_ci }, 377f9f848faSopenharmony_ci 378f9f848faSopenharmony_ci [UHID_CTRL_DT_RD] = { 379f9f848faSopenharmony_ci .type = UE_CONTROL, 380f9f848faSopenharmony_ci .endpoint = 0x00, /* Control pipe */ 381f9f848faSopenharmony_ci .direction = UE_DIR_ANY, 382f9f848faSopenharmony_ci .bufsize = sizeof(struct usb_device_request) + UHID_BSIZE, 383f9f848faSopenharmony_ci .callback = &uhid_read_callback, 384f9f848faSopenharmony_ci .timeout = 1000, /* 1 second */ 385f9f848faSopenharmony_ci }, 386f9f848faSopenharmony_ci}; 387f9f848faSopenharmony_ci 388f9f848faSopenharmony_cistatic void 389f9f848faSopenharmony_ciuhid_start_read(struct usb_fifo *fifo) 390f9f848faSopenharmony_ci{ 391f9f848faSopenharmony_ci struct uhid_softc *sc = usb_fifo_softc(fifo); 392f9f848faSopenharmony_ci 393f9f848faSopenharmony_ci if (sc->sc_flags & UHID_FLAG_IMMED) { 394f9f848faSopenharmony_ci usbd_transfer_start(sc->sc_xfer[UHID_CTRL_DT_RD]); 395f9f848faSopenharmony_ci } else { 396f9f848faSopenharmony_ci usbd_transfer_start(sc->sc_xfer[UHID_INTR_DT_RD]); 397f9f848faSopenharmony_ci } 398f9f848faSopenharmony_ci} 399f9f848faSopenharmony_ci 400f9f848faSopenharmony_cistatic void 401f9f848faSopenharmony_ciuhid_stop_read(struct usb_fifo *fifo) 402f9f848faSopenharmony_ci{ 403f9f848faSopenharmony_ci struct uhid_softc *sc = usb_fifo_softc(fifo); 404f9f848faSopenharmony_ci 405f9f848faSopenharmony_ci usbd_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_RD]); 406f9f848faSopenharmony_ci usbd_transfer_stop(sc->sc_xfer[UHID_INTR_DT_RD]); 407f9f848faSopenharmony_ci} 408f9f848faSopenharmony_ci 409f9f848faSopenharmony_cistatic void 410f9f848faSopenharmony_ciuhid_start_write(struct usb_fifo *fifo) 411f9f848faSopenharmony_ci{ 412f9f848faSopenharmony_ci struct uhid_softc *sc = usb_fifo_softc(fifo); 413f9f848faSopenharmony_ci 414f9f848faSopenharmony_ci if ((sc->sc_flags & UHID_FLAG_IMMED) || 415f9f848faSopenharmony_ci sc->sc_xfer[UHID_INTR_DT_WR] == NULL) { 416f9f848faSopenharmony_ci usbd_transfer_start(sc->sc_xfer[UHID_CTRL_DT_WR]); 417f9f848faSopenharmony_ci } else { 418f9f848faSopenharmony_ci usbd_transfer_start(sc->sc_xfer[UHID_INTR_DT_WR]); 419f9f848faSopenharmony_ci } 420f9f848faSopenharmony_ci} 421f9f848faSopenharmony_ci 422f9f848faSopenharmony_cistatic void 423f9f848faSopenharmony_ciuhid_stop_write(struct usb_fifo *fifo) 424f9f848faSopenharmony_ci{ 425f9f848faSopenharmony_ci struct uhid_softc *sc = usb_fifo_softc(fifo); 426f9f848faSopenharmony_ci 427f9f848faSopenharmony_ci usbd_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_WR]); 428f9f848faSopenharmony_ci usbd_transfer_stop(sc->sc_xfer[UHID_INTR_DT_WR]); 429f9f848faSopenharmony_ci} 430f9f848faSopenharmony_ci 431f9f848faSopenharmony_cistatic int 432f9f848faSopenharmony_ciuhid_get_report(struct uhid_softc *sc, uint8_t type, 433f9f848faSopenharmony_ci uint8_t id, void *kern_data, void *user_data, 434f9f848faSopenharmony_ci uint16_t len) 435f9f848faSopenharmony_ci{ 436f9f848faSopenharmony_ci int err; 437f9f848faSopenharmony_ci uint8_t free_data = 0; 438f9f848faSopenharmony_ci 439f9f848faSopenharmony_ci if (kern_data == NULL) { 440f9f848faSopenharmony_ci if (len == 0) { 441f9f848faSopenharmony_ci err = EPERM; 442f9f848faSopenharmony_ci goto done; 443f9f848faSopenharmony_ci } 444f9f848faSopenharmony_ci 445f9f848faSopenharmony_ci kern_data = malloc(len); 446f9f848faSopenharmony_ci if (kern_data == NULL) { 447f9f848faSopenharmony_ci err = ENOMEM; 448f9f848faSopenharmony_ci goto done; 449f9f848faSopenharmony_ci } 450f9f848faSopenharmony_ci free_data = 1; 451f9f848faSopenharmony_ci } 452f9f848faSopenharmony_ci err = usbd_req_get_report(sc->sc_udev, NULL, kern_data, 453f9f848faSopenharmony_ci len, sc->sc_iface_index, type, id); 454f9f848faSopenharmony_ci if (err) { 455f9f848faSopenharmony_ci err = ENXIO; 456f9f848faSopenharmony_ci goto done; 457f9f848faSopenharmony_ci } 458f9f848faSopenharmony_ci if (user_data) { 459f9f848faSopenharmony_ci /* dummy buffer */ 460f9f848faSopenharmony_ci err = copyout(kern_data, user_data, len); 461f9f848faSopenharmony_ci if (err) { 462f9f848faSopenharmony_ci goto done; 463f9f848faSopenharmony_ci } 464f9f848faSopenharmony_ci } 465f9f848faSopenharmony_cidone: 466f9f848faSopenharmony_ci if (free_data) { 467f9f848faSopenharmony_ci free(kern_data); 468f9f848faSopenharmony_ci } 469f9f848faSopenharmony_ci return (err); 470f9f848faSopenharmony_ci} 471f9f848faSopenharmony_ci 472f9f848faSopenharmony_cistatic int 473f9f848faSopenharmony_ciuhid_set_report(struct uhid_softc *sc, uint8_t type, 474f9f848faSopenharmony_ci uint8_t id, void *kern_data, const void *user_data, 475f9f848faSopenharmony_ci uint16_t len) 476f9f848faSopenharmony_ci{ 477f9f848faSopenharmony_ci int err; 478f9f848faSopenharmony_ci uint8_t free_data = 0; 479f9f848faSopenharmony_ci 480f9f848faSopenharmony_ci if (kern_data == NULL) { 481f9f848faSopenharmony_ci if (len == 0) { 482f9f848faSopenharmony_ci err = EPERM; 483f9f848faSopenharmony_ci goto done; 484f9f848faSopenharmony_ci } 485f9f848faSopenharmony_ci 486f9f848faSopenharmony_ci kern_data = malloc(len); 487f9f848faSopenharmony_ci if (kern_data == NULL) { 488f9f848faSopenharmony_ci err = ENOMEM; 489f9f848faSopenharmony_ci goto done; 490f9f848faSopenharmony_ci } 491f9f848faSopenharmony_ci free_data = 1; 492f9f848faSopenharmony_ci err = copyin(user_data, kern_data, len); 493f9f848faSopenharmony_ci if (err) { 494f9f848faSopenharmony_ci goto done; 495f9f848faSopenharmony_ci } 496f9f848faSopenharmony_ci } 497f9f848faSopenharmony_ci err = usbd_req_set_report(sc->sc_udev, NULL, kern_data, 498f9f848faSopenharmony_ci len, sc->sc_iface_index, type, id); 499f9f848faSopenharmony_ci if (err) { 500f9f848faSopenharmony_ci err = ENXIO; 501f9f848faSopenharmony_ci goto done; 502f9f848faSopenharmony_ci } 503f9f848faSopenharmony_cidone: 504f9f848faSopenharmony_ci if (free_data) { 505f9f848faSopenharmony_ci free(kern_data); 506f9f848faSopenharmony_ci } 507f9f848faSopenharmony_ci return (err); 508f9f848faSopenharmony_ci} 509f9f848faSopenharmony_ci 510f9f848faSopenharmony_cistatic int 511f9f848faSopenharmony_ciuhid_open(struct usb_fifo *fifo, int fflags) 512f9f848faSopenharmony_ci{ 513f9f848faSopenharmony_ci struct uhid_softc *sc = usb_fifo_softc(fifo); 514f9f848faSopenharmony_ci 515f9f848faSopenharmony_ci /* 516f9f848faSopenharmony_ci * The buffers are one byte larger than maximum so that one 517f9f848faSopenharmony_ci * can detect too large read/writes and short transfers: 518f9f848faSopenharmony_ci */ 519f9f848faSopenharmony_ci if ((unsigned int)fflags & FREAD) { 520f9f848faSopenharmony_ci /* reset flags */ 521f9f848faSopenharmony_ci mtx_lock(&sc->sc_mtx); 522f9f848faSopenharmony_ci sc->sc_flags &= ~UHID_FLAG_IMMED; 523f9f848faSopenharmony_ci mtx_unlock(&sc->sc_mtx); 524f9f848faSopenharmony_ci 525f9f848faSopenharmony_ci if (usb_fifo_alloc_buffer(fifo, 526f9f848faSopenharmony_ci sc->sc_isize + 1, UHID_FRAME_NUM)) { 527f9f848faSopenharmony_ci return (ENOMEM); 528f9f848faSopenharmony_ci } 529f9f848faSopenharmony_ci } 530f9f848faSopenharmony_ci if ((unsigned int)fflags & FWRITE) { 531f9f848faSopenharmony_ci if (usb_fifo_alloc_buffer(fifo, 532f9f848faSopenharmony_ci sc->sc_osize + 1, UHID_FRAME_NUM)) { 533f9f848faSopenharmony_ci return (ENOMEM); 534f9f848faSopenharmony_ci } 535f9f848faSopenharmony_ci } 536f9f848faSopenharmony_ci 537f9f848faSopenharmony_ci return (0); 538f9f848faSopenharmony_ci} 539f9f848faSopenharmony_ci 540f9f848faSopenharmony_cistatic void 541f9f848faSopenharmony_ciuhid_close(struct usb_fifo *fifo, int fflags) 542f9f848faSopenharmony_ci{ 543f9f848faSopenharmony_ci if ((unsigned int)fflags & (FREAD | FWRITE)) { 544f9f848faSopenharmony_ci usb_fifo_free_buffer(fifo); 545f9f848faSopenharmony_ci } 546f9f848faSopenharmony_ci} 547f9f848faSopenharmony_ci 548f9f848faSopenharmony_cistatic int 549f9f848faSopenharmony_ciuhid_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, 550f9f848faSopenharmony_ci int fflags) 551f9f848faSopenharmony_ci{ 552f9f848faSopenharmony_ci struct uhid_softc *sc = usb_fifo_softc(fifo); 553f9f848faSopenharmony_ci struct usb_gen_descriptor ugd; 554f9f848faSopenharmony_ci uint32_t size; 555f9f848faSopenharmony_ci int error = 0; 556f9f848faSopenharmony_ci uint8_t id; 557f9f848faSopenharmony_ci 558f9f848faSopenharmony_ci switch (cmd) { 559f9f848faSopenharmony_ci case USB_GET_REPORT_DESC: 560f9f848faSopenharmony_ci error = copyin((const void *)addr, &ugd, sizeof(struct usb_gen_descriptor)); 561f9f848faSopenharmony_ci if (error != ENOERR) { 562f9f848faSopenharmony_ci break; 563f9f848faSopenharmony_ci } 564f9f848faSopenharmony_ci if (sc->sc_repdesc_size > ugd.ugd_maxlen) { 565f9f848faSopenharmony_ci size = ugd.ugd_maxlen; 566f9f848faSopenharmony_ci } else { 567f9f848faSopenharmony_ci size = sc->sc_repdesc_size; 568f9f848faSopenharmony_ci } 569f9f848faSopenharmony_ci ugd.ugd_actlen = size; 570f9f848faSopenharmony_ci if (ugd.ugd_data == NULL) 571f9f848faSopenharmony_ci break; /* descriptor length only */ 572f9f848faSopenharmony_ci error = copyout(sc->sc_repdesc_ptr, ugd.ugd_data, size); 573f9f848faSopenharmony_ci if (error == ENOERR) { 574f9f848faSopenharmony_ci error = copyout((const void *)&ugd, addr, sizeof(struct usb_gen_descriptor)); 575f9f848faSopenharmony_ci } 576f9f848faSopenharmony_ci break; 577f9f848faSopenharmony_ci 578f9f848faSopenharmony_ci case USB_SET_IMMED: { 579f9f848faSopenharmony_ci int data; 580f9f848faSopenharmony_ci if (!((unsigned int)fflags & FREAD)) { 581f9f848faSopenharmony_ci error = EPERM; 582f9f848faSopenharmony_ci break; 583f9f848faSopenharmony_ci } 584f9f848faSopenharmony_ci error = copyin((const void *)addr, &data, sizeof(data)); 585f9f848faSopenharmony_ci if (error != ENOERR) { 586f9f848faSopenharmony_ci break; 587f9f848faSopenharmony_ci } 588f9f848faSopenharmony_ci if (data) { 589f9f848faSopenharmony_ci /* do a test read */ 590f9f848faSopenharmony_ci 591f9f848faSopenharmony_ci error = uhid_get_report(sc, UHID_INPUT_REPORT, 592f9f848faSopenharmony_ci sc->sc_iid, NULL, NULL, sc->sc_isize); 593f9f848faSopenharmony_ci if (error) { 594f9f848faSopenharmony_ci break; 595f9f848faSopenharmony_ci } 596f9f848faSopenharmony_ci mtx_lock(&sc->sc_mtx); 597f9f848faSopenharmony_ci sc->sc_flags |= UHID_FLAG_IMMED; 598f9f848faSopenharmony_ci mtx_unlock(&sc->sc_mtx); 599f9f848faSopenharmony_ci } else { 600f9f848faSopenharmony_ci mtx_lock(&sc->sc_mtx); 601f9f848faSopenharmony_ci sc->sc_flags &= ~UHID_FLAG_IMMED; 602f9f848faSopenharmony_ci mtx_unlock(&sc->sc_mtx); 603f9f848faSopenharmony_ci } 604f9f848faSopenharmony_ci break; 605f9f848faSopenharmony_ci } 606f9f848faSopenharmony_ci 607f9f848faSopenharmony_ci case USB_GET_REPORT: 608f9f848faSopenharmony_ci if (!((unsigned int)fflags & FREAD)) { 609f9f848faSopenharmony_ci error = EPERM; 610f9f848faSopenharmony_ci break; 611f9f848faSopenharmony_ci } 612f9f848faSopenharmony_ci error = copyin((const void *)addr, &ugd, sizeof(struct usb_gen_descriptor)); 613f9f848faSopenharmony_ci if (error != ENOERR) { 614f9f848faSopenharmony_ci break; 615f9f848faSopenharmony_ci } 616f9f848faSopenharmony_ci switch (ugd.ugd_report_type) { 617f9f848faSopenharmony_ci case UHID_INPUT_REPORT: 618f9f848faSopenharmony_ci size = sc->sc_isize; 619f9f848faSopenharmony_ci id = sc->sc_iid; 620f9f848faSopenharmony_ci break; 621f9f848faSopenharmony_ci case UHID_OUTPUT_REPORT: 622f9f848faSopenharmony_ci size = sc->sc_osize; 623f9f848faSopenharmony_ci id = sc->sc_oid; 624f9f848faSopenharmony_ci break; 625f9f848faSopenharmony_ci case UHID_FEATURE_REPORT: 626f9f848faSopenharmony_ci size = sc->sc_fsize; 627f9f848faSopenharmony_ci id = sc->sc_fid; 628f9f848faSopenharmony_ci break; 629f9f848faSopenharmony_ci default: 630f9f848faSopenharmony_ci return (EINVAL); 631f9f848faSopenharmony_ci } 632f9f848faSopenharmony_ci if (id != 0) 633f9f848faSopenharmony_ci copyin(ugd.ugd_data, &id, 1); 634f9f848faSopenharmony_ci error = uhid_get_report(sc, ugd.ugd_report_type, id, 635f9f848faSopenharmony_ci NULL, ugd.ugd_data, MIN(ugd.ugd_maxlen, size)); 636f9f848faSopenharmony_ci break; 637f9f848faSopenharmony_ci 638f9f848faSopenharmony_ci case USB_SET_REPORT: 639f9f848faSopenharmony_ci if (!((unsigned int)fflags & FWRITE)) { 640f9f848faSopenharmony_ci error = EPERM; 641f9f848faSopenharmony_ci break; 642f9f848faSopenharmony_ci } 643f9f848faSopenharmony_ci error = copyin((const void *)addr, &ugd, sizeof(struct usb_gen_descriptor)); 644f9f848faSopenharmony_ci if (error != ENOERR) { 645f9f848faSopenharmony_ci break; 646f9f848faSopenharmony_ci } 647f9f848faSopenharmony_ci switch (ugd.ugd_report_type) { 648f9f848faSopenharmony_ci case UHID_INPUT_REPORT: 649f9f848faSopenharmony_ci size = sc->sc_isize; 650f9f848faSopenharmony_ci id = sc->sc_iid; 651f9f848faSopenharmony_ci break; 652f9f848faSopenharmony_ci case UHID_OUTPUT_REPORT: 653f9f848faSopenharmony_ci size = sc->sc_osize; 654f9f848faSopenharmony_ci id = sc->sc_oid; 655f9f848faSopenharmony_ci break; 656f9f848faSopenharmony_ci case UHID_FEATURE_REPORT: 657f9f848faSopenharmony_ci size = sc->sc_fsize; 658f9f848faSopenharmony_ci id = sc->sc_fid; 659f9f848faSopenharmony_ci break; 660f9f848faSopenharmony_ci default: 661f9f848faSopenharmony_ci return (EINVAL); 662f9f848faSopenharmony_ci } 663f9f848faSopenharmony_ci if (id != 0) 664f9f848faSopenharmony_ci copyin(ugd.ugd_data, &id, 1); 665f9f848faSopenharmony_ci error = uhid_set_report(sc, ugd.ugd_report_type, id, 666f9f848faSopenharmony_ci NULL, ugd.ugd_data, MIN(ugd.ugd_maxlen, size)); 667f9f848faSopenharmony_ci break; 668f9f848faSopenharmony_ci 669f9f848faSopenharmony_ci case USB_GET_REPORT_ID: { 670f9f848faSopenharmony_ci int data = 0; 671f9f848faSopenharmony_ci error = copyout((const void *)&data, addr, sizeof(data)); /* XXX: we only support reportid 0? */ 672f9f848faSopenharmony_ci break; 673f9f848faSopenharmony_ci } 674f9f848faSopenharmony_ci 675f9f848faSopenharmony_ci default: 676f9f848faSopenharmony_ci error = ENOIOCTL; 677f9f848faSopenharmony_ci break; 678f9f848faSopenharmony_ci } 679f9f848faSopenharmony_ci 680f9f848faSopenharmony_ci return (error); 681f9f848faSopenharmony_ci} 682f9f848faSopenharmony_ci 683f9f848faSopenharmony_cistatic int 684f9f848faSopenharmony_ciuhid_ioctl_post(struct usb_fifo *fifo, u_long cmd, void *addr, 685f9f848faSopenharmony_ci int fflags) 686f9f848faSopenharmony_ci{ 687f9f848faSopenharmony_ci int error; 688f9f848faSopenharmony_ci 689f9f848faSopenharmony_ci switch (cmd) { 690f9f848faSopenharmony_ci case USB_GET_DEVICEINFO: 691f9f848faSopenharmony_ci error = ugen_fill_deviceinfo(fifo, addr); 692f9f848faSopenharmony_ci break; 693f9f848faSopenharmony_ci 694f9f848faSopenharmony_ci default: 695f9f848faSopenharmony_ci error = EINVAL; 696f9f848faSopenharmony_ci break; 697f9f848faSopenharmony_ci } 698f9f848faSopenharmony_ci 699f9f848faSopenharmony_ci return (error); 700f9f848faSopenharmony_ci} 701f9f848faSopenharmony_ci 702f9f848faSopenharmony_cistatic const STRUCT_USB_HOST_ID uhid_devs[] = { 703f9f848faSopenharmony_ci /* generic HID class */ 704f9f848faSopenharmony_ci {USB_IFACE_CLASS(UICLASS_HID),}, 705f9f848faSopenharmony_ci /* the Xbox 360 gamepad doesn't use the HID class */ 706f9f848faSopenharmony_ci {USB_IFACE_CLASS(UICLASS_VENDOR), 707f9f848faSopenharmony_ci USB_IFACE_SUBCLASS(UISUBCLASS_XBOX360_CONTROLLER), 708f9f848faSopenharmony_ci USB_IFACE_PROTOCOL(UIPROTO_XBOX360_GAMEPAD),}, 709f9f848faSopenharmony_ci}; 710f9f848faSopenharmony_ci 711f9f848faSopenharmony_cistatic int 712f9f848faSopenharmony_ciuhid_probe(device_t dev) 713f9f848faSopenharmony_ci{ 714f9f848faSopenharmony_ci struct usb_attach_arg *uaa = device_get_ivars(dev); 715f9f848faSopenharmony_ci int error; 716f9f848faSopenharmony_ci 717f9f848faSopenharmony_ci DPRINTFN(11, "\n"); 718f9f848faSopenharmony_ci 719f9f848faSopenharmony_ci if (uaa->usb_mode != USB_MODE_HOST) 720f9f848faSopenharmony_ci return (ENXIO); 721f9f848faSopenharmony_ci 722f9f848faSopenharmony_ci error = usbd_lookup_id_by_uaa(uhid_devs, sizeof(uhid_devs), uaa); 723f9f848faSopenharmony_ci if (error) 724f9f848faSopenharmony_ci return (error); 725f9f848faSopenharmony_ci 726f9f848faSopenharmony_ci if (usb_test_quirk(uaa, UQ_HID_IGNORE)) 727f9f848faSopenharmony_ci return (ENXIO); 728f9f848faSopenharmony_ci 729f9f848faSopenharmony_ci return (0); 730f9f848faSopenharmony_ci} 731f9f848faSopenharmony_ci 732f9f848faSopenharmony_cistatic int 733f9f848faSopenharmony_ciuhid_attach(device_t dev) 734f9f848faSopenharmony_ci{ 735f9f848faSopenharmony_ci struct usb_attach_arg *uaa = device_get_ivars(dev); 736f9f848faSopenharmony_ci struct uhid_softc *sc = device_get_softc(dev); 737f9f848faSopenharmony_ci int unit = device_get_unit(dev); 738f9f848faSopenharmony_ci int error = 0; 739f9f848faSopenharmony_ci int32_t ret; 740f9f848faSopenharmony_ci 741f9f848faSopenharmony_ci DPRINTFN(10, "sc=%p\n", sc); 742f9f848faSopenharmony_ci 743f9f848faSopenharmony_ci device_set_usb_desc(dev); 744f9f848faSopenharmony_ci 745f9f848faSopenharmony_ci mtx_init(&sc->sc_mtx, "uhid lock", NULL, MTX_DEF | MTX_RECURSE); 746f9f848faSopenharmony_ci 747f9f848faSopenharmony_ci sc->sc_udev = uaa->device; 748f9f848faSopenharmony_ci 749f9f848faSopenharmony_ci sc->sc_iface_no = uaa->info.bIfaceNum; 750f9f848faSopenharmony_ci sc->sc_iface_index = uaa->info.bIfaceIndex; 751f9f848faSopenharmony_ci 752f9f848faSopenharmony_ci error = usbd_transfer_setup(uaa->device, 753f9f848faSopenharmony_ci &uaa->info.bIfaceIndex, sc->sc_xfer, uhid_config, 754f9f848faSopenharmony_ci UHID_N_TRANSFER, sc, &sc->sc_mtx); 755f9f848faSopenharmony_ci 756f9f848faSopenharmony_ci if (error) { 757f9f848faSopenharmony_ci DPRINTF("error=%s\n", usbd_errstr(error)); 758f9f848faSopenharmony_ci goto detach; 759f9f848faSopenharmony_ci } 760f9f848faSopenharmony_ci if (uaa->info.idVendor == USB_VENDOR_WACOM) { 761f9f848faSopenharmony_ci /* the report descriptor for the Wacom Graphire is broken */ 762f9f848faSopenharmony_ci 763f9f848faSopenharmony_ci if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE) { 764f9f848faSopenharmony_ci sc->sc_repdesc_size = sizeof(uhid_graphire_report_descr); 765f9f848faSopenharmony_ci sc->sc_repdesc_ptr = __DECONST(void *, &uhid_graphire_report_descr); 766f9f848faSopenharmony_ci sc->sc_flags |= UHID_FLAG_STATIC_DESC; 767f9f848faSopenharmony_ci 768f9f848faSopenharmony_ci } else if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) { 769f9f848faSopenharmony_ci static uint8_t reportbuf[] = {2, 2, 2}; 770f9f848faSopenharmony_ci 771f9f848faSopenharmony_ci /* 772f9f848faSopenharmony_ci * The Graphire3 needs 0x0202 to be written to 773f9f848faSopenharmony_ci * feature report ID 2 before it'll start 774f9f848faSopenharmony_ci * returning digitizer data. 775f9f848faSopenharmony_ci */ 776f9f848faSopenharmony_ci error = usbd_req_set_report(uaa->device, NULL, 777f9f848faSopenharmony_ci reportbuf, sizeof(reportbuf), 778f9f848faSopenharmony_ci uaa->info.bIfaceIndex, UHID_FEATURE_REPORT, 2); 779f9f848faSopenharmony_ci 780f9f848faSopenharmony_ci if (error) { 781f9f848faSopenharmony_ci DPRINTF("set report failed, error=%s (ignored)\n", 782f9f848faSopenharmony_ci usbd_errstr(error)); 783f9f848faSopenharmony_ci } 784f9f848faSopenharmony_ci sc->sc_repdesc_size = sizeof(uhid_graphire3_4x5_report_descr); 785f9f848faSopenharmony_ci sc->sc_repdesc_ptr = __DECONST(void *, &uhid_graphire3_4x5_report_descr); 786f9f848faSopenharmony_ci sc->sc_flags |= UHID_FLAG_STATIC_DESC; 787f9f848faSopenharmony_ci } 788f9f848faSopenharmony_ci } else if ((uaa->info.bInterfaceClass == UICLASS_VENDOR) && 789f9f848faSopenharmony_ci (uaa->info.bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER) && 790f9f848faSopenharmony_ci (uaa->info.bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)) { 791f9f848faSopenharmony_ci static const uint8_t reportbuf[3] = {1, 3, 0}; 792f9f848faSopenharmony_ci /* 793f9f848faSopenharmony_ci * Turn off the four LEDs on the gamepad which 794f9f848faSopenharmony_ci * are blinking by default: 795f9f848faSopenharmony_ci */ 796f9f848faSopenharmony_ci error = usbd_req_set_report(uaa->device, NULL, 797f9f848faSopenharmony_ci __DECONST(void *, reportbuf), sizeof(reportbuf), 798f9f848faSopenharmony_ci uaa->info.bIfaceIndex, UHID_OUTPUT_REPORT, 0); 799f9f848faSopenharmony_ci if (error) { 800f9f848faSopenharmony_ci DPRINTF("set output report failed, error=%s (ignored)\n", 801f9f848faSopenharmony_ci usbd_errstr(error)); 802f9f848faSopenharmony_ci } 803f9f848faSopenharmony_ci /* the Xbox 360 gamepad has no report descriptor */ 804f9f848faSopenharmony_ci sc->sc_repdesc_size = sizeof(uhid_xb360gp_report_descr); 805f9f848faSopenharmony_ci sc->sc_repdesc_ptr = __DECONST(void *, &uhid_xb360gp_report_descr); 806f9f848faSopenharmony_ci sc->sc_flags |= UHID_FLAG_STATIC_DESC; 807f9f848faSopenharmony_ci } 808f9f848faSopenharmony_ci 809f9f848faSopenharmony_ci if (sc->sc_repdesc_ptr == NULL) { 810f9f848faSopenharmony_ci error = usbd_req_get_hid_desc(uaa->device, NULL, 811f9f848faSopenharmony_ci &sc->sc_repdesc_ptr, &sc->sc_repdesc_size, 812f9f848faSopenharmony_ci M_USBDEV, uaa->info.bIfaceIndex); 813f9f848faSopenharmony_ci 814f9f848faSopenharmony_ci if (error) { 815f9f848faSopenharmony_ci device_printf(dev, "no report descriptor\n"); 816f9f848faSopenharmony_ci goto detach; 817f9f848faSopenharmony_ci } 818f9f848faSopenharmony_ci } 819f9f848faSopenharmony_ci 820f9f848faSopenharmony_ci error = usbd_req_set_idle(uaa->device, NULL, 821f9f848faSopenharmony_ci uaa->info.bIfaceIndex, 0, 0); 822f9f848faSopenharmony_ci 823f9f848faSopenharmony_ci if (error) { 824f9f848faSopenharmony_ci DPRINTF("set idle failed, error=%s (ignored)\n", 825f9f848faSopenharmony_ci usbd_errstr(error)); 826f9f848faSopenharmony_ci } 827f9f848faSopenharmony_ci 828f9f848faSopenharmony_ci sc->sc_isize = hid_report_size 829f9f848faSopenharmony_ci (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_input, &sc->sc_iid); 830f9f848faSopenharmony_ci 831f9f848faSopenharmony_ci sc->sc_osize = hid_report_size 832f9f848faSopenharmony_ci (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_output, &sc->sc_oid); 833f9f848faSopenharmony_ci 834f9f848faSopenharmony_ci sc->sc_fsize = hid_report_size 835f9f848faSopenharmony_ci (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_feature, &sc->sc_fid); 836f9f848faSopenharmony_ci 837f9f848faSopenharmony_ci if (sc->sc_isize > UHID_BSIZE) { 838f9f848faSopenharmony_ci DPRINTF("input size is too large, " 839f9f848faSopenharmony_ci "%d bytes (truncating)\n", 840f9f848faSopenharmony_ci sc->sc_isize); 841f9f848faSopenharmony_ci sc->sc_isize = UHID_BSIZE; 842f9f848faSopenharmony_ci } 843f9f848faSopenharmony_ci if (sc->sc_osize > UHID_BSIZE) { 844f9f848faSopenharmony_ci DPRINTF("output size is too large, " 845f9f848faSopenharmony_ci "%d bytes (truncating)\n", 846f9f848faSopenharmony_ci sc->sc_osize); 847f9f848faSopenharmony_ci sc->sc_osize = UHID_BSIZE; 848f9f848faSopenharmony_ci } 849f9f848faSopenharmony_ci if (sc->sc_fsize > UHID_BSIZE) { 850f9f848faSopenharmony_ci DPRINTF("feature size is too large, " 851f9f848faSopenharmony_ci "%d bytes (truncating)\n", 852f9f848faSopenharmony_ci sc->sc_fsize); 853f9f848faSopenharmony_ci sc->sc_fsize = UHID_BSIZE; 854f9f848faSopenharmony_ci } 855f9f848faSopenharmony_ci 856f9f848faSopenharmony_ci error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, 857f9f848faSopenharmony_ci &uhid_fifo_methods, &sc->sc_fifo, 858f9f848faSopenharmony_ci unit, -1, uaa->info.bIfaceIndex, 859f9f848faSopenharmony_ci UID_ROOT, GID_OPERATOR, 0644); 860f9f848faSopenharmony_ci if (error) { 861f9f848faSopenharmony_ci goto detach; 862f9f848faSopenharmony_ci } 863f9f848faSopenharmony_ci 864f9f848faSopenharmony_ci sc->input_dev = (InputDevice*)zalloc(sizeof(InputDevice)); 865f9f848faSopenharmony_ci if (sc->input_dev) { 866f9f848faSopenharmony_ci if (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) { 867f9f848faSopenharmony_ci sc->input_dev->devType = INDEV_TYPE_MOUSE; 868f9f848faSopenharmony_ci sc->input_dev->devName = "mouse"; 869f9f848faSopenharmony_ci } else { 870f9f848faSopenharmony_ci sc->input_dev->devType = INDEV_TYPE_UNKNOWN; 871f9f848faSopenharmony_ci sc->input_dev->devName = "other"; 872f9f848faSopenharmony_ci } 873f9f848faSopenharmony_ci 874f9f848faSopenharmony_ci ret = RegisterInputDevice(sc->input_dev); 875f9f848faSopenharmony_ci if (ret != HDF_SUCCESS) { 876f9f848faSopenharmony_ci DPRINTF("%s register failed, ret = %d!\n", sc->input_dev->devName, ret); 877f9f848faSopenharmony_ci free(sc->input_dev); 878f9f848faSopenharmony_ci sc->input_dev = NULL; 879f9f848faSopenharmony_ci } else if (sc->input_dev->devType == INDEV_TYPE_MOUSE) { 880f9f848faSopenharmony_ci DPRINTF("mouse register success!\n"); 881f9f848faSopenharmony_ci mtx_lock(&sc->sc_mtx); 882f9f848faSopenharmony_ci sc->sc_flags &= ~UHID_FLAG_IMMED; 883f9f848faSopenharmony_ci usbd_transfer_start(sc->sc_xfer[UHID_INTR_DT_RD]); 884f9f848faSopenharmony_ci mtx_unlock(&sc->sc_mtx); 885f9f848faSopenharmony_ci } 886f9f848faSopenharmony_ci } 887f9f848faSopenharmony_ci 888f9f848faSopenharmony_ci return (0); /* success */ 889f9f848faSopenharmony_ci 890f9f848faSopenharmony_cidetach: 891f9f848faSopenharmony_ci uhid_detach(dev); 892f9f848faSopenharmony_ci return (ENOMEM); 893f9f848faSopenharmony_ci} 894f9f848faSopenharmony_ci 895f9f848faSopenharmony_cistatic int 896f9f848faSopenharmony_ciuhid_detach(device_t dev) 897f9f848faSopenharmony_ci{ 898f9f848faSopenharmony_ci struct uhid_softc *sc = device_get_softc(dev); 899f9f848faSopenharmony_ci 900f9f848faSopenharmony_ci DPRINTF("enter\n"); 901f9f848faSopenharmony_ci 902f9f848faSopenharmony_ci if (sc->input_dev) { 903f9f848faSopenharmony_ci if (sc->input_dev->devType == INDEV_TYPE_MOUSE) { 904f9f848faSopenharmony_ci usbd_transfer_stop(sc->sc_xfer[UHID_INTR_DT_RD]); 905f9f848faSopenharmony_ci } 906f9f848faSopenharmony_ci 907f9f848faSopenharmony_ci UnregisterInputDevice(sc->input_dev); 908f9f848faSopenharmony_ci free(sc->input_dev); 909f9f848faSopenharmony_ci sc->input_dev = NULL; 910f9f848faSopenharmony_ci } 911f9f848faSopenharmony_ci 912f9f848faSopenharmony_ci usb_fifo_detach(&sc->sc_fifo); 913f9f848faSopenharmony_ci 914f9f848faSopenharmony_ci usbd_transfer_unsetup(sc->sc_xfer, UHID_N_TRANSFER); 915f9f848faSopenharmony_ci 916f9f848faSopenharmony_ci if (sc->sc_repdesc_ptr) { 917f9f848faSopenharmony_ci if (!(sc->sc_flags & UHID_FLAG_STATIC_DESC)) { 918f9f848faSopenharmony_ci free(sc->sc_repdesc_ptr); 919f9f848faSopenharmony_ci } 920f9f848faSopenharmony_ci } 921f9f848faSopenharmony_ci mtx_destroy(&sc->sc_mtx); 922f9f848faSopenharmony_ci 923f9f848faSopenharmony_ci return (0); 924f9f848faSopenharmony_ci} 925f9f848faSopenharmony_ci 926f9f848faSopenharmony_cistatic devclass_t uhid_devclass; 927f9f848faSopenharmony_ci 928f9f848faSopenharmony_cistatic device_method_t uhid_methods[] = { 929f9f848faSopenharmony_ci DEVMETHOD(device_probe, uhid_probe), 930f9f848faSopenharmony_ci DEVMETHOD(device_attach, uhid_attach), 931f9f848faSopenharmony_ci DEVMETHOD(device_detach, uhid_detach), 932f9f848faSopenharmony_ci 933f9f848faSopenharmony_ci DEVMETHOD_END 934f9f848faSopenharmony_ci}; 935f9f848faSopenharmony_ci 936f9f848faSopenharmony_cistatic driver_t uhid_driver = { 937f9f848faSopenharmony_ci .name = "uhid", 938f9f848faSopenharmony_ci .methods = uhid_methods, 939f9f848faSopenharmony_ci .size = sizeof(struct uhid_softc), 940f9f848faSopenharmony_ci}; 941f9f848faSopenharmony_ci 942f9f848faSopenharmony_ciDRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, NULL, 0); 943