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