1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2014 Leon Dang <ldang@nahannisys.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/time.h>
33
34#include <machine/vmm_snapshot.h>
35
36#include <pthread.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40
41#include <dev/usb/usb.h>
42#include <dev/usb/usbdi.h>
43
44#include "usb_emul.h"
45#include "console.h"
46#include "bhyvegc.h"
47#include "debug.h"
48
49static int umouse_debug = 0;
50#define	DPRINTF(params) if (umouse_debug) PRINTLN params
51#define	WPRINTF(params) PRINTLN params
52
53/* USB endpoint context (1-15) for reporting mouse data events*/
54#define	UMOUSE_INTR_ENDPT	1
55
56#define UMOUSE_REPORT_DESC_TYPE	0x22
57
58#define	UMOUSE_GET_REPORT	0x01
59#define	UMOUSE_GET_IDLE		0x02
60#define	UMOUSE_GET_PROTOCOL	0x03
61#define	UMOUSE_SET_REPORT	0x09
62#define	UMOUSE_SET_IDLE		0x0A
63#define	UMOUSE_SET_PROTOCOL	0x0B
64
65#define HSETW(ptr, val)   ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
66
67enum {
68	UMSTR_LANG,
69	UMSTR_MANUFACTURER,
70	UMSTR_PRODUCT,
71	UMSTR_SERIAL,
72	UMSTR_CONFIG,
73	UMSTR_MAX
74};
75
76static const char *umouse_desc_strings[] = {
77	"\x09\x04",
78	"BHYVE",
79	"HID Tablet",
80	"01",
81	"HID Tablet Device",
82};
83
84struct umouse_hid_descriptor {
85	uint8_t	bLength;
86	uint8_t	bDescriptorType;
87	uint8_t	bcdHID[2];
88	uint8_t	bCountryCode;
89	uint8_t	bNumDescriptors;
90	uint8_t	bReportDescriptorType;
91	uint8_t	wItemLength[2];
92} __packed;
93
94struct umouse_config_desc {
95	struct usb_config_descriptor		confd;
96	struct usb_interface_descriptor		ifcd;
97	struct umouse_hid_descriptor		hidd;
98	struct usb_endpoint_descriptor		endpd;
99	struct usb_endpoint_ss_comp_descriptor	sscompd;
100} __packed;
101
102#define MOUSE_MAX_X	0x8000
103#define MOUSE_MAX_Y	0x8000
104
105static const uint8_t umouse_report_desc[] = {
106	0x05, 0x01,		/* USAGE_PAGE (Generic Desktop)		*/
107	0x09, 0x02,		/* USAGE (Mouse)			*/
108	0xa1, 0x01,		/* COLLECTION (Application) 		*/
109	0x09, 0x01,		/*   USAGE (Pointer)			*/
110	0xa1, 0x00,		/*   COLLECTION (Physical)		*/
111	0x05, 0x09,		/*     USAGE_PAGE (Button)		*/
112	0x19, 0x01,		/*     USAGE_MINIMUM (Button 1)		*/
113	0x29, 0x03,		/*     USAGE_MAXIMUM (Button 3)		*/
114	0x15, 0x00,		/*     LOGICAL_MINIMUM (0)		*/
115	0x25, 0x01,		/*     LOGICAL_MAXIMUM (1)		*/
116	0x75, 0x01,		/*     REPORT_SIZE (1)			*/
117	0x95, 0x03,		/*     REPORT_COUNT (3)			*/
118	0x81, 0x02,		/*     INPUT (Data,Var,Abs); 3 buttons	*/
119	0x75, 0x05,		/*     REPORT_SIZE (5)			*/
120	0x95, 0x01,		/*     REPORT_COUNT (1)			*/
121	0x81, 0x03,		/*     INPUT (Cnst,Var,Abs); padding	*/
122	0x05, 0x01,		/*     USAGE_PAGE (Generic Desktop)	*/
123	0x09, 0x30,		/*     USAGE (X)			*/
124	0x09, 0x31,		/*     USAGE (Y)			*/
125	0x35, 0x00,		/*     PHYSICAL_MINIMUM (0)		*/
126	0x46, 0xff, 0x7f,	/*     PHYSICAL_MAXIMUM (0x7fff)	*/
127	0x15, 0x00,		/*     LOGICAL_MINIMUM (0)		*/
128	0x26, 0xff, 0x7f,	/*     LOGICAL_MAXIMUM (0x7fff)		*/
129	0x75, 0x10,		/*     REPORT_SIZE (16)			*/
130	0x95, 0x02,		/*     REPORT_COUNT (2)			*/
131	0x81, 0x02,		/*     INPUT (Data,Var,Abs)		*/
132	0x05, 0x01,		/*     USAGE Page (Generic Desktop)	*/
133	0x09, 0x38,		/*     USAGE (Wheel)			*/
134	0x35, 0x00,		/*     PHYSICAL_MINIMUM (0)		*/
135	0x45, 0x00,		/*     PHYSICAL_MAXIMUM (0)		*/
136	0x15, 0x81,		/*     LOGICAL_MINIMUM (-127)		*/
137	0x25, 0x7f,		/*     LOGICAL_MAXIMUM (127)		*/
138	0x75, 0x08,		/*     REPORT_SIZE (8)			*/
139	0x95, 0x01,		/*     REPORT_COUNT (1)			*/
140	0x81, 0x06,		/*     INPUT (Data,Var,Rel)		*/
141	0xc0,			/*   END_COLLECTION			*/
142	0xc0			/* END_COLLECTION			*/
143};
144
145struct umouse_report {
146	uint8_t	buttons;	/* bits: 0 left, 1 right, 2 middle */
147	int16_t	x;		/* x position */
148	int16_t	y;		/* y position */
149	int8_t	z;		/* z wheel position */
150} __packed;
151
152
153#define	MSETW(ptr, val)	ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
154
155static struct usb_device_descriptor umouse_dev_desc = {
156	.bLength = sizeof(umouse_dev_desc),
157	.bDescriptorType = UDESC_DEVICE,
158	MSETW(.bcdUSB, UD_USB_3_0),
159	.bMaxPacketSize = 8,			/* max packet size */
160	MSETW(.idVendor, 0xFB5D),		/* vendor */
161	MSETW(.idProduct, 0x0001),		/* product */
162	MSETW(.bcdDevice, 0),			/* device version */
163	.iManufacturer = UMSTR_MANUFACTURER,
164	.iProduct = UMSTR_PRODUCT,
165	.iSerialNumber = UMSTR_SERIAL,
166	.bNumConfigurations = 1,
167};
168
169static struct umouse_config_desc umouse_confd = {
170	.confd = {
171		.bLength = sizeof(umouse_confd.confd),
172		.bDescriptorType = UDESC_CONFIG,
173		.wTotalLength[0] = sizeof(umouse_confd),
174		.bNumInterface = 1,
175		.bConfigurationValue = 1,
176		.iConfiguration = UMSTR_CONFIG,
177		.bmAttributes = UC_BUS_POWERED | UC_REMOTE_WAKEUP,
178		.bMaxPower = 0,
179	},
180	.ifcd = {
181		.bLength = sizeof(umouse_confd.ifcd),
182		.bDescriptorType = UDESC_INTERFACE,
183		.bNumEndpoints = 1,
184		.bInterfaceClass = UICLASS_HID,
185		.bInterfaceSubClass = UISUBCLASS_BOOT,
186		.bInterfaceProtocol = UIPROTO_MOUSE,
187	},
188	.hidd = {
189		.bLength = sizeof(umouse_confd.hidd),
190		.bDescriptorType = 0x21,
191		.bcdHID = { 0x01, 0x10 },
192		.bCountryCode = 0,
193		.bNumDescriptors = 1,
194		.bReportDescriptorType = UMOUSE_REPORT_DESC_TYPE,
195		.wItemLength = { sizeof(umouse_report_desc), 0 },
196	},
197	.endpd = {
198		.bLength = sizeof(umouse_confd.endpd),
199		.bDescriptorType = UDESC_ENDPOINT,
200		.bEndpointAddress = UE_DIR_IN | UMOUSE_INTR_ENDPT,
201		.bmAttributes = UE_INTERRUPT,
202		.wMaxPacketSize[0] = 8,
203		.bInterval = 0xA,
204	},
205	.sscompd = {
206		.bLength = sizeof(umouse_confd.sscompd),
207		.bDescriptorType = UDESC_ENDPOINT_SS_COMP,
208		.bMaxBurst = 0,
209		.bmAttributes = 0,
210		MSETW(.wBytesPerInterval, 0),
211	},
212};
213
214
215struct umouse_bos_desc {
216	struct usb_bos_descriptor		bosd;
217	struct usb_devcap_ss_descriptor		usbssd;
218} __packed;
219
220
221struct umouse_bos_desc umouse_bosd = {
222	.bosd = {
223		.bLength = sizeof(umouse_bosd.bosd),
224		.bDescriptorType = UDESC_BOS,
225		HSETW(.wTotalLength, sizeof(umouse_bosd)),
226		.bNumDeviceCaps = 1,
227	},
228	.usbssd = {
229		.bLength = sizeof(umouse_bosd.usbssd),
230		.bDescriptorType = UDESC_DEVICE_CAPABILITY,
231		.bDevCapabilityType = 3,
232		.bmAttributes = 0,
233		HSETW(.wSpeedsSupported, 0x08),
234		.bFunctionalitySupport = 3,
235		.bU1DevExitLat = 0xa,   /* dummy - not used */
236		.wU2DevExitLat = { 0x20, 0x00 },
237	}
238};
239
240
241struct umouse_softc {
242	struct usb_hci *hci;
243
244	char	*opt;
245
246	struct umouse_report um_report;
247	int	newdata;
248	struct {
249		uint8_t	idle;
250		uint8_t	protocol;
251		uint8_t	feature;
252	} hid;
253
254	pthread_mutex_t	mtx;
255	pthread_mutex_t	ev_mtx;
256	int		polling;
257	struct timeval	prev_evt;
258};
259
260static void
261umouse_event(uint8_t button, int x, int y, void *arg)
262{
263	struct umouse_softc *sc;
264	struct bhyvegc_image *gc;
265
266	gc = console_get_image();
267	if (gc == NULL) {
268		/* not ready */
269		return;
270	}
271
272	sc = arg;
273
274	pthread_mutex_lock(&sc->mtx);
275
276	sc->um_report.buttons = 0;
277	sc->um_report.z = 0;
278
279	if (button & 0x01)
280		sc->um_report.buttons |= 0x01;	/* left */
281	if (button & 0x02)
282		sc->um_report.buttons |= 0x04;	/* middle */
283	if (button & 0x04)
284		sc->um_report.buttons |= 0x02;	/* right */
285	if (button & 0x8)
286		sc->um_report.z = 1;
287	if (button & 0x10)
288		sc->um_report.z = -1;
289
290	/* scale coords to mouse resolution */
291	sc->um_report.x = MOUSE_MAX_X * x / gc->width;
292	sc->um_report.y = MOUSE_MAX_Y * y / gc->height;
293	sc->newdata = 1;
294	pthread_mutex_unlock(&sc->mtx);
295
296	pthread_mutex_lock(&sc->ev_mtx);
297	sc->hci->hci_intr(sc->hci, UE_DIR_IN | UMOUSE_INTR_ENDPT);
298	pthread_mutex_unlock(&sc->ev_mtx);
299}
300
301static void *
302umouse_init(struct usb_hci *hci, char *opt)
303{
304	struct umouse_softc *sc;
305
306	sc = calloc(1, sizeof(struct umouse_softc));
307	sc->hci = hci;
308
309	sc->hid.protocol = 1;	/* REPORT protocol */
310	sc->opt = strdup(opt);
311	pthread_mutex_init(&sc->mtx, NULL);
312	pthread_mutex_init(&sc->ev_mtx, NULL);
313
314	console_ptr_register(umouse_event, sc, 10);
315
316	return (sc);
317}
318
319#define	UREQ(x,y)	((x) | ((y) << 8))
320
321static int
322umouse_request(void *scarg, struct usb_data_xfer *xfer)
323{
324	struct umouse_softc *sc;
325	struct usb_data_xfer_block *data;
326	const char *str;
327	uint16_t value;
328	uint16_t index;
329	uint16_t len;
330	uint16_t slen;
331	uint8_t *udata;
332	int	err;
333	int	i, idx;
334	int	eshort;
335
336	sc = scarg;
337
338	data = NULL;
339	udata = NULL;
340	idx = xfer->head;
341	for (i = 0; i < xfer->ndata; i++) {
342		xfer->data[idx].bdone = 0;
343		if (data == NULL && USB_DATA_OK(xfer,i)) {
344			data = &xfer->data[idx];
345			udata = data->buf;
346		}
347
348		xfer->data[idx].processed = 1;
349		idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
350	}
351
352	err = USB_ERR_NORMAL_COMPLETION;
353	eshort = 0;
354
355	if (!xfer->ureq) {
356		DPRINTF(("umouse_request: port %d", sc->hci->hci_port));
357		goto done;
358	}
359
360	value = UGETW(xfer->ureq->wValue);
361	index = UGETW(xfer->ureq->wIndex);
362	len = UGETW(xfer->ureq->wLength);
363
364	DPRINTF(("umouse_request: port %d, type 0x%x, req 0x%x, val 0x%x, "
365	         "idx 0x%x, len %u",
366	         sc->hci->hci_port, xfer->ureq->bmRequestType,
367	         xfer->ureq->bRequest, value, index, len));
368
369	switch (UREQ(xfer->ureq->bRequest, xfer->ureq->bmRequestType)) {
370	case UREQ(UR_GET_CONFIG, UT_READ_DEVICE):
371		DPRINTF(("umouse: (UR_GET_CONFIG, UT_READ_DEVICE)"));
372		if (!data)
373			break;
374
375		*udata = umouse_confd.confd.bConfigurationValue;
376		data->blen = len > 0 ? len - 1 : 0;
377		eshort = data->blen > 0;
378		data->bdone += 1;
379		break;
380
381	case UREQ(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
382		DPRINTF(("umouse: (UR_GET_DESCRIPTOR, UT_READ_DEVICE) val %x",
383		        value >> 8));
384		if (!data)
385			break;
386
387		switch (value >> 8) {
388		case UDESC_DEVICE:
389			DPRINTF(("umouse: (->UDESC_DEVICE) len %u ?= "
390			         "sizeof(umouse_dev_desc) %lu",
391			         len, sizeof(umouse_dev_desc)));
392			if ((value & 0xFF) != 0) {
393				err = USB_ERR_STALLED;
394				goto done;
395			}
396			if (len > sizeof(umouse_dev_desc)) {
397				data->blen = len - sizeof(umouse_dev_desc);
398				len = sizeof(umouse_dev_desc);
399			} else
400				data->blen = 0;
401			memcpy(data->buf, &umouse_dev_desc, len);
402			data->bdone += len;
403			break;
404
405		case UDESC_CONFIG:
406			DPRINTF(("umouse: (->UDESC_CONFIG)"));
407			if ((value & 0xFF) != 0) {
408				err = USB_ERR_STALLED;
409				goto done;
410			}
411			if (len > sizeof(umouse_confd)) {
412				data->blen = len - sizeof(umouse_confd);
413				len = sizeof(umouse_confd);
414			} else
415				data->blen = 0;
416
417			memcpy(data->buf, &umouse_confd, len);
418			data->bdone += len;
419			break;
420
421		case UDESC_STRING:
422			DPRINTF(("umouse: (->UDESC_STRING)"));
423			str = NULL;
424			if ((value & 0xFF) < UMSTR_MAX)
425				str = umouse_desc_strings[value & 0xFF];
426			else
427				goto done;
428
429			if ((value & 0xFF) == UMSTR_LANG) {
430				udata[0] = 4;
431				udata[1] = UDESC_STRING;
432				data->blen = len - 2;
433				len -= 2;
434				data->bdone += 2;
435
436				if (len >= 2) {
437					udata[2] = str[0];
438					udata[3] = str[1];
439					data->blen -= 2;
440					data->bdone += 2;
441				} else
442					data->blen = 0;
443
444				goto done;
445			}
446
447			slen = 2 + strlen(str) * 2;
448			udata[0] = slen;
449			udata[1] = UDESC_STRING;
450
451			if (len > slen) {
452				data->blen = len - slen;
453				len = slen;
454			} else
455				data->blen = 0;
456			for (i = 2; i < len; i += 2) {
457				udata[i] = *str++;
458				udata[i+1] = '\0';
459			}
460			data->bdone += slen;
461
462			break;
463
464		case UDESC_BOS:
465			DPRINTF(("umouse: USB3 BOS"));
466			if (len > sizeof(umouse_bosd)) {
467				data->blen = len - sizeof(umouse_bosd);
468				len = sizeof(umouse_bosd);
469			} else
470				data->blen = 0;
471			memcpy(udata, &umouse_bosd, len);
472			data->bdone += len;
473			break;
474
475		default:
476			DPRINTF(("umouse: unknown(%d)->ERROR", value >> 8));
477			err = USB_ERR_STALLED;
478			goto done;
479		}
480		eshort = data->blen > 0;
481		break;
482
483	case UREQ(UR_GET_DESCRIPTOR, UT_READ_INTERFACE):
484		DPRINTF(("umouse: (UR_GET_DESCRIPTOR, UT_READ_INTERFACE) "
485		         "0x%x", (value >> 8)));
486		if (!data)
487			break;
488
489		switch (value >> 8) {
490		case UMOUSE_REPORT_DESC_TYPE:
491			if (len > sizeof(umouse_report_desc)) {
492				data->blen = len - sizeof(umouse_report_desc);
493				len = sizeof(umouse_report_desc);
494			} else
495				data->blen = 0;
496			memcpy(data->buf, umouse_report_desc, len);
497			data->bdone += len;
498			break;
499		default:
500			DPRINTF(("umouse: IO ERROR"));
501			err = USB_ERR_STALLED;
502			goto done;
503		}
504		eshort = data->blen > 0;
505		break;
506
507	case UREQ(UR_GET_INTERFACE, UT_READ_INTERFACE):
508		DPRINTF(("umouse: (UR_GET_INTERFACE, UT_READ_INTERFACE)"));
509		if (index != 0) {
510			DPRINTF(("umouse get_interface, invalid index %d",
511			        index));
512			err = USB_ERR_STALLED;
513			goto done;
514		}
515
516		if (!data)
517			break;
518
519		if (len > 0) {
520			*udata = 0;
521			data->blen = len - 1;
522		}
523		eshort = data->blen > 0;
524		data->bdone += 1;
525		break;
526
527	case UREQ(UR_GET_STATUS, UT_READ_DEVICE):
528		DPRINTF(("umouse: (UR_GET_STATUS, UT_READ_DEVICE)"));
529		if (data != NULL && len > 1) {
530			if (sc->hid.feature == UF_DEVICE_REMOTE_WAKEUP)
531				USETW(udata, UDS_REMOTE_WAKEUP);
532			else
533				USETW(udata, 0);
534			data->blen = len - 2;
535			data->bdone += 2;
536		}
537
538		eshort = data->blen > 0;
539		break;
540
541	case UREQ(UR_GET_STATUS, UT_READ_INTERFACE):
542	case UREQ(UR_GET_STATUS, UT_READ_ENDPOINT):
543		DPRINTF(("umouse: (UR_GET_STATUS, UT_READ_INTERFACE)"));
544		if (data != NULL && len > 1) {
545			USETW(udata, 0);
546			data->blen = len - 2;
547			data->bdone += 2;
548		}
549		eshort = data->blen > 0;
550		break;
551
552	case UREQ(UR_SET_ADDRESS, UT_WRITE_DEVICE):
553		/* XXX Controller should've handled this */
554		DPRINTF(("umouse set address %u", value));
555		break;
556
557	case UREQ(UR_SET_CONFIG, UT_WRITE_DEVICE):
558		DPRINTF(("umouse set config %u", value));
559		break;
560
561	case UREQ(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
562		DPRINTF(("umouse set descriptor %u", value));
563		break;
564
565
566	case UREQ(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
567		DPRINTF(("umouse: (UR_SET_FEATURE, UT_WRITE_DEVICE) %x", value));
568		if (value == UF_DEVICE_REMOTE_WAKEUP)
569			sc->hid.feature = 0;
570		break;
571
572	case UREQ(UR_SET_FEATURE, UT_WRITE_DEVICE):
573		DPRINTF(("umouse: (UR_SET_FEATURE, UT_WRITE_DEVICE) %x", value));
574		if (value == UF_DEVICE_REMOTE_WAKEUP)
575			sc->hid.feature = UF_DEVICE_REMOTE_WAKEUP;
576		break;
577
578	case UREQ(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
579	case UREQ(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
580	case UREQ(UR_SET_FEATURE, UT_WRITE_INTERFACE):
581	case UREQ(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
582		DPRINTF(("umouse: (UR_CLEAR_FEATURE, UT_WRITE_INTERFACE)"));
583		err = USB_ERR_STALLED;
584		goto done;
585
586	case UREQ(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
587		DPRINTF(("umouse set interface %u", value));
588		break;
589
590	case UREQ(UR_ISOCH_DELAY, UT_WRITE_DEVICE):
591		DPRINTF(("umouse set isoch delay %u", value));
592		break;
593
594	case UREQ(UR_SET_SEL, 0):
595		DPRINTF(("umouse set sel"));
596		break;
597
598	case UREQ(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
599		DPRINTF(("umouse synch frame"));
600		break;
601
602	/* HID device requests */
603
604	case UREQ(UMOUSE_GET_REPORT, UT_READ_CLASS_INTERFACE):
605		DPRINTF(("umouse: (UMOUSE_GET_REPORT, UT_READ_CLASS_INTERFACE) "
606		         "0x%x", (value >> 8)));
607		if (!data)
608			break;
609
610		if ((value >> 8) == 0x01 && len >= sizeof(sc->um_report)) {
611			/* TODO read from backend */
612
613			if (len > sizeof(sc->um_report)) {
614				data->blen = len - sizeof(sc->um_report);
615				len = sizeof(sc->um_report);
616			} else
617				data->blen = 0;
618
619			memcpy(data->buf, &sc->um_report, len);
620			data->bdone += len;
621		} else {
622			err = USB_ERR_STALLED;
623			goto done;
624		}
625		eshort = data->blen > 0;
626		break;
627
628	case UREQ(UMOUSE_GET_IDLE, UT_READ_CLASS_INTERFACE):
629		if (data != NULL && len > 0) {
630			*udata = sc->hid.idle;
631			data->blen = len - 1;
632			data->bdone += 1;
633		}
634		eshort = data->blen > 0;
635		break;
636
637	case UREQ(UMOUSE_GET_PROTOCOL, UT_READ_CLASS_INTERFACE):
638		if (data != NULL && len > 0) {
639			*udata = sc->hid.protocol;
640			data->blen = len - 1;
641			data->bdone += 1;
642		}
643		eshort = data->blen > 0;
644		break;
645
646	case UREQ(UMOUSE_SET_REPORT, UT_WRITE_CLASS_INTERFACE):
647		DPRINTF(("umouse: (UMOUSE_SET_REPORT, UT_WRITE_CLASS_INTERFACE) ignored"));
648		break;
649
650	case UREQ(UMOUSE_SET_IDLE, UT_WRITE_CLASS_INTERFACE):
651		sc->hid.idle = UGETW(xfer->ureq->wValue) >> 8;
652		DPRINTF(("umouse: (UMOUSE_SET_IDLE, UT_WRITE_CLASS_INTERFACE) %x",
653		        sc->hid.idle));
654		break;
655
656	case UREQ(UMOUSE_SET_PROTOCOL, UT_WRITE_CLASS_INTERFACE):
657		sc->hid.protocol = UGETW(xfer->ureq->wValue) >> 8;
658		DPRINTF(("umouse: (UR_CLEAR_FEATURE, UT_WRITE_CLASS_INTERFACE) %x",
659		        sc->hid.protocol));
660		break;
661
662	default:
663		DPRINTF(("**** umouse request unhandled"));
664		err = USB_ERR_STALLED;
665		break;
666	}
667
668done:
669	if (xfer->ureq && (xfer->ureq->bmRequestType & UT_WRITE) &&
670	    (err == USB_ERR_NORMAL_COMPLETION) && (data != NULL))
671		data->blen = 0;
672	else if (eshort)
673		err = USB_ERR_SHORT_XFER;
674
675	DPRINTF(("umouse request error code %d (0=ok), blen %u txlen %u",
676	        err, (data ? data->blen : 0), (data ? data->bdone : 0)));
677
678	return (err);
679}
680
681static int
682umouse_data_handler(void *scarg, struct usb_data_xfer *xfer, int dir,
683     int epctx)
684{
685	struct umouse_softc *sc;
686	struct usb_data_xfer_block *data;
687	uint8_t *udata;
688	int len, i, idx;
689	int err;
690
691	DPRINTF(("umouse handle data - DIR=%s|EP=%d, blen %d",
692	        dir ? "IN" : "OUT", epctx, xfer->data[0].blen));
693
694
695	/* find buffer to add data */
696	udata = NULL;
697	err = USB_ERR_NORMAL_COMPLETION;
698
699	/* handle xfer at first unprocessed item with buffer */
700	data = NULL;
701	idx = xfer->head;
702	for (i = 0; i < xfer->ndata; i++) {
703		data = &xfer->data[idx];
704		if (data->buf != NULL && data->blen != 0) {
705			break;
706		} else {
707			data->processed = 1;
708			data = NULL;
709		}
710		idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
711	}
712	if (!data)
713		goto done;
714
715	udata = data->buf;
716	len = data->blen;
717
718	if (udata == NULL) {
719		DPRINTF(("umouse no buffer provided for input"));
720		err = USB_ERR_NOMEM;
721		goto done;
722	}
723
724	sc = scarg;
725
726	if (dir) {
727
728		pthread_mutex_lock(&sc->mtx);
729
730		if (!sc->newdata) {
731			err = USB_ERR_CANCELLED;
732			USB_DATA_SET_ERRCODE(&xfer->data[xfer->head], USB_NAK);
733			pthread_mutex_unlock(&sc->mtx);
734			goto done;
735		}
736
737		if (sc->polling) {
738			err = USB_ERR_STALLED;
739			USB_DATA_SET_ERRCODE(data, USB_STALL);
740			pthread_mutex_unlock(&sc->mtx);
741			goto done;
742		}
743		sc->polling = 1;
744
745		if (len > 0) {
746			sc->newdata = 0;
747
748			data->processed = 1;
749			data->bdone += 6;
750			memcpy(udata, &sc->um_report, 6);
751			data->blen = len - 6;
752			if (data->blen > 0)
753				err = USB_ERR_SHORT_XFER;
754		}
755
756		sc->polling = 0;
757		pthread_mutex_unlock(&sc->mtx);
758	} else {
759		USB_DATA_SET_ERRCODE(data, USB_STALL);
760		err = USB_ERR_STALLED;
761	}
762
763done:
764	return (err);
765}
766
767static int
768umouse_reset(void *scarg)
769{
770	struct umouse_softc *sc;
771
772	sc = scarg;
773
774	sc->newdata = 0;
775
776	return (0);
777}
778
779static int
780umouse_remove(void *scarg)
781{
782
783	return (0);
784}
785
786static int
787umouse_stop(void *scarg)
788{
789
790	return (0);
791}
792
793#ifdef BHYVE_SNAPSHOT
794static int
795umouse_snapshot(void *scarg, struct vm_snapshot_meta *meta)
796{
797	int ret;
798	struct umouse_softc *sc;
799
800	sc = scarg;
801
802	SNAPSHOT_VAR_OR_LEAVE(sc->um_report, meta, ret, done);
803	SNAPSHOT_VAR_OR_LEAVE(sc->newdata, meta, ret, done);
804	SNAPSHOT_VAR_OR_LEAVE(sc->hid.idle, meta, ret, done);
805	SNAPSHOT_VAR_OR_LEAVE(sc->hid.protocol, meta, ret, done);
806	SNAPSHOT_VAR_OR_LEAVE(sc->hid.feature, meta, ret, done);
807
808	SNAPSHOT_VAR_OR_LEAVE(sc->polling, meta, ret, done);
809	SNAPSHOT_VAR_OR_LEAVE(sc->prev_evt.tv_sec, meta, ret, done);
810	SNAPSHOT_VAR_OR_LEAVE(sc->prev_evt.tv_usec, meta, ret, done);
811
812done:
813	return (ret);
814}
815#endif
816
817struct usb_devemu ue_mouse = {
818	.ue_emu =	"tablet",
819	.ue_usbver =	3,
820	.ue_usbspeed =	USB_SPEED_HIGH,
821	.ue_init =	umouse_init,
822	.ue_request =	umouse_request,
823	.ue_data =	umouse_data_handler,
824	.ue_reset =	umouse_reset,
825	.ue_remove =	umouse_remove,
826	.ue_stop =	umouse_stop,
827#ifdef BHYVE_SNAPSHOT
828	.ue_snapshot =	umouse_snapshot,
829#endif
830};
831USB_EMUL_SET(ue_mouse);
832