proc->thread stage 4: rework the VFS and DEVICE subsystems to take thread
[dragonfly.git] / sys / bus / usb / usb.c
CommitLineData
984263bc
MD
1/* $NetBSD: usb.c,v 1.33 1999/11/22 21:57:09 augustss Exp $ */
2/* $FreeBSD: src/sys/dev/usb/usb.c,v 1.26.2.9 2002/11/13 15:15:22 joe Exp $ */
dadab5e9 3/* $DragonFly: src/sys/bus/usb/usb.c,v 1.4 2003/06/25 03:55:50 dillon Exp $ */
984263bc
MD
4
5/*
6 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Lennart Augustsson (lennart@augustsson.net) at
11 * Carlstedt Research & Technology.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the NetBSD
24 * Foundation, Inc. and its contributors.
25 * 4. Neither the name of The NetBSD Foundation nor the names of its
26 * contributors may be used to endorse or promote products derived
27 * from this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 * POSSIBILITY OF SUCH DAMAGE.
40 */
41
42/*
43 * USB specifications and other documentation can be found at
44 * http://www.usb.org/developers/data/ and
45 * http://www.usb.org/developers/index.html .
46 */
47
48#include <sys/param.h>
49#include <sys/systm.h>
50#include <sys/kernel.h>
51#include <sys/malloc.h>
52#if defined(__NetBSD__) || defined(__OpenBSD__)
53#include <sys/device.h>
54#include <sys/kthread.h>
55#include <sys/proc.h>
56#elif defined(__FreeBSD__)
57#include <sys/module.h>
58#include <sys/bus.h>
59#include <sys/filio.h>
60#include <sys/uio.h>
61#endif
62#include <sys/conf.h>
63#include <sys/poll.h>
64#include <sys/select.h>
65#include <sys/vnode.h>
66#include <sys/signalvar.h>
67#include <sys/sysctl.h>
68
69#include <dev/usb/usb.h>
70#include <dev/usb/usbdi.h>
71#include <dev/usb/usbdi_util.h>
72
73#define USBUNIT(d) (minor(d)) /* usb_discover device nodes, kthread */
74#define USB_DEV_MINOR 255 /* event queue device */
75
76#if defined(__FreeBSD__)
77MALLOC_DEFINE(M_USB, "USB", "USB");
78MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device");
79MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller");
80
81#include "usb_if.h"
82#endif /* defined(__FreeBSD__) */
83
84#include <machine/bus.h>
85
86#include <dev/usb/usbdivar.h>
87#include <dev/usb/usb_quirks.h>
88
89/* Define this unconditionally in case a kernel module is loaded that
90 * has been compiled with debugging options.
91 */
92SYSCTL_NODE(_hw, OID_AUTO, usb, CTLFLAG_RW, 0, "USB debugging");
93
94#ifdef USB_DEBUG
95#define DPRINTF(x) if (usbdebug) logprintf x
96#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x
97int usbdebug = 0;
98SYSCTL_INT(_hw_usb, OID_AUTO, debug, CTLFLAG_RW,
99 &usbdebug, 0, "usb debug level");
100#ifdef USB_DEBUG
101extern int uhcidebug;
102#endif
103#ifdef USB_DEBUG
104extern int ohcidebug;
105#endif
106/*
107 * 0 - do usual exploration
108 * 1 - do not use timeout exploration
109 * >1 - do no exploration
110 */
111int usb_noexplore = 0;
112#else
113#define DPRINTF(x)
114#define DPRINTFN(n,x)
115#endif
116
117struct usb_softc {
118 USBBASEDEVICE sc_dev; /* base device */
119 usbd_bus_handle sc_bus; /* USB controller */
120 struct usbd_port sc_port; /* dummy port for root hub */
121
122#if defined(__FreeBSD__)
123 /* This part should be deleted when kthreads is available */
124 struct selinfo sc_consel; /* waiting for connect change */
125#else
126 struct proc *sc_event_thread;
127#endif
128
129 char sc_dying;
130};
131
132#if defined(__NetBSD__) || defined(__OpenBSD__)
133cdev_decl(usb);
134#elif defined(__FreeBSD__)
135d_open_t usbopen;
136d_close_t usbclose;
137d_read_t usbread;
138d_ioctl_t usbioctl;
139int usbpoll(dev_t, int, usb_proc_ptr);
140
141struct cdevsw usb_cdevsw = {
142 /* open */ usbopen,
143 /* close */ usbclose,
144 /* read */ usbread,
145 /* write */ nowrite,
146 /* ioctl */ usbioctl,
147 /* poll */ usbpoll,
148 /* mmap */ nommap,
149 /* strategy */ nostrategy,
150 /* name */ "usb",
151 /* maj */ USB_CDEV_MAJOR,
152 /* dump */ nodump,
153 /* psize */ nopsize,
154 /* flags */ 0,
155 /* bmaj */ -1
156};
157#endif
158
159Static usbd_status usb_discover(struct usb_softc *);
160#if defined(__NetBSD__) || defined(__OpenBSD__)
161Static void usb_create_event_thread(void *);
162Static void usb_event_thread(void *);
163#endif
164
165#define USB_MAX_EVENTS 50
166struct usb_event_q {
167 struct usb_event ue;
168 SIMPLEQ_ENTRY(usb_event_q) next;
169};
170Static SIMPLEQ_HEAD(, usb_event_q) usb_events =
171 SIMPLEQ_HEAD_INITIALIZER(usb_events);
172Static int usb_nevents = 0;
173Static struct selinfo usb_selevent;
41c20dac 174Static struct thread *usb_async_proc; /* process who wants USB SIGIO */
984263bc
MD
175Static int usb_dev_open = 0;
176
177Static int usb_get_next_event(struct usb_event *);
178
179#if defined(__NetBSD__) || defined(__OpenBSD__)
180/* Flag to see if we are in the cold boot process. */
181extern int cold;
182#endif
183
184Static const char *usbrev_str[] = USBREV_STR;
185
186USB_DECLARE_DRIVER_INIT(usb,
187 DEVMETHOD(device_suspend, bus_generic_suspend),
188 DEVMETHOD(device_resume, bus_generic_resume),
189 DEVMETHOD(device_shutdown, bus_generic_shutdown)
190 );
191
192USB_MATCH(usb)
193{
194 DPRINTF(("usbd_match\n"));
195 return (UMATCH_GENERIC);
196}
197
198USB_ATTACH(usb)
199{
200#if defined(__NetBSD__) || defined(__OpenBSD__)
201 struct usb_softc *sc = (struct usb_softc *)self;
202#elif defined(__FreeBSD__)
203 struct usb_softc *sc = device_get_softc(self);
204 void *aux = device_get_ivars(self);
205 static int global_init_done = 0;
206#endif
207 usbd_device_handle dev;
208 usbd_status err;
209 int usbrev;
210
211 sc->sc_dev = self;
212
213 DPRINTF(("usbd_attach\n"));
214
215 usbd_init();
216 sc->sc_bus = aux;
217 sc->sc_bus->usbctl = sc;
218 sc->sc_port.power = USB_MAX_POWER;
219
220#if defined(__FreeBSD__)
221 printf("%s", USBDEVNAME(sc->sc_dev));
222#endif
223 usbrev = sc->sc_bus->usbrev;
224 printf(": USB revision %s", usbrev_str[usbrev]);
225 if (usbrev != USBREV_1_0 && usbrev != USBREV_1_1) {
226 printf(", not supported\n");
227 USB_ATTACH_ERROR_RETURN;
228 }
229 printf("\n");
230
231 err = usbd_new_device(USBDEV(sc->sc_dev), sc->sc_bus, 0, 0, 0,
232 &sc->sc_port);
233 if (!err) {
234 dev = sc->sc_port.device;
235 if (dev->hub == NULL) {
236 sc->sc_dying = 1;
237 printf("%s: root device is not a hub\n",
238 USBDEVNAME(sc->sc_dev));
239 USB_ATTACH_ERROR_RETURN;
240 }
241 sc->sc_bus->root_hub = dev;
242#if 1
243 /*
244 * Turning this code off will delay attachment of USB devices
245 * until the USB event thread is running, which means that
246 * the keyboard will not work until after cold boot.
247 */
248 if (cold) {
249 sc->sc_bus->use_polling++;
250 dev->hub->explore(sc->sc_bus->root_hub);
251 sc->sc_bus->use_polling--;
252 }
253#endif
254 } else {
255 printf("%s: root hub problem, error=%d\n",
256 USBDEVNAME(sc->sc_dev), err);
257 sc->sc_dying = 1;
258 }
259
260 kthread_create(usb_create_event_thread, sc);
261
262#if defined(__FreeBSD__)
263 /* The per controller devices (used for usb_discover) */
264 make_dev(&usb_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR,
265 0644, "usb%d", device_get_unit(self));
266 if (!global_init_done) {
267 /* The device spitting out events */
268 make_dev(&usb_cdevsw, USB_DEV_MINOR, UID_ROOT, GID_OPERATOR,
269 0644, "usb");
270 global_init_done = 1;
271 }
272#endif
273
274 USB_ATTACH_SUCCESS_RETURN;
275}
276
277#if defined(__NetBSD__) || defined(__OpenBSD__)
278void
279usb_create_event_thread(void *arg)
280{
281 struct usb_softc *sc = arg;
282
283 if (kthread_create1(usb_event_thread, sc, &sc->sc_event_thread,
284 "%s", sc->sc_dev.dv_xname)) {
285 printf("%s: unable to create event thread for\n",
286 sc->sc_dev.dv_xname);
287 panic("usb_create_event_thread");
288 }
289}
290
291void
292usb_event_thread(void *arg)
293{
294 struct usb_softc *sc = arg;
295
296 DPRINTF(("usb_event_thread: start\n"));
297
298 while (!sc->sc_dying) {
299#ifdef USB_DEBUG
300 if (usb_noexplore < 2)
301#endif
302 usb_discover(sc);
303 (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt",
304#ifdef USB_DEBUG
305 usb_noexplore ? 0 :
306#endif
307 hz*60
308 );
309 DPRINTFN(2,("usb_event_thread: woke up\n"));
310 }
311 sc->sc_event_thread = 0;
312
313 /* In case parent is waiting for us to exit. */
314 wakeup(sc);
315
316 DPRINTF(("usb_event_thread: exit\n"));
317 kthread_exit(0);
318}
319
320int
321usbctlprint(void *aux, const char *pnp)
322{
323 /* only "usb"es can attach to host controllers */
324 if (pnp)
325 printf("usb at %s", pnp);
326
327 return (UNCONF);
328}
329#endif /* defined(__NetBSD__) || defined(__OpenBSD__) */
330
331int
332usbopen(dev_t dev, int flag, int mode, usb_proc_ptr p)
333{
334 int unit = USBUNIT(dev);
335 struct usb_softc *sc;
336
337 if (unit == USB_DEV_MINOR) {
338 if (usb_dev_open)
339 return (EBUSY);
340 usb_dev_open = 1;
41c20dac 341 usb_async_proc = NULL;
984263bc
MD
342 return (0);
343 } else {
344 USB_GET_SC_OPEN(usb, unit, sc);
345
346 if (sc->sc_dying)
347 return (EIO);
348
349 return (0);
350 }
351}
352
353int
354usbread(dev_t dev, struct uio *uio, int flag)
355{
356 struct usb_event ue;
357 int unit = USBUNIT(dev);
358 int s, error, n;
359
360 if (unit != USB_DEV_MINOR)
361 return (ENODEV);
362
363 if (uio->uio_resid != sizeof(struct usb_event))
364 return (EINVAL);
365
366 error = 0;
367 s = splusb();
368 for (;;) {
369 n = usb_get_next_event(&ue);
370 if (n != 0)
371 break;
372 if (flag & IO_NDELAY) {
373 error = EWOULDBLOCK;
374 break;
375 }
376 error = tsleep(&usb_events, PZERO | PCATCH, "usbrea", 0);
377 if (error)
378 break;
379 }
380 splx(s);
381 if (!error)
382 error = uiomove((void *)&ue, uio->uio_resid, uio);
383
384 return (error);
385}
386
387int
388usbclose(dev_t dev, int flag, int mode, usb_proc_ptr p)
389{
390 int unit = USBUNIT(dev);
391
392 if (unit == USB_DEV_MINOR) {
41c20dac 393 usb_async_proc = NULL;
984263bc
MD
394 usb_dev_open = 0;
395 }
396
397 return (0);
398}
399
400int
401usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
402{
403 struct usb_softc *sc;
404 int unit = USBUNIT(devt);
405
406 if (unit == USB_DEV_MINOR) {
407 switch (cmd) {
408 case FIONBIO:
409 /* All handled in the upper FS layer. */
410 return (0);
411
412 case FIOASYNC:
413 if (*(int *)data)
414 usb_async_proc = p;
415 else
41c20dac 416 usb_async_proc = NULL;
984263bc
MD
417 return (0);
418
419 default:
420 return (EINVAL);
421 }
422 }
423
424 USB_GET_SC(usb, unit, sc);
425
426 if (sc->sc_dying)
427 return (EIO);
428
429 switch (cmd) {
430#if defined(__FreeBSD__)
431 /* This part should be deleted when kthreads is available */
432 case USB_DISCOVER:
433 usb_discover(sc);
434 break;
435#endif
436#ifdef USB_DEBUG
437 case USB_SETDEBUG:
438 usbdebug = ((*(int *)data) & 0x000000ff);
439 uhcidebug = ((*(int *)data) & 0x0000ff00) >> 8;
440 ohcidebug = ((*(int *)data) & 0x00ff0000) >> 16;
441 break;
442#endif
443 case USB_REQUEST:
444 {
445 struct usb_ctl_request *ur = (void *)data;
446 int len = UGETW(ur->ucr_request.wLength);
447 struct iovec iov;
448 struct uio uio;
449 void *ptr = 0;
450 int addr = ur->ucr_addr;
451 usbd_status err;
452 int error = 0;
453
454 DPRINTF(("usbioctl: USB_REQUEST addr=%d len=%d\n", addr, len));
455 if (len < 0 || len > 32768)
456 return (EINVAL);
457 if (addr < 0 || addr >= USB_MAX_DEVICES ||
458 sc->sc_bus->devices[addr] == 0)
459 return (EINVAL);
460 if (len != 0) {
461 iov.iov_base = (caddr_t)ur->ucr_data;
462 iov.iov_len = len;
463 uio.uio_iov = &iov;
464 uio.uio_iovcnt = 1;
465 uio.uio_resid = len;
466 uio.uio_offset = 0;
467 uio.uio_segflg = UIO_USERSPACE;
468 uio.uio_rw =
469 ur->ucr_request.bmRequestType & UT_READ ?
470 UIO_READ : UIO_WRITE;
dadab5e9 471 uio.uio_td = p;
984263bc
MD
472 ptr = malloc(len, M_TEMP, M_WAITOK);
473 if (uio.uio_rw == UIO_WRITE) {
474 error = uiomove(ptr, len, &uio);
475 if (error)
476 goto ret;
477 }
478 }
479 err = usbd_do_request_flags(sc->sc_bus->devices[addr],
480 &ur->ucr_request, ptr, ur->ucr_flags, &ur->ucr_actlen);
481 if (err) {
482 error = EIO;
483 goto ret;
484 }
485 if (len != 0) {
486 if (uio.uio_rw == UIO_READ) {
487 error = uiomove(ptr, len, &uio);
488 if (error)
489 goto ret;
490 }
491 }
492 ret:
493 if (ptr)
494 free(ptr, M_TEMP);
495 return (error);
496 }
497
498 case USB_DEVICEINFO:
499 {
500 struct usb_device_info *di = (void *)data;
501 int addr = di->udi_addr;
502 usbd_device_handle dev;
503
504 if (addr < 1 || addr >= USB_MAX_DEVICES)
505 return (EINVAL);
506 dev = sc->sc_bus->devices[addr];
507 if (dev == 0)
508 return (ENXIO);
509 usbd_fill_deviceinfo(dev, di);
510 break;
511 }
512
513 case USB_DEVICESTATS:
514 *(struct usb_device_stats *)data = sc->sc_bus->stats;
515 break;
516
517 default:
518 return (EINVAL);
519 }
520 return (0);
521}
522
523int
524usbpoll(dev_t dev, int events, usb_proc_ptr p)
525{
526 int revents, mask, s;
527 int unit = USBUNIT(dev);
528
529 if (unit == USB_DEV_MINOR) {
530 revents = 0;
531 mask = POLLIN | POLLRDNORM;
532
533 s = splusb();
534 if ((events & mask) && usb_nevents > 0)
535 revents |= events & mask;
536 if (revents == 0 && (events & mask)) {
537 DPRINTFN(2,("usb: sleeping on %p\n", &usb_selevent));
538 selrecord(p, &usb_selevent);
539 }
540 splx(s);
541
542 return (revents);
543 } else {
544#if defined(__FreeBSD__)
545 /* This part should be deleted when kthreads is available */
546 struct usb_softc *sc;
547
548 USB_GET_SC(usb, unit, sc);
549
550 revents = 0;
551 mask = POLLOUT | POLLRDNORM;
552
553 s = splusb();
554 if ((events & mask) && sc->sc_bus->needs_explore)
555 revents |= events & mask;
556 if (revents == 0 && (events & mask))
557 selrecord(p, &sc->sc_consel);
558 splx(s);
559
560 return (revents);
561#else
562 return (ENXIO);
563#endif
564 }
565}
566
567/* Explore device tree from the root. */
568usbd_status
569usb_discover(struct usb_softc *sc)
570{
571#if defined(__FreeBSD__)
572 /* The splxxx parts should be deleted when kthreads is available */
573 int s;
574#endif
575
576 /*
577 * We need mutual exclusion while traversing the device tree,
578 * but this is guaranteed since this function is only called
579 * from the event thread for the controller.
580 */
581#if defined(__FreeBSD__)
582 s = splusb();
583#endif
584 while (sc->sc_bus->needs_explore && !sc->sc_dying) {
585 sc->sc_bus->needs_explore = 0;
586#if defined(__FreeBSD__)
587 splx(s);
588#endif
589 sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
590#if defined(__FreeBSD__)
591 s = splusb();
592#endif
593 }
594#if defined(__FreeBSD__)
595 splx(s);
596#endif
597
598 return (USBD_NORMAL_COMPLETION);
599}
600
601void
602usb_needs_explore(usbd_bus_handle bus)
603{
604 bus->needs_explore = 1;
605#if defined(__FreeBSD__)
606 /* This part should be deleted when kthreads is available */
607 selwakeup(&bus->usbctl->sc_consel);
608#endif
609 wakeup(&bus->needs_explore);
610}
611
612/* Called at splusb() */
613int
614usb_get_next_event(struct usb_event *ue)
615{
616 struct usb_event_q *ueq;
617
618 if (usb_nevents <= 0)
619 return (0);
620 ueq = SIMPLEQ_FIRST(&usb_events);
621 *ue = ueq->ue;
622 SIMPLEQ_REMOVE_HEAD(&usb_events, ueq, next);
623 free(ueq, M_USBDEV);
624 usb_nevents--;
625 return (1);
626}
627
628void
629usbd_add_event(int type, usbd_device_handle dev)
630{
631 struct usb_event_q *ueq;
632 struct usb_event ue;
633 struct timeval thetime;
634 int s;
635
636 s = splusb();
637 if (++usb_nevents >= USB_MAX_EVENTS) {
638 /* Too many queued events, drop an old one. */
639 DPRINTF(("usb: event dropped\n"));
640 (void)usb_get_next_event(&ue);
641 }
642 /* Don't want to wait here inside splusb() */
643 ueq = malloc(sizeof *ueq, M_USBDEV, M_NOWAIT);
644 if (ueq == NULL) {
645 printf("usb: no memory, event dropped\n");
646 splx(s);
647 return;
648 }
649 ueq->ue.ue_type = type;
650 ueq->ue.u.ue_driver.ue_cookie = dev->cookie;
651 usbd_fill_deviceinfo(dev, &ueq->ue.u.ue_device);
652 microtime(&thetime);
653 TIMEVAL_TO_TIMESPEC(&thetime, &ueq->ue.ue_time);
654 SIMPLEQ_INSERT_TAIL(&usb_events, ueq, next);
655 wakeup(&usb_events);
656 selwakeup(&usb_selevent);
41c20dac
MD
657 if (usb_async_proc != NULL && usb_async_proc->td_proc)
658 psignal(usb_async_proc->td_proc, SIGIO);
984263bc
MD
659 splx(s);
660}
661
662#if defined(__NetBSD__) || defined(__OpenBSD__)
663int
664usb_activate(device_ptr_t self, devact act)
665{
666 struct usb_softc *sc = (struct usb_softc *)self;
667 usbd_device_handle dev = sc->sc_port.device;
668 int i, rv = 0;
669
670 switch (act) {
671 case DVACT_ACTIVATE:
672 return (EOPNOTSUPP);
673 break;
674
675 case DVACT_DEACTIVATE:
676 sc->sc_dying = 1;
677 if (dev && dev->cdesc && dev->subdevs) {
678 for (i = 0; dev->subdevs[i]; i++)
679 rv |= config_deactivate(dev->subdevs[i]);
680 }
681 break;
682 }
683 return (rv);
684}
685
686int
687usb_detach(device_ptr_t self, int flags)
688{
689 struct usb_softc *sc = (struct usb_softc *)self;
690
691 DPRINTF(("usb_detach: start\n"));
692
693 sc->sc_dying = 1;
694
695 /* Make all devices disconnect. */
696 if (sc->sc_port.device)
697 usb_disconnect_port(&sc->sc_port, self);
698
699 /* Kill off event thread. */
700 if (sc->sc_event_thread) {
701 wakeup(&sc->sc_bus->needs_explore);
702 if (tsleep(sc, PWAIT, "usbdet", hz * 60))
703 printf("%s: event thread didn't die\n",
704 USBDEVNAME(sc->sc_dev));
705 DPRINTF(("usb_detach: event thread dead\n"));
706 }
707
708 usbd_finish();
709 return (0);
710}
711#elif defined(__FreeBSD__)
712int
713usb_detach(device_t self)
714{
715 DPRINTF(("%s: unload, prevented\n", USBDEVNAME(self)));
716
717 return (EINVAL);
718}
719#endif
720
721
722#if defined(__FreeBSD__)
723DRIVER_MODULE(usb, ohci, usb_driver, usb_devclass, 0, 0);
724DRIVER_MODULE(usb, uhci, usb_driver, usb_devclass, 0, 0);
725#endif