xref: /netbsd-src/sys/rump/dev/lib/libugenhc/ugenhc.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: ugenhc.c,v 1.22 2014/08/02 12:38:01 skrll 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.22 2014/08/02 12:38:01 skrll 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_subr.h>
82 
83 #include <rump/rumpuser.h>
84 
85 #include "ugenhc_user.h"
86 
87 #include "rump_private.h"
88 #include "rump_dev_private.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 	int sc_addr;
103 	int sc_conf;
104 
105 	struct lwp *sc_rhintr;
106 	usbd_xfer_handle sc_intrxfer;
107 
108 	kmutex_t sc_lock;
109 };
110 
111 static int	ugenhc_probe(device_t, cfdata_t, void *);
112 static void	ugenhc_attach(device_t, device_t, void *);
113 
114 CFATTACH_DECL_NEW(ugenhc, sizeof(struct ugenhc_softc),
115 	ugenhc_probe, ugenhc_attach, NULL, NULL);
116 
117 struct rusb_xfer {
118 	struct usbd_xfer rusb_xfer;
119 	int rusb_status; /* now this is a cheap trick */
120 };
121 #define RUSB(x) ((struct rusb_xfer *)x)
122 
123 #define UGENDEV_BASESTR "/dev/ugen"
124 #define UGENDEV_BUFSIZE 32
125 static void
126 makeugendevstr(int devnum, int endpoint, char *buf, size_t len)
127 {
128 
129 	CTASSERT(UGENDEV_BUFSIZE > sizeof(UGENDEV_BASESTR)+sizeof("0.00")+1);
130 	snprintf(buf, len, "%s%d.%02d", UGENDEV_BASESTR, devnum, endpoint);
131 }
132 
133 /*
134  * Our fictional hubbie.
135  */
136 
137 static const usb_device_descriptor_t rumphub_udd = {
138 	.bLength		= USB_DEVICE_DESCRIPTOR_SIZE,
139 	.bDescriptorType	= UDESC_DEVICE,
140 	.bDeviceClass		= UDCLASS_HUB,
141 	.bDeviceSubClass	= UDSUBCLASS_HUB,
142 	.bDeviceProtocol	= UDPROTO_FSHUB,
143 	.bMaxPacketSize		= 64,
144 	.idVendor		= { 0x75, 0x72 },
145 	.idProduct		= { 0x70, 0x6d },
146 	.bNumConfigurations	= 1,
147 };
148 
149 static const usb_config_descriptor_t rumphub_ucd = {
150 	.bLength		= USB_CONFIG_DESCRIPTOR_SIZE,
151 	.bDescriptorType	= UDESC_CONFIG,
152 	.wTotalLength		= { USB_CONFIG_DESCRIPTOR_SIZE
153 				  + USB_INTERFACE_DESCRIPTOR_SIZE
154 				  + USB_ENDPOINT_DESCRIPTOR_SIZE },
155 	.bNumInterface		= 1,
156 	.bmAttributes		= UC_SELF_POWERED | UC_ATTR_MBO,
157 };
158 
159 static const usb_interface_descriptor_t rumphub_uid = {
160 	.bLength		= USB_INTERFACE_DESCRIPTOR_SIZE,
161 	.bDescriptorType	= UDESC_INTERFACE,
162 	.bInterfaceNumber	= 0,
163 	.bNumEndpoints		= 1,
164 	.bInterfaceClass	= UICLASS_HUB,
165 	.bInterfaceSubClass	= UISUBCLASS_HUB,
166 	.bInterfaceProtocol	= UIPROTO_FSHUB,
167 };
168 
169 static const usb_endpoint_descriptor_t rumphub_epd = {
170 	.bLength		= USB_ENDPOINT_DESCRIPTOR_SIZE,
171 	.bDescriptorType	= UDESC_ENDPOINT,
172 	.bmAttributes		= UE_INTERRUPT,
173 	.wMaxPacketSize		= {64, 0},
174 };
175 
176 static const usb_hub_descriptor_t rumphub_hdd = {
177 	.bDescLength		= USB_HUB_DESCRIPTOR_SIZE,
178 	.bDescriptorType	= UDESC_HUB,
179 	.bNbrPorts		= 1,
180 };
181 
182 static usbd_status
183 rumpusb_root_ctrl_start(usbd_xfer_handle xfer)
184 {
185 	usb_device_request_t *req = &xfer->request;
186 	struct ugenhc_softc *sc = xfer->pipe->device->bus->hci_private;
187 	int len, totlen, value, curlen, err;
188 	uint8_t *buf = NULL;
189 
190 	len = totlen = UGETW(req->wLength);
191 	if (len)
192 		buf = KERNADDR(&xfer->dmabuf, 0);
193 	value = UGETW(req->wValue);
194 
195 #define C(x,y) ((x) | ((y) << 8))
196 	switch(C(req->bRequest, req->bmRequestType)) {
197 
198 	case C(UR_GET_CONFIG, UT_READ_DEVICE):
199 		if (len > 0) {
200 			*buf = sc->sc_conf;
201 			totlen = 1;
202 		}
203 		break;
204 
205 	case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
206 		switch (value >> 8) {
207 		case UDESC_DEVICE:
208 			totlen = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
209 			memcpy(buf, &rumphub_udd, totlen);
210 			break;
211 
212 		case UDESC_CONFIG:
213 			totlen = 0;
214 			curlen = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
215 			memcpy(buf, &rumphub_ucd, curlen);
216 			len -= curlen;
217 			buf += curlen;
218 			totlen += curlen;
219 
220 			curlen = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
221 			memcpy(buf, &rumphub_uid, curlen);
222 			len -= curlen;
223 			buf += curlen;
224 			totlen += curlen;
225 
226 			curlen = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE);
227 			memcpy(buf, &rumphub_epd, curlen);
228 			len -= curlen;
229 			buf += curlen;
230 			totlen += curlen;
231 			break;
232 
233 		case UDESC_STRING:
234 #define sd ((usb_string_descriptor_t *)buf)
235 			switch (value & 0xff) {
236 			case 0: /* Language table */
237 				totlen = usb_makelangtbl(sd, len);
238 				break;
239 			case 1: /* Vendor */
240 				totlen = usb_makestrdesc(sd, len, "rod nevada");
241 				break;
242 			case 2: /* Product */
243 				totlen = usb_makestrdesc(sd, len,
244 				    "RUMPUSBHC root hub");
245 				break;
246 			}
247 #undef sd
248 			break;
249 
250 		default:
251 			panic("unhandled read device request");
252 			break;
253 		}
254 		break;
255 
256 	case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
257 		if (value >= USB_MAX_DEVICES) {
258 			err = USBD_IOERROR;
259 			goto ret;
260 		}
261 		sc->sc_addr = value;
262 		break;
263 
264 	case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
265 		if (value != 0 && value != 1) {
266 			err = USBD_IOERROR;
267 			goto ret;
268 		}
269 		sc->sc_conf = value;
270 		break;
271 
272 	case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
273 		switch (value) {
274 		case UHF_PORT_RESET:
275 			sc->sc_port_change |= UPS_C_PORT_RESET;
276 			break;
277 		case UHF_PORT_POWER:
278 			break;
279 		default:
280 			panic("unhandled");
281 		}
282 		break;
283 
284 	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
285 		sc->sc_port_change &= ~value;
286 		break;
287 
288 	case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
289 		totlen = min(len, USB_HUB_DESCRIPTOR_SIZE);
290 		memcpy(buf, &rumphub_hdd, totlen);
291 		break;
292 
293 	case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
294 		/* huh?  other hc's do this */
295 		memset(buf, 0, len);
296 		totlen = len;
297 		break;
298 
299 	case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
300 		{
301 		usb_port_status_t ps;
302 
303 		USETW(ps.wPortStatus, sc->sc_port_status);
304 		USETW(ps.wPortChange, sc->sc_port_change);
305 		totlen = min(len, sizeof(ps));
306 		memcpy(buf, &ps, totlen);
307 		break;
308 		}
309 
310 	default:
311 		panic("unhandled request");
312 		break;
313 	}
314 	err = USBD_NORMAL_COMPLETION;
315 	xfer->actlen = totlen;
316 
317 ret:
318 	xfer->status = err;
319 	mutex_enter(&sc->sc_lock);
320 	usb_transfer_complete(xfer);
321 	mutex_exit(&sc->sc_lock);
322 
323 	return (USBD_IN_PROGRESS);
324 }
325 
326 static usbd_status
327 rumpusb_root_ctrl_transfer(usbd_xfer_handle xfer)
328 {
329 	struct ugenhc_softc *sc = xfer->pipe->device->bus->hci_private;
330 	usbd_status err;
331 
332 	mutex_enter(&sc->sc_lock);
333 	err = usb_insert_transfer(xfer);
334 	mutex_exit(&sc->sc_lock);
335 	if (err)
336 		return (err);
337 
338 	return (rumpusb_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
339 }
340 
341 static void
342 rumpusb_root_ctrl_abort(usbd_xfer_handle xfer)
343 {
344 
345 }
346 
347 static void
348 rumpusb_root_ctrl_close(usbd_pipe_handle pipe)
349 {
350 
351 }
352 
353 static void
354 rumpusb_root_ctrl_cleartoggle(usbd_pipe_handle pipe)
355 {
356 
357 }
358 
359 static void
360 rumpusb_root_ctrl_done(usbd_xfer_handle xfer)
361 {
362 
363 }
364 
365 static const struct usbd_pipe_methods rumpusb_root_ctrl_methods = {
366 	.transfer =	rumpusb_root_ctrl_transfer,
367 	.start =	rumpusb_root_ctrl_start,
368 	.abort =	rumpusb_root_ctrl_abort,
369 	.close =	rumpusb_root_ctrl_close,
370 	.cleartoggle =	rumpusb_root_ctrl_cleartoggle,
371 	.done =		rumpusb_root_ctrl_done,
372 };
373 
374 static usbd_status
375 rumpusb_device_ctrl_start(usbd_xfer_handle xfer)
376 {
377 	usb_device_request_t *req = &xfer->request;
378 	struct ugenhc_softc *sc = xfer->pipe->device->bus->hci_private;
379 	uint8_t *buf = NULL;
380 	int len, totlen;
381 	int value;
382 	int err = 0;
383 	int ru_error, mightfail = 0;
384 
385 	len = totlen = UGETW(req->wLength);
386 	if (len)
387 		buf = KERNADDR(&xfer->dmabuf, 0);
388 	value = UGETW(req->wValue);
389 
390 #define C(x,y) ((x) | ((y) << 8))
391 	switch(C(req->bRequest, req->bmRequestType)) {
392 	case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
393 		switch (value>>8) {
394 		case UDESC_DEVICE:
395 			{
396 			usb_device_descriptor_t uddesc;
397 			totlen = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
398 			memset(buf, 0, totlen);
399 			if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
400 			    USB_GET_DEVICE_DESC, &uddesc, &ru_error) == -1) {
401 				err = EIO;
402 				goto ret;
403 			}
404 			memcpy(buf, &uddesc, totlen);
405 			}
406 
407 			break;
408 		case UDESC_CONFIG:
409 			{
410 			struct usb_full_desc ufdesc;
411 			ufdesc.ufd_config_index = value & 0xff;
412 			ufdesc.ufd_size = len;
413 			ufdesc.ufd_data = buf;
414 			memset(buf, 0, len);
415 			if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
416 			    USB_GET_FULL_DESC, &ufdesc, &ru_error) == -1) {
417 				err = USBD_IOERROR;
418 				goto ret;
419 			}
420 			totlen = ufdesc.ufd_size;
421 			}
422 			break;
423 
424 		case UDESC_STRING:
425 			{
426 			struct usb_device_info udi;
427 
428 			if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
429 			    USB_GET_DEVICEINFO, &udi, &ru_error) == -1) {
430 				printf("ugenhc: get dev info failed: %d\n",
431 				    ru_error);
432 				err = USBD_IOERROR;
433 				goto ret;
434 			}
435 
436 			switch (value & 0xff) {
437 #define sd ((usb_string_descriptor_t *)buf)
438 			case 0: /* language table */
439 				break;
440 			case 1: /* vendor */
441 				totlen = usb_makestrdesc(sd, len,
442 				    udi.udi_vendor);
443 				break;
444 			case 2: /* product */
445 				totlen = usb_makestrdesc(sd, len,
446 				    udi.udi_product);
447 				break;
448 			}
449 #undef sd
450 			}
451 			break;
452 
453 		default:
454 			panic("not handled");
455 		}
456 		break;
457 
458 	case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
459 		/* ignored, ugen won't let us */
460 		break;
461 
462 	case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
463 		if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
464 		    USB_SET_CONFIG, &value, &ru_error) == -1) {
465 			printf("ugenhc: set config failed: %d\n",
466 			    ru_error);
467 			err = USBD_IOERROR;
468 			goto ret;
469 		}
470 		break;
471 
472 	case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
473 		{
474 		struct usb_alt_interface uai;
475 
476 		totlen = 0;
477 		uai.uai_interface_index = UGETW(req->wIndex);
478 		uai.uai_alt_no = value;
479 		if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
480 		    USB_SET_ALTINTERFACE, &uai, &ru_error) == -1) {
481 			printf("ugenhc: set alt interface failed: %d\n",
482 			    ru_error);
483 			err = USBD_IOERROR;
484 			goto ret;
485 		}
486 		break;
487 		}
488 
489 	/*
490 	 * This request might fail unknown reasons.  "EIO" doesn't
491 	 * give much help, and debugging the host ugen would be
492 	 * necessary.  However, since it doesn't seem to really
493 	 * affect anything, just let it fail for now.
494 	 */
495 	case C(0x00, UT_WRITE_CLASS_INTERFACE):
496 		mightfail = 1;
497 		/*FALLTHROUGH*/
498 
499 	/*
500 	 * XXX: don't wildcard these yet.  I want to better figure
501 	 * out what to trap here.  This is kinda silly, though ...
502 	 */
503 
504 	case C(0x01, UT_WRITE_VENDOR_DEVICE):
505 	case C(0x06, UT_WRITE_VENDOR_DEVICE):
506 	case C(0x07, UT_READ_VENDOR_DEVICE):
507 	case C(0x09, UT_READ_VENDOR_DEVICE):
508 	case C(0xfe, UT_READ_CLASS_INTERFACE):
509 	case C(0x01, UT_READ_CLASS_INTERFACE):
510 	case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
511 	case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
512 	case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
513 	case C(UR_GET_DESCRIPTOR, UT_READ_INTERFACE):
514 	case C(0xff, UT_WRITE_CLASS_INTERFACE):
515 	case C(0x20, UT_WRITE_CLASS_INTERFACE):
516 	case C(0x22, UT_WRITE_CLASS_INTERFACE):
517 	case C(0x0a, UT_WRITE_CLASS_INTERFACE):
518 	case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
519 	case C(0x00, UT_WRITE_CLASS_DEVICE):
520 	case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
521 	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
522 	case C(UR_SET_REPORT, UT_WRITE_CLASS_INTERFACE):
523 	case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
524 		{
525 		struct usb_ctl_request ucr;
526 
527 		memcpy(&ucr.ucr_request, req, sizeof(ucr.ucr_request));
528 		ucr.ucr_data = buf;
529 		if (rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[UGEN_EPT_CTRL],
530 		    USB_DO_REQUEST, &ucr, &ru_error) == -1) {
531 			if (!mightfail) {
532 				panic("request failed: %d", ru_error);
533 			} else {
534 				err = ru_error;
535 			}
536 		}
537 		}
538 		break;
539 
540 	default:
541 		panic("unhandled request");
542 		break;
543 	}
544 	xfer->actlen = totlen;
545 	err = USBD_NORMAL_COMPLETION;
546 
547  ret:
548 	xfer->status = err;
549 	mutex_enter(&sc->sc_lock);
550 	usb_transfer_complete(xfer);
551 	mutex_exit(&sc->sc_lock);
552 
553 	return (USBD_IN_PROGRESS);
554 }
555 
556 static usbd_status
557 rumpusb_device_ctrl_transfer(usbd_xfer_handle xfer)
558 {
559 	struct ugenhc_softc *sc = xfer->pipe->device->bus->hci_private;
560 	usbd_status err;
561 
562 	mutex_enter(&sc->sc_lock);
563 	err = usb_insert_transfer(xfer);
564 	mutex_exit(&sc->sc_lock);
565 	if (err)
566 		return (err);
567 
568 	return (rumpusb_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
569 }
570 
571 static void
572 rumpusb_device_ctrl_abort(usbd_xfer_handle xfer)
573 {
574 
575 }
576 
577 static void
578 rumpusb_device_ctrl_close(usbd_pipe_handle pipe)
579 {
580 
581 }
582 
583 static void
584 rumpusb_device_ctrl_cleartoggle(usbd_pipe_handle pipe)
585 {
586 
587 }
588 
589 static void
590 rumpusb_device_ctrl_done(usbd_xfer_handle xfer)
591 {
592 
593 }
594 
595 static const struct usbd_pipe_methods rumpusb_device_ctrl_methods = {
596 	.transfer =	rumpusb_device_ctrl_transfer,
597 	.start =	rumpusb_device_ctrl_start,
598 	.abort =	rumpusb_device_ctrl_abort,
599 	.close =	rumpusb_device_ctrl_close,
600 	.cleartoggle =	rumpusb_device_ctrl_cleartoggle,
601 	.done =		rumpusb_device_ctrl_done,
602 };
603 
604 static void
605 rhscintr(void *arg)
606 {
607 	char buf[UGENDEV_BUFSIZE];
608 	struct ugenhc_softc *sc = arg;
609 	usbd_xfer_handle xfer;
610 	int fd, error;
611 
612 	makeugendevstr(sc->sc_devnum, 0, buf, sizeof(buf));
613 
614 	for (;;) {
615 		/*
616 		 * Detect device attach.
617 		 */
618 
619 		for (;;) {
620 			error = rumpuser_open(buf, RUMPUSER_OPEN_RDWR, &fd);
621 			if (error == 0)
622 				break;
623 			kpause("ugwait", false, hz/4, NULL);
624 		}
625 
626 		sc->sc_ugenfd[UGEN_EPT_CTRL] = fd;
627 		sc->sc_port_status = UPS_CURRENT_CONNECT_STATUS
628 		    | UPS_PORT_ENABLED | UPS_PORT_POWER;
629 		sc->sc_port_change = UPS_C_CONNECT_STATUS | UPS_C_PORT_RESET;
630 
631 		xfer = sc->sc_intrxfer;
632 		memset(xfer->buffer, 0xff, xfer->length);
633 		xfer->actlen = xfer->length;
634 		xfer->status = USBD_NORMAL_COMPLETION;
635 
636 		mutex_enter(&sc->sc_lock);
637 		usb_transfer_complete(xfer);
638 		mutex_exit(&sc->sc_lock);
639 
640 		kpause("ugwait2", false, hz, NULL);
641 
642 		/*
643 		 * Detect device detach.
644 		 */
645 
646 		for (;;) {
647 			fd = rumpuser_open(buf, RUMPUSER_OPEN_RDWR, &error);
648 			if (fd == -1)
649 				break;
650 
651 			error = rumpuser_close(fd);
652 			kpause("ugwait2", false, hz/4, NULL);
653 		}
654 
655 		sc->sc_port_status = ~(UPS_CURRENT_CONNECT_STATUS
656 		    | UPS_PORT_ENABLED | UPS_PORT_POWER);
657 		sc->sc_port_change = UPS_C_CONNECT_STATUS | UPS_C_PORT_RESET;
658 
659 		error = rumpuser_close(sc->sc_ugenfd[UGEN_EPT_CTRL]);
660 		sc->sc_ugenfd[UGEN_EPT_CTRL] = -1;
661 
662 		xfer = sc->sc_intrxfer;
663 		memset(xfer->buffer, 0xff, xfer->length);
664 		xfer->actlen = xfer->length;
665 		xfer->status = USBD_NORMAL_COMPLETION;
666 		mutex_enter(&sc->sc_lock);
667 		usb_transfer_complete(xfer);
668 		mutex_exit(&sc->sc_lock);
669 
670 		kpause("ugwait3", false, hz, NULL);
671 	}
672 
673 	kthread_exit(0);
674 }
675 
676 static usbd_status
677 rumpusb_root_intr_start(usbd_xfer_handle xfer)
678 {
679 	struct ugenhc_softc *sc = xfer->pipe->device->bus->hci_private;
680 	int error;
681 
682 	mutex_enter(&sc->sc_lock);
683 	sc->sc_intrxfer = xfer;
684 	if (!sc->sc_rhintr) {
685 		error = kthread_create(PRI_NONE, 0, NULL,
686 		    rhscintr, sc, &sc->sc_rhintr, "ugenrhi");
687 		if (error)
688 			xfer->status = USBD_IOERROR;
689 	}
690 	mutex_exit(&sc->sc_lock);
691 
692 	return (USBD_IN_PROGRESS);
693 }
694 
695 static usbd_status
696 rumpusb_root_intr_transfer(usbd_xfer_handle xfer)
697 {
698 	struct ugenhc_softc *sc = xfer->pipe->device->bus->hci_private;
699 	usbd_status err;
700 
701 	mutex_enter(&sc->sc_lock);
702 	err = usb_insert_transfer(xfer);
703 	mutex_exit(&sc->sc_lock);
704 	if (err)
705 		return (err);
706 
707 	return (rumpusb_root_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue)));
708 }
709 
710 static void
711 rumpusb_root_intr_abort(usbd_xfer_handle xfer)
712 {
713 
714 }
715 
716 static void
717 rumpusb_root_intr_close(usbd_pipe_handle pipe)
718 {
719 
720 }
721 
722 static void
723 rumpusb_root_intr_cleartoggle(usbd_pipe_handle pipe)
724 {
725 
726 }
727 
728 static void
729 rumpusb_root_intr_done(usbd_xfer_handle xfer)
730 {
731 
732 }
733 
734 static const struct usbd_pipe_methods rumpusb_root_intr_methods = {
735 	.transfer =	rumpusb_root_intr_transfer,
736 	.start =	rumpusb_root_intr_start,
737 	.abort =	rumpusb_root_intr_abort,
738 	.close =	rumpusb_root_intr_close,
739 	.cleartoggle =	rumpusb_root_intr_cleartoggle,
740 	.done =		rumpusb_root_intr_done,
741 };
742 
743 static usbd_status
744 rumpusb_device_bulk_start(usbd_xfer_handle xfer)
745 {
746 	struct ugenhc_softc *sc = xfer->pipe->device->bus->hci_private;
747 	usb_endpoint_descriptor_t *ed = xfer->pipe->endpoint->edesc;
748 	size_t n, done;
749 	bool isread;
750 	int len, error, endpt;
751 	uint8_t *buf;
752 	int xfererr = USBD_NORMAL_COMPLETION;
753 	int shortval, i;
754 
755 	ed = xfer->pipe->endpoint->edesc;
756 	endpt = ed->bEndpointAddress;
757 	isread = UE_GET_DIR(endpt) == UE_DIR_IN;
758 	endpt = UE_GET_ADDR(endpt);
759 	KASSERT(endpt < UGEN_NEPTS);
760 
761 	buf = KERNADDR(&xfer->dmabuf, 0);
762 	done = 0;
763 	if ((ed->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS) {
764 		for (i = 0, len = 0; i < xfer->nframes; i++)
765 			len += xfer->frlengths[i];
766 	} else {
767 		KASSERT(xfer->length);
768 		len = xfer->length;
769 	}
770 	shortval = (xfer->flags & USBD_SHORT_XFER_OK) != 0;
771 
772 	while (RUSB(xfer)->rusb_status == 0) {
773 		if (isread) {
774 			struct rumpuser_iovec iov;
775 
776 			rumpcomp_ugenhc_ioctl(sc->sc_ugenfd[endpt],
777 			    USB_SET_SHORT_XFER, &shortval, &error);
778 			iov.iov_base = buf+done;
779 			iov.iov_len = len-done;
780 			error = rumpuser_iovread(sc->sc_ugenfd[endpt], &iov, 1,
781 			    RUMPUSER_IOV_NOSEEK, &n);
782 			if (error) {
783 				n = 0;
784 				if (done == 0) {
785 					if (error == ETIMEDOUT)
786 						continue;
787 					xfererr = USBD_IOERROR;
788 					goto out;
789 				}
790 			}
791 			done += n;
792 			if (done == len)
793 				break;
794 		} else {
795 			struct rumpuser_iovec iov;
796 
797 			iov.iov_base = buf;
798 			iov.iov_len = len;
799 			error = rumpuser_iovwrite(sc->sc_ugenfd[endpt], &iov, 1,
800 			    RUMPUSER_IOV_NOSEEK, &n);
801 			done = n;
802 			if (done == len)
803 				break;
804 			else if (!error)
805 				panic("short write");
806 
807 			xfererr = USBD_IOERROR;
808 			goto out;
809 		}
810 
811 		if (shortval) {
812 			/*
813 			 * Holy XXX, bitman.  I get >16byte interrupt
814 			 * transfers from ugen in 16 byte chunks.
815 			 * Don't know how to better fix this for now.
816 			 * Of course this hack will fail e.g. if someone
817 			 * sports other magic values or if the transfer
818 			 * happens to be an integral multiple of 16
819 			 * in size ....
820 			 */
821 			if ((ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT
822 			    && n == 16) {
823 				continue;
824 			} else {
825 				break;
826 			}
827 		}
828 	}
829 
830 	if (RUSB(xfer)->rusb_status == 0) {
831 		xfer->actlen = done;
832 	} else {
833 		xfererr = USBD_CANCELLED;
834 		RUSB(xfer)->rusb_status = 2;
835 	}
836  out:
837 	if ((ed->bmAttributes & UE_XFERTYPE) == UE_ISOCHRONOUS)
838 		if (done != len)
839 			panic("lazy bum");
840 	xfer->status = xfererr;
841 	mutex_enter(&sc->sc_lock);
842 	usb_transfer_complete(xfer);
843 	mutex_exit(&sc->sc_lock);
844 	return (USBD_IN_PROGRESS);
845 }
846 
847 static void
848 doxfer_kth(void *arg)
849 {
850 	usbd_pipe_handle pipe = arg;
851 	struct ugenhc_softc *sc = pipe->device->bus->hci_private;
852 
853 	mutex_enter(&sc->sc_lock);
854 	do {
855 		usbd_xfer_handle xfer = SIMPLEQ_FIRST(&pipe->queue);
856 		mutex_exit(&sc->sc_lock);
857 		rumpusb_device_bulk_start(xfer);
858 		mutex_enter(&sc->sc_lock);
859 	} while (!SIMPLEQ_EMPTY(&pipe->queue));
860 	mutex_exit(&sc->sc_lock);
861 	kthread_exit(0);
862 }
863 
864 static usbd_status
865 rumpusb_device_bulk_transfer(usbd_xfer_handle xfer)
866 {
867 	struct ugenhc_softc *sc = xfer->pipe->device->bus->hci_private;
868 	usbd_status err;
869 
870 	if (!rump_threads) {
871 		/* XXX: lie about supporting async transfers */
872 		if ((xfer->flags & USBD_SYNCHRONOUS) == 0) {
873 			printf("non-threaded rump does not support "
874 			    "async transfers.\n");
875 			return USBD_IN_PROGRESS;
876 		}
877 
878 		mutex_enter(&sc->sc_lock);
879 		err = usb_insert_transfer(xfer);
880 		mutex_exit(&sc->sc_lock);
881 		if (err)
882 			return err;
883 
884 		return rumpusb_device_bulk_start(
885 		    SIMPLEQ_FIRST(&xfer->pipe->queue));
886 	} else {
887 		mutex_enter(&sc->sc_lock);
888 		err = usb_insert_transfer(xfer);
889 		mutex_exit(&sc->sc_lock);
890 		if (err)
891 			return err;
892 		kthread_create(PRI_NONE, 0, NULL, doxfer_kth, xfer->pipe, NULL,
893 		    "rusbhcxf");
894 
895 		return USBD_IN_PROGRESS;
896 	}
897 }
898 
899 /* wait for transfer to abort.  yea, this is cheesy (from a spray can) */
900 static void
901 rumpusb_device_bulk_abort(usbd_xfer_handle xfer)
902 {
903 	struct rusb_xfer *rx = RUSB(xfer);
904 
905 	rx->rusb_status = 1;
906 	while (rx->rusb_status < 2) {
907 		kpause("jopo", false, hz/10, NULL);
908 	}
909 }
910 
911 static void
912 rumpusb_device_bulk_close(usbd_pipe_handle pipe)
913 {
914 	struct ugenhc_softc *sc = pipe->device->bus->hci_private;
915 	int endpt = pipe->endpoint->edesc->bEndpointAddress;
916 	usbd_xfer_handle xfer;
917 
918 	KASSERT(mutex_owned(&sc->sc_lock));
919 
920 	endpt = UE_GET_ADDR(endpt);
921 
922 	while ((xfer = SIMPLEQ_FIRST(&pipe->queue)) != NULL)
923 		rumpusb_device_bulk_abort(xfer);
924 
925 	rumpuser_close(sc->sc_ugenfd[endpt]);
926 	sc->sc_ugenfd[endpt] = -1;
927 	sc->sc_fdmodes[endpt] = -1;
928 }
929 
930 static void
931 rumpusb_device_bulk_cleartoggle(usbd_pipe_handle pipe)
932 {
933 
934 }
935 
936 static void
937 rumpusb_device_bulk_done(usbd_xfer_handle xfer)
938 {
939 
940 }
941 
942 static const struct usbd_pipe_methods rumpusb_device_bulk_methods = {
943 	.transfer =	rumpusb_device_bulk_transfer,
944 	.start =	rumpusb_device_bulk_start,
945 	.abort =	rumpusb_device_bulk_abort,
946 	.close =	rumpusb_device_bulk_close,
947 	.cleartoggle =	rumpusb_device_bulk_cleartoggle,
948 	.done =		rumpusb_device_bulk_done,
949 };
950 
951 static usbd_status
952 ugenhc_open(struct usbd_pipe *pipe)
953 {
954 	usbd_device_handle dev = pipe->device;
955 	struct ugenhc_softc *sc = dev->bus->hci_private;
956 	usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc;
957 	u_int8_t addr = dev->address;
958 	u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE;
959 	char buf[UGENDEV_BUFSIZE];
960 	int endpt, oflags, error;
961 	int fd, val;
962 
963 	if (addr == sc->sc_addr) {
964 		switch (xfertype) {
965 		case UE_CONTROL:
966 			pipe->methods = &rumpusb_root_ctrl_methods;
967 			break;
968 		case UE_INTERRUPT:
969 			pipe->methods = &rumpusb_root_intr_methods;
970 			break;
971 		default:
972 			panic("%d not supported", xfertype);
973 			break;
974 		}
975 	} else {
976 		switch (xfertype) {
977 		case UE_CONTROL:
978 			pipe->methods = &rumpusb_device_ctrl_methods;
979 			break;
980 		case UE_INTERRUPT:
981 		case UE_BULK:
982 		case UE_ISOCHRONOUS:
983 			pipe->methods = &rumpusb_device_bulk_methods;
984 			endpt = pipe->endpoint->edesc->bEndpointAddress;
985 			if (UE_GET_DIR(endpt) == UE_DIR_IN) {
986 				oflags = O_RDONLY;
987 			} else {
988 				oflags = O_WRONLY;
989 			}
990 			endpt = UE_GET_ADDR(endpt);
991 
992 			if (oflags != O_RDONLY && xfertype == UE_ISOCHRONOUS) {
993 				printf("WARNING: faking isoc write open\n");
994 				oflags = O_RDONLY;
995 			}
996 
997 			if (sc->sc_fdmodes[endpt] == oflags
998 			    || sc->sc_fdmodes[endpt] == O_RDWR)
999 				break;
1000 
1001 			if (sc->sc_fdmodes[endpt] != -1) {
1002 				/* XXX: closing from under someone? */
1003 				error = rumpuser_close(sc->sc_ugenfd[endpt]);
1004 				oflags = O_RDWR;
1005 			}
1006 
1007 			makeugendevstr(sc->sc_devnum, endpt, buf, sizeof(buf));
1008 			/* XXX: theoretically should convert oflags */
1009 			error = rumpuser_open(buf, oflags, &fd);
1010 			if (error != 0) {
1011 				return USBD_INVAL; /* XXX: no mapping */
1012 			}
1013 			val = 100;
1014 			if (rumpcomp_ugenhc_ioctl(fd, USB_SET_TIMEOUT, &val,
1015 			    &error) == -1)
1016 				panic("timeout set failed");
1017 			sc->sc_ugenfd[endpt] = fd;
1018 			sc->sc_fdmodes[endpt] = oflags;
1019 
1020 			break;
1021 		default:
1022 			panic("%d not supported", xfertype);
1023 			break;
1024 
1025 		}
1026 	}
1027 	return 0;
1028 }
1029 
1030 static void
1031 ugenhc_softint(void *arg)
1032 {
1033 
1034 }
1035 
1036 static void
1037 ugenhc_poll(struct usbd_bus *ubus)
1038 {
1039 
1040 }
1041 
1042 static usbd_status
1043 ugenhc_allocm(struct usbd_bus *bus, usb_dma_t *dma, uint32_t size)
1044 {
1045 	struct ugenhc_softc *sc = bus->hci_private;
1046 
1047 	return usb_allocmem(&sc->sc_bus, size, 0, dma);
1048 }
1049 
1050 static void
1051 ugenhc_freem(struct usbd_bus *bus, usb_dma_t *dma)
1052 {
1053 	struct ugenhc_softc *sc = bus->hci_private;
1054 
1055 	usb_freemem(&sc->sc_bus, dma);
1056 }
1057 
1058 static struct usbd_xfer *
1059 ugenhc_allocx(struct usbd_bus *bus)
1060 {
1061 	usbd_xfer_handle xfer;
1062 
1063 	xfer = kmem_zalloc(sizeof(struct usbd_xfer), KM_SLEEP);
1064 	xfer->busy_free = XFER_BUSY;
1065 
1066 	return xfer;
1067 }
1068 
1069 static void
1070 ugenhc_freex(struct usbd_bus *bus, struct usbd_xfer *xfer)
1071 {
1072 
1073 	kmem_free(xfer, sizeof(struct usbd_xfer));
1074 }
1075 
1076 
1077 static void
1078 ugenhc_getlock(struct usbd_bus *bus, kmutex_t **lock)
1079 {
1080 	struct ugenhc_softc *sc = bus->hci_private;
1081 
1082 	*lock = &sc->sc_lock;
1083 }
1084 
1085 struct ugenhc_pipe {
1086 	struct usbd_pipe pipe;
1087 };
1088 
1089 static const struct usbd_bus_methods ugenhc_bus_methods = {
1090 	.open_pipe =	ugenhc_open,
1091 	.soft_intr =	ugenhc_softint,
1092 	.do_poll =	ugenhc_poll,
1093 	.allocm = 	ugenhc_allocm,
1094 	.freem = 	ugenhc_freem,
1095 	.allocx = 	ugenhc_allocx,
1096 	.freex =	ugenhc_freex,
1097 	.get_lock =	ugenhc_getlock
1098 };
1099 
1100 static int
1101 ugenhc_probe(device_t parent, cfdata_t match, void *aux)
1102 {
1103 	char buf[UGENDEV_BUFSIZE];
1104 
1105 	makeugendevstr(match->cf_unit, 0, buf, sizeof(buf));
1106 	if (rumpuser_getfileinfo(buf, NULL, NULL) != 0)
1107 		return 0;
1108 
1109 	return 1;
1110 }
1111 
1112 static void
1113 ugenhc_attach(device_t parent, device_t self, void *aux)
1114 {
1115 	struct mainbus_attach_args *maa = aux;
1116 	struct ugenhc_softc *sc = device_private(self);
1117 
1118 	aprint_normal("\n");
1119 
1120 	memset(sc, 0, sizeof(*sc));
1121 	memset(&sc->sc_ugenfd, -1, sizeof(sc->sc_ugenfd));
1122 	memset(&sc->sc_fdmodes, -1, sizeof(sc->sc_fdmodes));
1123 
1124 	sc->sc_bus.usbrev = USBREV_2_0;
1125 	sc->sc_bus.methods = &ugenhc_bus_methods;
1126 	sc->sc_bus.hci_private = sc;
1127 	sc->sc_bus.pipe_size = sizeof(struct ugenhc_pipe);
1128 	sc->sc_devnum = maa->maa_unit;
1129 
1130 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
1131 
1132 	config_found(self, &sc->sc_bus, usbctlprint);
1133 }
1134