summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/usb.c')
-rw-r--r--sys/dev/usb/usb.c449
1 files changed, 331 insertions, 118 deletions
diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c
index f774835..5005a4d 100644
--- a/sys/dev/usb/usb.c
+++ b/sys/dev/usb/usb.c
@@ -1,4 +1,4 @@
-/* $NetBSD: usb.c,v 1.19 1999/09/05 19:32:19 augustss Exp $ */
+/* $NetBSD: usb.c,v 1.28 1999/10/13 08:10:57 augustss Exp $ */
/* $FreeBSD$ */
/*
@@ -55,17 +55,21 @@
#elif defined(__FreeBSD__)
#include <sys/module.h>
#include <sys/bus.h>
-#include <sys/ioccom.h>
+#include <sys/filio.h>
#include <sys/uio.h>
-#include <sys/conf.h>
#endif
+#include <sys/conf.h>
#include <sys/poll.h>
#include <sys/select.h>
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
+#define USB_DEV_MINOR 255
+
#if defined(__FreeBSD__)
MALLOC_DEFINE(M_USB, "USB", "USB");
MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device");
@@ -74,6 +78,8 @@ MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller");
#include "usb_if.h"
#endif /* defined(__FreeBSD__) */
+#include <machine/bus.h>
+
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_quirks.h>
@@ -81,35 +87,39 @@ MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller");
#define DPRINTF(x) if (usbdebug) logprintf x
#define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x
int usbdebug = 0;
-extern int uhcidebug;
-extern int ohcidebug;
+#ifdef UHCI_DEBUG
+extern int uhcidebug;
+#endif
+#ifdef OHCI_DEBUG
+extern int ohcidebug;
+#endif
+int usb_noexplore = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
-#define USBUNIT(dev) (minor(dev))
-
struct usb_softc {
- USBBASEDEVICE sc_dev; /* base device */
+ USBBASEDEVICE sc_dev; /* base device */
usbd_bus_handle sc_bus; /* USB controller */
struct usbd_port sc_port; /* dummy port for root hub */
- char sc_running;
- char sc_exploring;
- struct selinfo sc_consel; /* waiting for connect change */
- int shutdown;
- struct proc *event_thread;
+
+#if defined(__FreeBSD__)
+ /* This part should be deleted when kthreads is available */
+ struct selinfo sc_consel; /* waiting for connect change */
+#else
+ struct proc *sc_event_thread;
+#endif
+
+ char sc_dying;
};
#if defined(__NetBSD__) || defined(__OpenBSD__)
-int usbopen __P((dev_t, int, int, struct proc *));
-int usbclose __P((dev_t, int, int, struct proc *));
-int usbioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
-int usbpoll __P((dev_t, int, struct proc *));
-
+cdev_decl(usb);
#elif defined(__FreeBSD__)
d_open_t usbopen;
d_close_t usbclose;
+d_read_t usbread;
d_ioctl_t usbioctl;
int usbpoll __P((dev_t, int, struct proc *));
@@ -131,9 +141,28 @@ struct cdevsw usb_cdevsw = {
};
#endif
-usbd_status usb_discover __P((struct usb_softc *));
-void usb_create_event_thread __P((void *));
-void usb_event_thread __P((void *));
+static usbd_status usb_discover __P((struct usb_softc *));
+static void usb_create_event_thread __P((void *));
+static void usb_event_thread __P((void *));
+
+#define USB_MAX_EVENTS 50
+struct usb_event_q {
+ struct usb_event ue;
+ SIMPLEQ_ENTRY(usb_event_q) next;
+};
+static SIMPLEQ_HEAD(, usb_event_q) usb_events =
+ SIMPLEQ_HEAD_INITIALIZER(usb_events);
+static int usb_nevents = 0;
+static struct selinfo usb_selevent;
+static struct proc *usb_async_proc; /* process who wants USB SIGIO */
+static int usb_dev_open = 0;
+
+static int usb_get_next_event __P((struct usb_event *));
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+/* Flag to see if we are in the cold boot process. */
+extern int cold;
+#endif
USB_DECLARE_DRIVER(usb);
@@ -152,7 +181,7 @@ USB_ATTACH(usb)
void *aux = device_get_ivars(self);
#endif
usbd_device_handle dev;
- usbd_status r;
+ usbd_status err;
#if defined(__NetBSD__) || defined(__OpenBSD__)
printf("\n");
@@ -161,38 +190,45 @@ USB_ATTACH(usb)
#endif
DPRINTF(("usbd_attach\n"));
-
+ usbd_init();
sc->sc_bus = aux;
sc->sc_bus->usbctl = sc;
- sc->sc_running = 1;
- sc->sc_bus->use_polling = 1;
sc->sc_port.power = USB_MAX_POWER;
- r = usbd_new_device(USBDEV(sc->sc_dev), sc->sc_bus, 0,0,0, &sc->sc_port);
+ err = usbd_new_device(USBDEV(sc->sc_dev), sc->sc_bus, 0, 0, 0,
+ &sc->sc_port);
- if (r == USBD_NORMAL_COMPLETION) {
+ if (!err) {
dev = sc->sc_port.device;
- if (!dev->hub) {
- sc->sc_running = 0;
+ if (dev->hub == NULL) {
+ sc->sc_dying = 1;
printf("%s: root device is not a hub\n",
USBDEVNAME(sc->sc_dev));
USB_ATTACH_ERROR_RETURN;
}
sc->sc_bus->root_hub = dev;
- dev->hub->explore(sc->sc_bus->root_hub);
+#if 1
+ /*
+ * Turning this code off will delay attachment of USB devices
+ * until the USB event thread is running, which means that
+ * the keyboard will not work until after cold boot.
+ */
+ if (cold) {
+ sc->sc_bus->use_polling++;
+ dev->hub->explore(sc->sc_bus->root_hub);
+ sc->sc_bus->use_polling--;
+ }
+#endif
} else {
printf("%s: root hub problem, error=%d\n",
- USBDEVNAME(sc->sc_dev), r);
- sc->sc_running = 0;
+ USBDEVNAME(sc->sc_dev), err);
+ sc->sc_dying = 1;
}
- sc->sc_bus->use_polling = 0;
-#if defined(__NetBSD__) || defined(__OpenBSD__)
kthread_create(usb_create_event_thread, sc);
-#endif
#if defined(__FreeBSD__)
make_dev(&usb_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR,
- 0644, "usb%d", device_get_unit(self));
+ 0644, "usb%d", device_get_unit(self));
#endif
USB_ATTACH_SUCCESS_RETURN;
@@ -205,7 +241,7 @@ usb_create_event_thread(arg)
{
struct usb_softc *sc = arg;
- if (kthread_create1(usb_event_thread, sc, &sc->event_thread,
+ if (kthread_create1(usb_event_thread, sc, &sc->sc_event_thread,
"%s", sc->sc_dev.dv_xname)) {
printf("%s: unable to create event thread for\n",
sc->sc_dev.dv_xname);
@@ -219,17 +255,23 @@ usb_event_thread(arg)
{
struct usb_softc *sc = arg;
- while (!sc->shutdown) {
+ DPRINTF(("usb_event_thread: start\n"));
+
+ while (!sc->sc_dying) {
+#ifdef USB_DEBUG
+ if (!usb_noexplore)
+#endif
+ usb_discover(sc);
(void)tsleep(&sc->sc_bus->needs_explore,
- PWAIT, "usbevt", hz*30);
+ PWAIT, "usbevt", hz*60);
DPRINTFN(2,("usb_event_thread: woke up\n"));
- usb_discover(sc);
}
- sc->event_thread = 0;
+ sc->sc_event_thread = 0;
/* In case parent is waiting for us to exit. */
wakeup(sc);
+ DPRINTF(("usb_event_thread: exit\n"));
kthread_exit(0);
}
@@ -244,7 +286,7 @@ usbctlprint(aux, pnp)
return (UNCONF);
}
-#endif /* NetBSD && OpenBSD */
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) */
int
usbopen(dev, flag, mode, p)
@@ -252,20 +294,74 @@ usbopen(dev, flag, mode, p)
int flag, mode;
struct proc *p;
{
- USB_GET_SC_OPEN(usb, USBUNIT(dev), sc);
+ int unit = minor(dev);
+ struct usb_softc *sc;
- if (sc == 0 || !sc->sc_running)
- return (ENXIO);
+ if (unit == USB_DEV_MINOR) {
+ if (usb_dev_open)
+ return (EBUSY);
+ usb_dev_open = 1;
+ usb_async_proc = 0;
+ return (0);
+ }
+
+ USB_GET_SC_OPEN(usb, unit, sc);
+
+ if (sc->sc_dying)
+ return (EIO);
return (0);
}
int
+usbread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ struct usb_event ue;
+ int s, error, n;
+
+ if (minor(dev) != USB_DEV_MINOR)
+ return (ENXIO);
+
+ if (uio->uio_resid != sizeof(struct usb_event))
+ return (EINVAL);
+
+ error = 0;
+ s = splusb();
+ for (;;) {
+ n = usb_get_next_event(&ue);
+ if (n != 0)
+ break;
+ if (flag & IO_NDELAY) {
+ error = EWOULDBLOCK;
+ break;
+ }
+ error = tsleep(&usb_events, PZERO | PCATCH, "usbrea", 0);
+ if (error)
+ break;
+ }
+ splx(s);
+ if (!error)
+ error = uiomove((void *)&ue, uio->uio_resid, uio);
+
+ return (error);
+}
+
+int
usbclose(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
+ int unit = minor(dev);
+
+ if (unit == USB_DEV_MINOR) {
+ usb_async_proc = 0;
+ usb_dev_open = 0;
+ }
+
return (0);
}
@@ -277,19 +373,48 @@ usbioctl(devt, cmd, data, flag, p)
int flag;
struct proc *p;
{
- USB_GET_SC(usb, USBUNIT(devt), sc);
+ struct usb_softc *sc;
+ int unit = minor(devt);
+
+ if (unit == USB_DEV_MINOR) {
+ switch (cmd) {
+ case FIONBIO:
+ /* All handled in the upper FS layer. */
+ return (0);
+
+ case FIOASYNC:
+ if (*(int *)data)
+ usb_async_proc = p;
+ else
+ usb_async_proc = 0;
+ return (0);
+
+ default:
+ return (EINVAL);
+ }
+ }
+
+ USB_GET_SC(usb, unit, sc);
+
+ if (sc->sc_dying)
+ return (EIO);
- if (sc == 0 || !sc->sc_running)
- return (ENXIO);
switch (cmd) {
+#if defined(__FreeBSD__)
+ /* This part should be deleted when kthreads is available */
+ case USB_DISCOVER:
+ usb_discover(sc);
+ break;
+#endif
#ifdef USB_DEBUG
case USB_SETDEBUG:
- usbdebug = uhcidebug = ohcidebug = *(int *)data;
- break;
+ usbdebug = ((*(int *)data) & 0x000000ff);
+#ifdef UHCI_DEBUG
+ uhcidebug = ((*(int *)data) & 0x0000ff00) >> 8;
+#endif
+#ifdef OHCI_DEBUG
+ ohcidebug = ((*(int *)data) & 0x00ff0000) >> 16;
#endif
-#if defined(__FreeBSD__)
- case USB_DISCOVER:
- usb_discover(sc);
break;
#endif
case USB_REQUEST:
@@ -300,7 +425,7 @@ usbioctl(devt, cmd, data, flag, p)
struct uio uio;
void *ptr = 0;
int addr = ur->addr;
- usbd_status r;
+ usbd_status err;
int error = 0;
DPRINTF(("usbioctl: USB_REQUEST addr=%d len=%d\n", addr, len));
@@ -328,10 +453,9 @@ usbioctl(devt, cmd, data, flag, p)
goto ret;
}
}
- r = usbd_do_request_flags(sc->sc_bus->devices[addr],
- &ur->request, ptr,
- ur->flags, &ur->actlen);
- if (r != USBD_NORMAL_COMPLETION) {
+ err = usbd_do_request_flags(sc->sc_bus->devices[addr],
+ &ur->request, ptr, ur->flags, &ur->actlen);
+ if (err) {
error = EIO;
goto ret;
}
@@ -368,7 +492,7 @@ usbioctl(devt, cmd, data, flag, p)
break;
default:
- return (ENXIO);
+ return (EINVAL);
}
return (0);
}
@@ -379,79 +503,77 @@ usbpoll(dev, events, p)
int events;
struct proc *p;
{
- int revents, s;
- USB_GET_SC(usb, USBUNIT(dev), sc);
+ int revents, mask, s;
- DPRINTFN(15, ("usbpoll: sc=%p events=0x%x\n", sc, events));
- s = splusb();
- revents = 0;
- if (events & (POLLOUT | POLLWRNORM))
- if (sc->sc_bus->needs_explore)
- revents |= events & (POLLOUT | POLLWRNORM);
- DPRINTFN(15, ("usbpoll: revents=0x%x\n", revents));
- if (revents == 0) {
- if (events & (POLLOUT | POLLWRNORM)) {
- DPRINTFN(2, ("usbpoll: selrecord\n"));
- selrecord(p, &sc->sc_consel);
- }
- }
- splx(s);
- return (revents);
-}
+ if (minor(dev) == USB_DEV_MINOR) {
+ revents = 0;
+ mask = POLLIN | POLLRDNORM;
-#if 0
-int
-usb_bus_count()
-{
- int i, n;
+ s = splusb();
+ if (events & mask && usb_nevents > 0)
+ revents |= events & mask;
+ if (revents == 0 && events & mask)
+ selrecord(p, &usb_selevent);
+ splx(s);
- for (i = n = 0; i < usb_cd.cd_ndevs; i++)
- if (usb_cd.cd_devs[i])
- n++;
- return (n);
-}
-#endif
+ return (revents);
+ } else {
+#if defined(__FreeBSD__)
+ /* This part should be deleted when kthreads is available */
+ struct usb_softc *sc;
+ int unit = minor(dev);
-#if 0
-usbd_status
-usb_get_bus_handle(n, h)
- int n;
- usbd_bus_handle *h;
-{
- int i;
+ USB_GET_SC(usb, unit, sc);
- for (i = 0; i < usb_cd.cd_ndevs; i++)
- if (usb_cd.cd_devs[i] && n-- == 0) {
- *h = usb_cd.cd_devs[i];
- return (USBD_NORMAL_COMPLETION);
- }
- return (USBD_INVAL);
-}
+ revents = 0;
+ mask = POLLOUT | POLLRDNORM;
+
+ s = splusb();
+ if (events & mask && sc->sc_bus->needs_explore)
+ revents |= events & mask;
+ if (revents == 0 && events & mask)
+ selrecord(p, &sc->sc_consel);
+ splx(s);
+
+ return (revents);
+#else
+ return (ENXIO);
#endif
+ }
+}
+/* Explore device tree from the root. */
usbd_status
usb_discover(sc)
struct usb_softc *sc;
{
+#if defined(__FreeBSD__)
+ /* The splxxx parts should be deleted when kthreads is available */
int s;
+#endif
- /* Explore device tree from the root */
- /* We need mutual exclusion while traversing the device tree. */
- do {
- s = splusb();
- while (sc->sc_exploring)
- tsleep(&sc->sc_exploring, PRIBIO, "usbdis", 0);
- sc->sc_exploring = 1;
+ /*
+ * We need mutual exclusion while traversing the device tree,
+ * but this is guaranteed since this function is only called
+ * from the event thread for the controller.
+ */
+#if defined(__FreeBSD__)
+ s = splusb();
+#endif
+ while (sc->sc_bus->needs_explore && !sc->sc_dying) {
sc->sc_bus->needs_explore = 0;
+#if defined(__FreeBSD__)
splx(s);
-
+#endif
sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
-
+#if defined(__FreeBSD__)
s = splusb();
- sc->sc_exploring = 0;
- wakeup(&sc->sc_exploring);
- splx(s);
- } while (sc->sc_bus->needs_explore);
+#endif
+ }
+#if defined(__FreeBSD__)
+ splx(s);
+#endif
+
return (USBD_NORMAL_COMPLETION);
}
@@ -460,18 +582,90 @@ usb_needs_explore(bus)
usbd_bus_handle bus;
{
bus->needs_explore = 1;
+#if defined(__FreeBSD__)
+ /* This part should be deleted when kthreads is available */
selwakeup(&bus->usbctl->sc_consel);
+#endif
wakeup(&bus->needs_explore);
}
+/* Called at splusb() */
+int
+usb_get_next_event(ue)
+ struct usb_event *ue;
+{
+ struct usb_event_q *ueq;
+
+ if (usb_nevents <= 0)
+ return (0);
+ ueq = SIMPLEQ_FIRST(&usb_events);
+ *ue = ueq->ue;
+ SIMPLEQ_REMOVE_HEAD(&usb_events, ueq, next);
+ free(ueq, M_USBDEV);
+ usb_nevents--;
+ return (1);
+}
+
+void
+usbd_add_event(type, dev)
+ int type;
+ usbd_device_handle dev;
+{
+ struct usb_event_q *ueq;
+ struct usb_event ue;
+ struct timeval thetime;
+ int s;
+
+ s = splusb();
+ if (++usb_nevents >= USB_MAX_EVENTS) {
+ /* Too many queued events, drop an old one. */
+ DPRINTFN(-1,("usb: event dropped\n"));
+ (void)usb_get_next_event(&ue);
+ }
+ /* Don't want to wait here inside splusb() */
+ ueq = malloc(sizeof *ueq, M_USBDEV, M_NOWAIT);
+ if (ueq == NULL) {
+ printf("usb: no memory, event dropped\n");
+ splx(s);
+ return;
+ }
+ ueq->ue.ue_type = type;
+ ueq->ue.ue_cookie = dev->cookie;
+ usbd_fill_deviceinfo(dev, &ueq->ue.ue_device);
+ microtime(&thetime);
+ TIMEVAL_TO_TIMESPEC(&thetime, &ueq->ue.ue_time);
+ SIMPLEQ_INSERT_TAIL(&usb_events, ueq, next);
+ wakeup(&usb_events);
+ selwakeup(&usb_selevent);
+ if (usb_async_proc != NULL)
+ psignal(usb_async_proc, SIGIO);
+ splx(s);
+}
+
#if defined(__NetBSD__) || defined(__OpenBSD__)
int
usb_activate(self, act)
device_ptr_t self;
enum devact act;
{
- panic("usb_activate\n");
- return (0);
+ struct usb_softc *sc = (struct usb_softc *)self;
+ usbd_device_handle dev = sc->sc_port.device;
+ int i, rv = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ break;
+
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ if (dev && dev->cdesc && dev->subdevs) {
+ for (i = 0; dev->subdevs[i]; i++)
+ rv |= config_deactivate(dev->subdevs[i]);
+ }
+ break;
+ }
+ return (rv);
}
int
@@ -479,7 +673,26 @@ usb_detach(self, flags)
device_ptr_t self;
int flags;
{
- panic("usb_detach\n");
+ struct usb_softc *sc = (struct usb_softc *)self;
+
+ DPRINTF(("usb_detach: start\n"));
+
+ sc->sc_dying = 1;
+
+ /* Make all devices disconnect. */
+ if (sc->sc_port.device)
+ usb_disconnect_port(&sc->sc_port, self);
+
+ /* Kill off event thread. */
+ if (sc->sc_event_thread) {
+ wakeup(&sc->sc_bus->needs_explore);
+ if (tsleep(sc, PWAIT, "usbdet", hz * 60))
+ printf("%s: event thread didn't die\n",
+ USBDEVNAME(sc->sc_dev));
+ DPRINTF(("usb_detach: event thread dead\n"));
+ }
+
+ usbd_finish();
return (0);
}
#elif defined(__FreeBSD__)
OpenPOWER on IntegriCloud