xref: /netbsd-src/sys/rump/dev/lib/libugenhc/ugenhc.c (revision 53b02e147d4ed531c0d2a5ca9b3e8026ba3e99b5)
1 /*	$NetBSD: ugenhc.c,v 1.29 2021/08/07 16:19:18 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 2009, 2010 Antti Kantee.  All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 /*
29  * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
30  * All rights reserved.
31  *
32  * This code is derived from software contributed to The NetBSD Foundation
33  * by Lennart Augustsson (lennart@augustsson.net) at
34  * Carlstedt Research & Technology.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
46  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
47  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
49  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
50  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
52  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
53  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
55  * POSSIBILITY OF SUCH DAMAGE.
56  */
57 
58 /*
59  * This rump driver attaches ugen as a kernel usb host controller.
60  * It's still somewhat under the hammer ....
61  */
62 
63 #include <sys/cdefs.h>
64 __KERNEL_RCSID(0, "$NetBSD: ugenhc.c,v 1.29 2021/08/07 16:19:18 thorpej Exp $");
65 
66 #include <sys/param.h>
67 #include <sys/bus.h>
68 #include <sys/conf.h>
69 #include <sys/device.h>
70 #include <sys/fcntl.h>
71 #include <sys/kmem.h>
72 #include <sys/kernel.h>
73 #include <sys/kthread.h>
74 #include <sys/mutex.h>
75 
76 #include <dev/usb/usb.h>
77 #include <dev/usb/usbdi.h>
78 #include <dev/usb/usbhid.h>
79 #include <dev/usb/usbdivar.h>
80 #include <dev/usb/usb_mem.h>
81 #include <dev/usb/usbroothub.h>
82 
83 #include <rump/rumpuser.h>
84 
85 #include <rump-sys/kern.h>
86 #include <rump-sys/dev.h>
87 
88 #include "ugenhc_user.h"
89 
90 #define UGEN_NEPTS 16
91 #define UGEN_EPT_CTRL 0 /* ugenx.00 is the control endpoint */
92 
93 struct ugenhc_softc {
94 	struct usbd_bus sc_bus;
95 	int sc_devnum;
96 
97 	int sc_ugenfd[UGEN_NEPTS];
98 	int sc_fdmodes[UGEN_NEPTS];
99 
100 	int sc_port_status;
101 	int sc_port_change;
102 
103 	struct lwp *sc_rhintr;
104 	struct usbd_xfer *sc_intrxfer;
105 
106 	kmutex_t sc_lock;
107 };
108 
109 static int	ugenhc_probe(device_t, cfdata_t, void *);
110 static void	ugenhc_attach(device_t, device_t, void *);
111 
112 CFATTACH_DECL_NEW(ugenhc, sizeof(struct ugenhc_softc),
113 	ugenhc_probe, ugenhc_attach, NULL, NULL);
114 
115 struct rusb_xfer {
116 	struct usbd_xfer rusb_xfer;
117 	int rusb_status; /* now this is a cheap trick */
118 };
119 #define RUSB(x) ((struct rusb_xfer *)x)
120 
121 #define UGENHC_BUS2SC(bus)	((bus)->ub_hcpriv)
122 #define UGENHC_PIPE2SC(pipe)	UGENHC_BUS2SC((pipe)->up_dev->ud_bus)
123 #define UGENHC_XFER2SC(pipe)	UGENHC_BUS2SC((xfer)->ux_bus)
124 
125 #define UGENDEV_BASESTR "/dev/ugen"
126 #define UGENDEV_BUFSIZE 32
127 static void
128 makeugendevstr(int devnum, int endpoint, char *buf, size_t len)
129 {
130 
131 	CTASSERT(UGENDEV_BUFSIZE > sizeof(UGENDEV_BASESTR)+sizeof("0.00")+1);
132 	snprintf(buf, len, "%s%d.%02d", UGENDEV_BASESTR, devnum, endpoint);
133 }
134 
135 static int
136 ugenhc_roothub_ctrl(struct usbd_bus *bus, usb_device_request_t *req,
137     void *buf, int buflen)
138 {
139 	struct ugenhc_softc *sc = UGENHC_BUS2SC(bus);
140 	int totlen = 0;
141 	uint16_t len, value;
142 
143 	len = UGETW(req->wLength);
144 	value = UGETW(req->wValue);
145 
146 #define C(x,y) ((x) | ((y) << 8))
147 	switch (C(req->bRequest, req->bmRequestType)) {
148 	case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
149 		switch (value) {
150 		case C(0, UDESC_DEVICE): {
151 			usb_device_descriptor_t devd;
152 
153 			totlen = uimin(buflen, sizeof(devd));
154 			memcpy(&devd, buf, totlen);
155 			USETW(devd.idVendor, 0x7275);
156 			USETW(devd.idProduct, 0x6d72);
157 			memcpy(buf, &devd, totlen);
158 			break;
159 		}
160 #define sd ((usb_string_descriptor_t *)buf)
161 		case C(1, UDESC_STRING):
162 			/* Vendor */
163 			totlen = usb_makestrdesc(sd, len, "rod nevada");
164 			break;
165 		case C(2, UDESC_STRING):
166 			/* Product */
167 			totlen = usb_makestrdesc(sd, len, "RUMPUSBHC root hub");
168 			break;
169 #undef sd
170 		default:
171 			/* default from usbroothub */
172 			return buflen;
173 		}
174 		break;
175 
176 	case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
177 		switch (value) {
178 		case UHF_PORT_RESET:
179 			sc->sc_port_change |= UPS_C_PORT_RESET;
180 			break;
181 		case UHF_PORT_POWER:
182 			break;
183 		default:
184 			return -1;
185 		}
186 		break;
187 
188 	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
189 		sc->sc_port_change &= ~value;
190 		break;
191 
192 	case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
193 		totlen = buflen;
194 		break;
195 
196 	case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
197 		/* huh?  other hc's do this */
198 		memset(buf, 0, len);
199 		totlen = len;
200 		break;
201 
202 	case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): {
203 		usb_port_status_t ps;
204 
205 		USETW(ps.wPortStatus, sc->sc_port_status);
206 		USETW(ps.wPortChange, sc->sc_port_change);
207 		totlen = uimin(len, sizeof(ps));
208 		memcpy(buf, &ps, totlen);
209 		break;
210 	}
211 	default:
212 		/* default from usbroothub */
213 		return buflen;
214 	}
215 
216 	return totlen;
217 }
218 
219 static usbd_status
220 rumpusb_device_ctrl_start(struct usbd_xfer *xfer)
221 {
222 	usb_device_request_t *req = &xfer->ux_request;
223 	struct ugenhc_softc *sc = UGENHC_XFER2SC(xfer);
224 	uint8_t *buf = NULL;
225 	int len, totlen;
226 	int value;
227 	int err = 0;
228 	int ru_error, mightfail = 0;
229 
230 	len = totlen = UGETW(req->wLength);
231 	if (len)
232 		buf = xfer->ux_buf;
233 	value = UGETW(req->wValue);
234 
235 #define C(x,y) ((x) | ((y) << 8))
236 	switch(C(req->bRequest, req->bmRequestType)) {
237 	case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
238 		switch (value>>8) {
239 		case UDESC_DEVICE:
240 			{
241 			usb_device_descriptor_t uddesc;
242 			totlen = uimin(len, USB_DEVICE_DESCRIPTOR_SIZE);
243 			memset(buf, 0, totlen);
244 			if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
245 			    USB_GET_DEVICE_DESC, &uddesc, &ru_error) == -1) {
246 				err = EIO;
247 				goto ret;
248 			}
249 			memcpy(buf, &uddesc, totlen);
250 			}
251 
252 			break;
253 		case UDESC_CONFIG:
254 			{
255 			struct usb_full_desc ufdesc;
256 			ufdesc.ufd_config_index = value & 0xff;
257 			ufdesc.ufd_size = len;
258 			ufdesc.ufd_data = buf;
259 			memset(buf, 0, len);
260 			if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
261 			    USB_GET_FULL_DESC, &ufdesc, &ru_error) == -1) {
262 				err = USBD_IOERROR;
263 				goto ret;
264 			}
265 			totlen = ufdesc.ufd_size;
266 			}
267 			break;
268 
269 		case UDESC_STRING:
270 			{
271 			struct usb_device_info udi;
272 
273 			if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
274 			    USB_GET_DEVICEINFO, &udi, &ru_error) == -1) {
275 				printf("ugenhc: get dev info failed: %d\n",
276 				    ru_error);
277 				err = USBD_IOERROR;
278 				goto ret;
279 			}
280 
281 			switch (value & 0xff) {
282 #define sd ((usb_string_descriptor_t *)buf)
283 			case 0: /* language table */
284 				break;
285 			case 1: /* vendor */
286 				totlen = usb_makestrdesc(sd, len,
287 				    udi.udi_vendor);
288 				break;
289 			case 2: /* product */
290 				totlen = usb_makestrdesc(sd, len,
291 				    udi.udi_product);
292 				break;
293 			}
294 #undef sd
295 			}
296 			break;
297 
298 		default:
299 			panic("not handled");
300 		}
301 		break;
302 
303 	case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
304 		/* ignored, ugen won't let us */
305 		break;
306 
307 	case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
308 		if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
309 		    USB_SET_CONFIG, &value, &ru_error) == -1) {
310 			printf("ugenhc: set config failed: %d\n",
311 			    ru_error);
312 			err = USBD_IOERROR;
313 			goto ret;
314 		}
315 		break;
316 
317 	case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
318 		{
319 		struct usb_alt_interface uai;
320 
321 		totlen = 0;
322 		uai.uai_interface_index = UGETW(req->wIndex);
323 		uai.uai_alt_no = value;
324 		if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
325 		    USB_SET_ALTINTERFACE, &uai, &ru_error) == -1) {
326 			printf("ugenhc: set alt interface failed: %d\n",
327 			    ru_error);
328 			err = USBD_IOERROR;
329 			goto ret;
330 		}
331 		break;
332 		}
333 
334 	/*
335 	 * This request might fail unknown reasons.  "EIO" doesn't
336 	 * give much help, and debugging the host ugen would be
337 	 * necessary.  However, since it doesn't seem to really
338 	 * affect anything, just let it fail for now.
339 	 */
340 	case C(0x00, UT_WRITE_CLASS_INTERFACE):
341 		mightfail = 1;
342 		/*FALLTHROUGH*/
343 
344 	/*
345 	 * XXX: don't wildcard these yet.  I want to better figure
346 	 * out what to trap here.  This is kinda silly, though ...
347 	 */
348 
349 	case C(0x01, UT_WRITE_VENDOR_DEVICE):
350 	case C(0x06, UT_WRITE_VENDOR_DEVICE):
351 	case C(0x07, UT_READ_VENDOR_DEVICE):
352 	case C(0x09, UT_READ_VENDOR_DEVICE):
353 	case C(0xfe, UT_READ_CLASS_INTERFACE):
354 	case C(0x01, UT_READ_CLASS_INTERFACE):
355 	case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
356 	case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
357 	case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
358 	case C(UR_GET_DESCRIPTOR, UT_READ_INTERFACE):
359 	case C(0xff, UT_WRITE_CLASS_INTERFACE):
360 	case C(0x20, UT_WRITE_CLASS_INTERFACE):
361 	case C(0x22, UT_WRITE_CLASS_INTERFACE):
362 	case C(0x0a, UT_WRITE_CLASS_INTERFACE):
363 	case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
364 	case C(0x00, UT_WRITE_CLASS_DEVICE):
365 	case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
366 	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
367 	case C(UR_SET_REPORT, UT_WRITE_CLASS_INTERFACE):
368 	case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
369 		{
370 		struct usb_ctl_request ucr;
371 
372 		memcpy(&ucr.ucr_request, req, sizeof(ucr.ucr_request));
373 		ucr.ucr_data = buf;
374 		if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
375 		    USB_DO_REQUEST, &ucr, &ru_error) == -1) {
376 			if (!mightfail) {
377 				panic("request failed: %d", ru_error);
378 			} else {
379 				err = ru_error;
380 			}
381 		}
382 		}
383 		break;
384 
385 	default:
386 		panic("unhandled request");
387 		break;
388 	}
389 	xfer->ux_actlen = totlen;
390 	err = USBD_NORMAL_COMPLETION;
391 
392  ret:
393 	xfer->ux_status = err;
394 	mutex_enter(&sc->sc_lock);
395 	usb_transfer_complete(xfer);
396 	mutex_exit(&sc->sc_lock);
397 
398 	return USBD_IN_PROGRESS;
399 }
400 
401 static usbd_status
402 rumpusb_device_ctrl_transfer(struct usbd_xfer *xfer)
403 {
404 	struct ugenhc_softc *sc = UGENHC_XFER2SC(xfer);
405 	usbd_status err;
406 
407 	mutex_enter(&sc->sc_lock);
408 	err = usb_insert_transfer(xfer);
409 	mutex_exit(&sc->sc_lock);
410 	if (err)
411 		return err;
412 
413 	return rumpusb_device_ctrl_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
414 }
415 
416 static void
417 rumpusb_device_ctrl_abort(struct usbd_xfer *xfer)
418 {
419 
420 }
421 
422 static void
423 rumpusb_device_ctrl_close(struct usbd_pipe *pipe)
424 {
425 
426 }
427 
428 static void
429 rumpusb_device_ctrl_cleartoggle(struct usbd_pipe *pipe)
430 {
431 
432 }
433 
434 static void
435 rumpusb_device_ctrl_done(struct usbd_xfer *xfer)
436 {
437 
438 }
439 
440 static const struct usbd_pipe_methods rumpusb_device_ctrl_methods = {
441 	.upm_transfer =	rumpusb_device_ctrl_transfer,
442 	.upm_start =	rumpusb_device_ctrl_start,
443 	.upm_abort =	rumpusb_device_ctrl_abort,
444 	.upm_close =	rumpusb_device_ctrl_close,
445 	.upm_cleartoggle =	rumpusb_device_ctrl_cleartoggle,
446 	.upm_done =	rumpusb_device_ctrl_done,
447 };
448 
449 static void
450 rhscintr(void *arg)
451 {
452 	char buf[UGENDEV_BUFSIZE];
453 	struct ugenhc_softc *sc = arg;
454 	struct usbd_xfer *xfer;
455 	int fd, error;
456 
457 	makeugendevstr(sc->sc_devnum, 0, buf, sizeof(buf));
458 
459 	for (;;) {
460 		/*
461 		 * Detect device attach.
462 		 */
463 
464 		for (;;) {
465 			error = rumpuser_open(buf, RUMPUSER_OPEN_RDWR, &fd);
466 			if (error == 0)
467 				break;
468 			kpause("ugwait", false, hz/4, NULL);
469 		}
470 
471 		sc->sc_ugenfd[UGEN_EPT_CTRL] = fd;
472 		sc->sc_port_status = UPS_CURRENT_CONNECT_STATUS
473 		    | UPS_PORT_ENABLED | UPS_PORT_POWER;
474 		sc->sc_port_change = UPS_C_CONNECT_STATUS | UPS_C_PORT_RESET;
475 
476 		xfer = sc->sc_intrxfer;
477 		memset(xfer->ux_buffer, 0xff, xfer->ux_length);
478 		xfer->ux_actlen = xfer->ux_length;
479 		xfer->ux_status = USBD_NORMAL_COMPLETION;
480 
481 		mutex_enter(&sc->sc_lock);
482 		usb_transfer_complete(xfer);
483 		mutex_exit(&sc->sc_lock);
484 
485 		kpause("ugwait2", false, hz, NULL);
486 
487 		/*
488 		 * Detect device detach.
489 		 */
490 
491 		for (;;) {
492 			error = rumpuser_open(buf, RUMPUSER_OPEN_RDWR, &fd);
493 			if (fd == -1)
494 				break;
495 
496 			error = rumpuser_close(fd);
497 			kpause("ugwait2", false, hz/4, NULL);
498 		}
499 
500 		sc->sc_port_status = ~(UPS_CURRENT_CONNECT_STATUS
501 		    | UPS_PORT_ENABLED | UPS_PORT_POWER);
502 		sc->sc_port_change = UPS_C_CONNECT_STATUS | UPS_C_PORT_RESET;
503 
504 		error = rumpuser_close(sc->sc_ugenfd[UGEN_EPT_CTRL]);
505 		sc->sc_ugenfd[UGEN_EPT_CTRL] = -1;
506 
507 		xfer = sc->sc_intrxfer;
508 		memset(xfer->ux_buffer, 0xff, xfer->ux_length);
509 		xfer->ux_actlen = xfer->ux_length;
510 		xfer->ux_status = USBD_NORMAL_COMPLETION;
511 		mutex_enter(&sc->sc_lock);
512 		usb_transfer_complete(xfer);
513 		mutex_exit(&sc->sc_lock);
514 
515 		kpause("ugwait3", false, hz, NULL);
516 	}
517 
518 	kthread_exit(0);
519 }
520 
521 static usbd_status
522 rumpusb_root_intr_start(struct usbd_xfer *xfer)
523 {
524 	struct ugenhc_softc *sc = UGENHC_XFER2SC(xfer);
525 	int error;
526 
527 	mutex_enter(&sc->sc_lock);
528 	sc->sc_intrxfer = xfer;
529 	if (!sc->sc_rhintr) {
530 		error = kthread_create(PRI_NONE, 0, NULL,
531 		    rhscintr, sc, &sc->sc_rhintr, "ugenrhi");
532 		if (error)
533 			xfer->ux_status = USBD_IOERROR;
534 	}
535 	mutex_exit(&sc->sc_lock);
536 
537 	return USBD_IN_PROGRESS;
538 }
539 
540 static usbd_status
541 rumpusb_root_intr_transfer(struct usbd_xfer *xfer)
542 {
543 	struct ugenhc_softc *sc = UGENHC_XFER2SC(xfer);
544 	usbd_status err;
545 
546 	mutex_enter(&sc->sc_lock);
547 	err = usb_insert_transfer(xfer);
548 	mutex_exit(&sc->sc_lock);
549 	if (err)
550 		return err;
551 
552 	return rumpusb_root_intr_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
553 }
554 
555 static void
556 rumpusb_root_intr_abort(struct usbd_xfer *xfer)
557 {
558 
559 }
560 
561 static void
562 rumpusb_root_intr_close(struct usbd_pipe *pipe)
563 {
564 
565 }
566 
567 static void
568 rumpusb_root_intr_cleartoggle(struct usbd_pipe *pipe)
569 {
570 
571 }
572 
573 static void
574 rumpusb_root_intr_done(struct usbd_xfer *xfer)
575 {
576 
577 }
578 
579 static const struct usbd_pipe_methods rumpusb_root_intr_methods = {
580 	.upm_transfer =	rumpusb_root_intr_transfer,
581 	.upm_start =	rumpusb_root_intr_start,
582 	.upm_abort =	rumpusb_root_intr_abort,
583 	.upm_close =	rumpusb_root_intr_close,
584 	.upm_cleartoggle =	rumpusb_root_intr_cleartoggle,
585 	.upm_done =	rumpusb_root_intr_done,
586 };
587 
588 static usbd_status
589 rumpusb_device_bulk_start(struct usbd_xfer *xfer)
590 {
591 	struct ugenhc_softc *sc = UGENHC_XFER2SC(xfer);
592 	usb_endpoint_descriptor_t *ed = xfer->ux_pipe->up_endpoint->ue_edesc;
593 	size_t n, done;
594 	bool isread;
595 	int len, error, endpt;
596 	uint8_t *buf;
597 	int xfererr = USBD_NORMAL_COMPLETION;
598 	int shortval, i;
599 
600 	ed = xfer->ux_pipe->up_endpoint->ue_edesc;
601 	endpt = ed->bEndpointAddress;
602 	isread = UE_GET_DIR(endpt) == UE_DIR_IN;
603 	endpt = UE_GET_ADDR(endpt);
604 	KASSERT(endpt < UGEN_NEPTS);
605 
606 	buf = xfer->ux_buf;
607 	done = 0;
608 	if ((ed->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS) {
609 		for (i = 0, len = 0; i < xfer->ux_nframes; i++)
610 			len += xfer->ux_frlengths[i];
611 	} else {
612 		KASSERT(xfer->ux_length);
613 		len = xfer->ux_length;
614 	}
615 	shortval = (xfer->ux_flags & USBD_SHORT_XFER_OK) != 0;
616 
617 	while (RUSB(xfer)->rusb_status == 0) {
618 		if (isread) {
619 			struct rumpuser_iovec iov;
620 
621 			rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[endpt],
622 			    USB_SET_SHORT_XFER, &shortval, &error);
623 			iov.iov_base = buf+done;
624 			iov.iov_len = len-done;
625 			error = rumpuser_iovread(sc->sc_ugenfd[endpt], &iov, 1,
626 			    RUMPUSER_IOV_NOSEEK, &n);
627 			if (error) {
628 				n = 0;
629 				if (done == 0) {
630 					if (error == ETIMEDOUT)
631 						continue;
632 					xfererr = USBD_IOERROR;
633 					goto out;
634 				}
635 			}
636 			done += n;
637 			if (done == len)
638 				break;
639 		} else {
640 			struct rumpuser_iovec iov;
641 
642 			iov.iov_base = buf;
643 			iov.iov_len = len;
644 			error = rumpuser_iovwrite(sc->sc_ugenfd[endpt], &iov, 1,
645 			    RUMPUSER_IOV_NOSEEK, &n);
646 			done = n;
647 			if (done == len)
648 				break;
649 			else if (!error)
650 				panic("short write");
651 
652 			xfererr = USBD_IOERROR;
653 			goto out;
654 		}
655 
656 		if (shortval) {
657 			/*
658 			 * Holy XXX, bitman.  I get >16byte interrupt
659 			 * transfers from ugen in 16 byte chunks.
660 			 * Don't know how to better fix this for now.
661 			 * Of course this hack will fail e.g. if someone
662 			 * sports other magic values or if the transfer
663 			 * happens to be an integral multiple of 16
664 			 * in size ....
665 			 */
666 			if ((ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT
667 			    && n == 16) {
668 				continue;
669 			} else {
670 				break;
671 			}
672 		}
673 	}
674 
675 	if (RUSB(xfer)->rusb_status == 0) {
676 		xfer->ux_actlen = done;
677 	} else {
678 		xfererr = USBD_CANCELLED;
679 		RUSB(xfer)->rusb_status = 2;
680 	}
681  out:
682 	if ((ed->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS)
683 		if (done != len)
684 			panic("lazy bum");
685 	xfer->ux_status = xfererr;
686 	mutex_enter(&sc->sc_lock);
687 	usb_transfer_complete(xfer);
688 	mutex_exit(&sc->sc_lock);
689 	return USBD_IN_PROGRESS;
690 }
691 
692 static void
693 doxfer_kth(void *arg)
694 {
695 	struct usbd_pipe *pipe = arg;
696 	struct ugenhc_softc *sc = UGENHC_PIPE2SC(pipe);
697 
698 	mutex_enter(&sc->sc_lock);
699 	do {
700 		struct usbd_xfer *xfer = SIMPLEQ_FIRST(&pipe->up_queue);
701 		mutex_exit(&sc->sc_lock);
702 		rumpusb_device_bulk_start(xfer);
703 		mutex_enter(&sc->sc_lock);
704 	} while (!SIMPLEQ_EMPTY(&pipe->up_queue));
705 	mutex_exit(&sc->sc_lock);
706 	kthread_exit(0);
707 }
708 
709 static usbd_status
710 rumpusb_device_bulk_transfer(struct usbd_xfer *xfer)
711 {
712 	struct ugenhc_softc *sc = UGENHC_XFER2SC(xfer);
713 	usbd_status err;
714 
715 	if (!rump_threads) {
716 		/* XXX: lie about supporting async transfers */
717 		if ((xfer->ux_flags & USBD_SYNCHRONOUS) == 0) {
718 			printf("non-threaded rump does not support "
719 			    "async transfers.\n");
720 			return USBD_IN_PROGRESS;
721 		}
722 
723 		mutex_enter(&sc->sc_lock);
724 		err = usb_insert_transfer(xfer);
725 		mutex_exit(&sc->sc_lock);
726 		if (err)
727 			return err;
728 
729 		return rumpusb_device_bulk_start(
730 		    SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
731 	} else {
732 		mutex_enter(&sc->sc_lock);
733 		err = usb_insert_transfer(xfer);
734 		mutex_exit(&sc->sc_lock);
735 		if (err)
736 			return err;
737 		kthread_create(PRI_NONE, 0, NULL, doxfer_kth, xfer->ux_pipe, NULL,
738 		    "rusbhcxf");
739 
740 		return USBD_IN_PROGRESS;
741 	}
742 }
743 
744 /* wait for transfer to abort.  yea, this is cheesy (from a spray can) */
745 static void
746 rumpusb_device_bulk_abort(struct usbd_xfer *xfer)
747 {
748 	struct rusb_xfer *rx = RUSB(xfer);
749 
750 	rx->rusb_status = 1;
751 	while (rx->rusb_status < 2) {
752 		kpause("jopo", false, hz/10, NULL);
753 	}
754 }
755 
756 static void
757 rumpusb_device_bulk_close(struct usbd_pipe *pipe)
758 {
759 	struct ugenhc_softc *sc = UGENHC_PIPE2SC(pipe);
760 	int endpt = pipe->up_endpoint->ue_edesc->bEndpointAddress;
761 	struct usbd_xfer *xfer;
762 
763 	KASSERT(mutex_owned(&sc->sc_lock));
764 
765 	endpt = UE_GET_ADDR(endpt);
766 
767 	while ((xfer = SIMPLEQ_FIRST(&pipe->up_queue)) != NULL)
768 		rumpusb_device_bulk_abort(xfer);
769 
770 	rumpuser_close(sc->sc_ugenfd[endpt]);
771 	sc->sc_ugenfd[endpt] = -1;
772 	sc->sc_fdmodes[endpt] = -1;
773 }
774 
775 static void
776 rumpusb_device_bulk_cleartoggle(struct usbd_pipe *pipe)
777 {
778 
779 }
780 
781 static void
782 rumpusb_device_bulk_done(struct usbd_xfer *xfer)
783 {
784 
785 }
786 
787 static const struct usbd_pipe_methods rumpusb_device_bulk_methods = {
788 	.upm_transfer =	rumpusb_device_bulk_transfer,
789 	.upm_start =	rumpusb_device_bulk_start,
790 	.upm_abort =	rumpusb_device_bulk_abort,
791 	.upm_close =	rumpusb_device_bulk_close,
792 	.upm_cleartoggle =	rumpusb_device_bulk_cleartoggle,
793 	.upm_done =	rumpusb_device_bulk_done,
794 };
795 
796 static usbd_status
797 ugenhc_open(struct usbd_pipe *pipe)
798 {
799 	struct usbd_device *dev = pipe->up_dev;
800 	struct ugenhc_softc *sc = UGENHC_PIPE2SC(pipe);
801 	usb_endpoint_descriptor_t *ed = pipe->up_endpoint->ue_edesc;
802 	uint8_t rhaddr = dev->ud_bus->ub_rhaddr;
803 	uint8_t addr = dev->ud_addr;
804 	uint8_t xfertype = ed->bmAttributes & UE_XFERTYPE;
805 	char buf[UGENDEV_BUFSIZE];
806 	int endpt, oflags, error;
807 	int fd, val;
808 
809 	if (addr == rhaddr) {
810 		switch (xfertype) {
811 		case UE_CONTROL:
812 			pipe->up_methods = &roothub_ctrl_methods;
813 			break;
814 		case UE_INTERRUPT:
815 			pipe->up_methods = &rumpusb_root_intr_methods;
816 			break;
817 		default:
818 			panic("%d not supported", xfertype);
819 			break;
820 		}
821 	} else {
822 		switch (xfertype) {
823 		case UE_CONTROL:
824 			pipe->up_methods = &rumpusb_device_ctrl_methods;
825 			break;
826 		case UE_INTERRUPT:
827 		case UE_BULK:
828 		case UE_ISOCHRONOUS:
829 			pipe->up_methods = &rumpusb_device_bulk_methods;
830 			endpt = pipe->up_endpoint->ue_edesc->bEndpointAddress;
831 			if (UE_GET_DIR(endpt) == UE_DIR_IN) {
832 				oflags = O_RDONLY;
833 			} else {
834 				oflags = O_WRONLY;
835 			}
836 			endpt = UE_GET_ADDR(endpt);
837 
838 			if (oflags != O_RDONLY && xfertype == UE_ISOCHRONOUS) {
839 				printf("WARNING: faking isoc write open\n");
840 				oflags = O_RDONLY;
841 			}
842 
843 			if (sc->sc_fdmodes[endpt] == oflags
844 			    || sc->sc_fdmodes[endpt] == O_RDWR)
845 				break;
846 
847 			if (sc->sc_fdmodes[endpt] != -1) {
848 				/* XXX: closing from under someone? */
849 				error = rumpuser_close(sc->sc_ugenfd[endpt]);
850 				oflags = O_RDWR;
851 			}
852 
853 			makeugendevstr(sc->sc_devnum, endpt, buf, sizeof(buf));
854 			/* XXX: theoretically should convert oflags */
855 			error = rumpuser_open(buf, oflags, &fd);
856 			if (error != 0) {
857 				return USBD_INVAL; /* XXX: no mapping */
858 			}
859 			val = 100;
860 			if (rumpcomp_ugenhc_ioctl(fd, USB_SET_TIMEOUT, &val,
861 			    &error) == -1)
862 				panic("timeout set failed");
863 			sc->sc_ugenfd[endpt] = fd;
864 			sc->sc_fdmodes[endpt] = oflags;
865 
866 			break;
867 		default:
868 			panic("%d not supported", xfertype);
869 			break;
870 
871 		}
872 	}
873 	return 0;
874 }
875 
876 static void
877 ugenhc_softint(void *arg)
878 {
879 
880 }
881 
882 static void
883 ugenhc_poll(struct usbd_bus *ubus)
884 {
885 
886 }
887 
888 static struct usbd_xfer *
889 ugenhc_allocx(struct usbd_bus *bus, unsigned int nframes)
890 {
891 	struct usbd_xfer *xfer;
892 
893 	xfer = kmem_zalloc(sizeof(struct usbd_xfer), KM_SLEEP);
894 	xfer->ux_state = XFER_BUSY;
895 
896 	return xfer;
897 }
898 
899 static void
900 ugenhc_freex(struct usbd_bus *bus, struct usbd_xfer *xfer)
901 {
902 
903 	kmem_free(xfer, sizeof(struct usbd_xfer));
904 }
905 
906 
907 static void
908 ugenhc_getlock(struct usbd_bus *bus, kmutex_t **lock)
909 {
910 	struct ugenhc_softc *sc = UGENHC_BUS2SC(bus);
911 
912 	*lock = &sc->sc_lock;
913 }
914 
915 struct ugenhc_pipe {
916 	struct usbd_pipe pipe;
917 };
918 
919 static const struct usbd_bus_methods ugenhc_bus_methods = {
920 	.ubm_open =	ugenhc_open,
921 	.ubm_softint =	ugenhc_softint,
922 	.ubm_dopoll =	ugenhc_poll,
923 	.ubm_allocx = 	ugenhc_allocx,
924 	.ubm_freex =	ugenhc_freex,
925 	.ubm_getlock =	ugenhc_getlock,
926 	.ubm_rhctrl =	ugenhc_roothub_ctrl,
927 };
928 
929 static int
930 ugenhc_probe(device_t parent, cfdata_t match, void *aux)
931 {
932 	char buf[UGENDEV_BUFSIZE];
933 
934 	makeugendevstr(match->cf_unit, 0, buf, sizeof(buf));
935 	if (rumpuser_getfileinfo(buf, NULL, NULL) != 0)
936 		return 0;
937 
938 	return 1;
939 }
940 
941 static void
942 ugenhc_attach(device_t parent, device_t self, void *aux)
943 {
944 	struct mainbus_attach_args *maa = aux;
945 	struct ugenhc_softc *sc = device_private(self);
946 
947 	aprint_normal("\n");
948 
949 	memset(sc, 0, sizeof(*sc));
950 	memset(&sc->sc_ugenfd, -1, sizeof(sc->sc_ugenfd));
951 	memset(&sc->sc_fdmodes, -1, sizeof(sc->sc_fdmodes));
952 
953 	sc->sc_bus.ub_revision = USBREV_2_0;
954 	sc->sc_bus.ub_methods = &ugenhc_bus_methods;
955 	sc->sc_bus.ub_hcpriv = sc;
956 	sc->sc_bus.ub_pipesize = sizeof(struct ugenhc_pipe);
957 	sc->sc_bus.ub_usedma = false;
958 	sc->sc_devnum = maa->maa_unit;
959 
960 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
961 
962 	config_found(self, &sc->sc_bus, usbctlprint, CFARGS_NONE);
963 }
964