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