Make the USB subsystem unloadable and detachable, though currently

a significant amount of memory may be leaked each time a host
controller is detached.
This commit is contained in:
Ian Dowse 2004-08-02 15:37:35 +00:00
parent 5ba0615c03
commit f50033ff2e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=133018
11 changed files with 160 additions and 78 deletions

View file

@ -168,10 +168,6 @@ struct ehci_pipe {
} u;
};
#if defined(__NetBSD__) || defined(__OpenBSD__)
Static void ehci_power(int, void *);
#endif
Static usbd_status ehci_open(usbd_pipe_handle);
Static void ehci_poll(struct usbd_bus *);
Static void ehci_softintr(void *);
@ -929,32 +925,38 @@ ehci_poll(struct usbd_bus *bus)
ehci_intr1(sc);
}
#if defined(__NetBSD__) || defined(__OpenBSD__)
int
ehci_detach(struct ehci_softc *sc, int flags)
{
int rv = 0;
#if defined(__NetBSD__) || defined(__OpenBSD__)
if (sc->sc_child != NULL)
rv = config_detach(sc->sc_child, flags);
if (rv != 0)
return (rv);
#endif
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
EOWRITE4(sc, EHCI_USBCMD, 0);
EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
usb_uncallout(sc->sc_tmo_pcd, ehci_pcd_enable, sc);
#if defined(__NetBSD__) || defined(__OpenBSD__)
if (sc->sc_powerhook != NULL)
powerhook_disestablish(sc->sc_powerhook);
if (sc->sc_shutdownhook != NULL)
shutdownhook_disestablish(sc->sc_shutdownhook);
#endif
usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */
usb_freemem(&sc->sc_bus, &sc->sc_fldma);
/* XXX free other data structures XXX */
return (rv);
}
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__)
int
@ -981,10 +983,9 @@ ehci_activate(device_ptr_t self, enum devact act)
* Handle suspend/resume.
*
* We need to switch to polling mode here, because this routine is
* called from an intterupt context. This is all right since we
* called from an interrupt context. This is all right since we
* are almost suspended anyway.
*/
#if defined(__NetBSD__) || defined(__OpenBSD__)
void
ehci_power(int why, void *v)
{
@ -1000,7 +1001,9 @@ ehci_power(int why, void *v)
s = splhardusb();
switch (why) {
case PWR_SUSPEND:
#if defined(__NetBSD__) || defined(__OpenBSD__)
case PWR_STANDBY:
#endif
sc->sc_bus.use_polling++;
#if 0
OOO
@ -1044,14 +1047,15 @@ OOO
#endif
sc->sc_bus.use_polling--;
break;
#if defined(__NetBSD__) || defined(__OpenBSD__)
case PWR_SOFTSUSPEND:
case PWR_SOFTSTANDBY:
case PWR_SOFTRESUME:
break;
#endif
}
splx(s);
}
#endif
/*
* Shut down the controller when the system is going down.

View file

@ -102,9 +102,37 @@ static const char *ehci_device_generic = "EHCI (generic) USB 2.0 controller";
static int ehci_pci_attach(device_t self);
static int ehci_pci_detach(device_t self);
static int ehci_pci_shutdown(device_t self);
static int ehci_pci_suspend(device_t self);
static int ehci_pci_resume(device_t self);
static void ehci_pci_givecontroller(device_t self);
static void ehci_pci_takecontroller(device_t self);
static int
ehci_pci_suspend(device_t self)
{
ehci_softc_t *sc = device_get_softc(self);
int err;
err = bus_generic_suspend(self);
if (err)
return (err);
ehci_power(PWR_SUSPEND, sc);
return 0;
}
static int
ehci_pci_resume(device_t self)
{
ehci_softc_t *sc = device_get_softc(self);
ehci_pci_takecontroller(self);
ehci_power(PWR_RESUME, sc);
bus_generic_resume(self);
return 0;
}
static int
ehci_pci_shutdown(device_t self)
{
@ -296,14 +324,14 @@ ehci_pci_attach(device_t self)
ehci_pci_takecontroller(self);
err = ehci_init(sc);
if (!err)
if (!err) {
sc->sc_flags |= EHCI_SCFLG_DONEINIT;
err = device_probe_and_attach(sc->sc_bus.bdev);
}
if (err) {
device_printf(self, "USB init failed err=%d\n", err);
#if 0 /* TODO */
ehci_pci_detach(self);
#endif
return EIO;
}
return 0;
@ -314,10 +342,10 @@ ehci_pci_detach(device_t self)
{
ehci_softc_t *sc = device_get_softc(self);
/*
* XXX this code is not yet fit to be used as detach for the EHCI
* controller
*/
if (sc->sc_flags & EHCI_SCFLG_DONEINIT) {
ehci_detach(sc, 0);
sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
}
/*
* disable interrupts that might have been switched on in ehci_init
@ -406,6 +434,9 @@ static device_method_t ehci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ehci_pci_probe),
DEVMETHOD(device_attach, ehci_pci_attach),
DEVMETHOD(device_detach, ehci_pci_detach),
DEVMETHOD(device_suspend, ehci_pci_suspend),
DEVMETHOD(device_resume, ehci_pci_resume),
DEVMETHOD(device_shutdown, ehci_pci_shutdown),
/* Bus interface */

View file

@ -87,8 +87,11 @@ struct ehci_soft_islot {
#define EHCI_HASH_SIZE 128
#define EHCI_COMPANION_MAX 8
#define EHCI_SCFLG_DONEINIT 0x0001 /* ehci_init() has been called. */
typedef struct ehci_softc {
struct usbd_bus sc_bus; /* base device */
int sc_flags;
bus_space_tag_t iot;
bus_space_handle_t ioh;
bus_size_t sc_size;
@ -161,10 +164,11 @@ typedef struct ehci_softc {
usbd_status ehci_init(ehci_softc_t *);
int ehci_intr(void *);
#if defined(__NetBSD__) || defined(__OpenBSD__)
int ehci_detach(ehci_softc_t *, int);
#if defined(__NetBSD__) || defined(__OpenBSD__)
int ehci_activate(device_ptr_t, enum devact);
#endif
void ehci_power(int state, void *priv);
void ehci_shutdown(void *v);
#define MS_TO_TICKS(ms) ((ms) * hz / 1000)

View file

@ -382,17 +382,20 @@ ohci_activate(device_ptr_t self, enum devact act)
}
return (rv);
}
#endif
int
ohci_detach(struct ohci_softc *sc, int flags)
{
int rv = 0;
int i, rv = 0;
#if defined(__NetBSD__) || defined(__OpenBSD__)
if (sc->sc_child != NULL)
rv = config_detach(sc->sc_child, flags);
if (rv != 0)
return (rv);
#endif
usb_uncallout(sc->sc_tmo_rhsc, ohci_rhsc_enable, sc);
@ -401,13 +404,20 @@ ohci_detach(struct ohci_softc *sc, int flags)
shutdownhook_disestablish(sc->sc_shutdownhook);
#endif
OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */
/* free data structures XXX */
for (i = 0; i < OHCI_NO_EDS; i++)
ohci_free_sed(sc, sc->sc_eds[i]);
ohci_free_sed(sc, sc->sc_isoc_head);
ohci_free_sed(sc, sc->sc_bulk_head);
ohci_free_sed(sc, sc->sc_ctrl_head);
usb_freemem(&sc->sc_bus, &sc->sc_hccadma);
return (rv);
}
#endif
ohci_soft_ed_t *
ohci_alloc_sed(ohci_softc_t *sc)

View file

@ -290,8 +290,10 @@ ohci_pci_attach(device_t self)
return ENXIO;
}
err = ohci_init(sc);
if (!err)
if (!err) {
sc->sc_flags |= OHCI_SCFLG_DONEINIT;
err = device_probe_and_attach(sc->sc_bus.bdev);
}
if (err) {
device_printf(self, "USB init failed\n");
@ -306,17 +308,10 @@ ohci_pci_detach(device_t self)
{
ohci_softc_t *sc = device_get_softc(self);
/*
* XXX this code is not yet fit to be used as detach for the OHCI
* controller
*/
/*
* disable interrupts that might have been switched on in ohci_init
*/
if (sc->iot && sc->ioh)
bus_space_write_4(sc->iot, sc->ioh,
OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
if (sc->sc_flags & OHCI_SCFLG_DONEINIT) {
ohci_detach(sc, 0);
sc->sc_flags &= ~OHCI_SCFLG_DONEINIT;
}
if (sc->irq_res && sc->ih) {
int err = bus_teardown_intr(self, sc->irq_res, sc->ih);
@ -348,6 +343,7 @@ static device_method_t ohci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ohci_pci_probe),
DEVMETHOD(device_attach, ohci_pci_attach),
DEVMETHOD(device_detach, ohci_pci_detach),
DEVMETHOD(device_suspend, ohci_pci_suspend),
DEVMETHOD(device_resume, ohci_pci_resume),
DEVMETHOD(device_shutdown, bus_generic_shutdown),

View file

@ -83,8 +83,11 @@ typedef struct ohci_soft_itd {
#define OHCI_HASH_SIZE 128
#define OHCI_SCFLG_DONEINIT 0x0001 /* ohci_init() done. */
typedef struct ohci_softc {
struct usbd_bus sc_bus; /* base device */
int sc_flags;
bus_space_tag_t iot;
bus_space_handle_t ioh;
bus_size_t sc_size;
@ -159,8 +162,8 @@ struct ohci_xfer {
usbd_status ohci_init(ohci_softc_t *);
int ohci_intr(void *);
int ohci_detach(ohci_softc_t *, int);
#if defined(__NetBSD__) || defined(__OpenBSD__)
int ohci_detach(ohci_softc_t *, int);
int ohci_activate(device_ptr_t, enum devact);
#endif

View file

@ -572,6 +572,7 @@ uhci_activate(device_ptr_t self, enum devact act)
}
return (rv);
}
#endif
int
uhci_detach(struct uhci_softc *sc, int flags)
@ -579,11 +580,16 @@ uhci_detach(struct uhci_softc *sc, int flags)
usbd_xfer_handle xfer;
int rv = 0;
#if defined(__NetBSD__) || defined(__OpenBSD__)
if (sc->sc_child != NULL)
rv = config_detach(sc->sc_child, flags);
if (rv != 0)
return (rv);
#endif
UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */
uhci_run(sc, 0);
#if defined(__NetBSD__) || defined(__OpenBSD__)
powerhook_disestablish(sc->sc_powerhook);
@ -600,10 +606,10 @@ uhci_detach(struct uhci_softc *sc, int flags)
}
/* XXX free other data structures XXX */
usb_freemem(&sc->sc_bus, &sc->sc_dma);
return (rv);
}
#endif
usbd_status
uhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size)
@ -638,6 +644,8 @@ uhci_allocx(struct usbd_bus *bus)
if (xfer != NULL) {
memset(xfer, 0, sizeof (struct uhci_xfer));
UXFER(xfer)->iinfo.sc = sc;
usb_init_task(&UXFER(xfer)->abort_task, uhci_timeout_task,
xfer);
#ifdef DIAGNOSTIC
UXFER(xfer)->iinfo.isdone = 1;
xfer->busy_free = XFER_BUSY;
@ -733,6 +741,9 @@ uhci_power(int why, void *v)
if (cmd & UHCI_CMD_RS)
uhci_run(sc, 0); /* in case BIOS has started it */
uhci_globalreset(sc);
uhci_reset(sc);
/* restore saved state */
UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0));
UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum);
@ -1507,7 +1518,6 @@ uhci_timeout(void *addr)
}
/* Execute the abort in a process context. */
usb_init_task(&uxfer->abort_task, uhci_timeout_task, ii->xfer);
usb_add_task(uxfer->xfer.pipe->device, &uxfer->abort_task);
}
@ -1935,7 +1945,7 @@ uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
s = splusb();
xfer->status = status; /* make software ignore it */
usb_uncallout(xfer->timeout_handle, uhci_timeout, xfer);
usb_transfer_complete(xfer);
usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task);
splx(s);
return;
}
@ -1949,6 +1959,7 @@ uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
s = splusb();
xfer->status = status; /* make software ignore it */
usb_uncallout(xfer->timeout_handle, uhci_timeout, ii);
usb_rem_task(xfer->pipe->device, &UXFER(xfer)->abort_task);
DPRINTFN(1,("uhci_abort_xfer: stop ii=%p\n", ii));
for (std = ii->stdstart; std != NULL; std = std->link.std)
std->td.td_status &= htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC));

View file

@ -329,8 +329,10 @@ uhci_pci_attach(device_t self)
pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2);
err = uhci_init(sc);
if (!err)
if (!err) {
sc->sc_flags |= UHCI_SCFLG_DONEINIT;
err = device_probe_and_attach(sc->sc_bus.bdev);
}
if (err) {
device_printf(self, "USB init failed\n");
@ -345,22 +347,11 @@ uhci_pci_detach(device_t self)
{
uhci_softc_t *sc = device_get_softc(self);
/*
* XXX This function is not yet complete and should not be added
* method list.
*/
#if 0
if uhci_init
was successful
we should call something like uhci_deinit
#endif
if (sc->sc_flags & UHCI_SCFLG_DONEINIT) {
uhci_detach(sc, 0);
sc->sc_flags &= ~UHCI_SCFLG_DONEINIT;
}
/*
* disable interrupts that might have been switched on in
* uhci_init.
*/
if (sc->iot && sc->ioh)
bus_space_write_2(sc->iot, sc->ioh, UHCI_INTR, 0);
if (sc->irq_res && sc->ih) {
int err = bus_teardown_intr(self, sc->irq_res, sc->ih);
@ -394,6 +385,7 @@ static device_method_t uhci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, uhci_pci_probe),
DEVMETHOD(device_attach, uhci_pci_attach),
DEVMETHOD(device_detach, uhci_pci_detach),
DEVMETHOD(device_suspend, uhci_pci_suspend),
DEVMETHOD(device_resume, uhci_pci_resume),
DEVMETHOD(device_shutdown, bus_generic_shutdown),

View file

@ -131,8 +131,11 @@ struct uhci_vframe {
u_int bandwidth; /* max bandwidth used by this frame */
};
#define UHCI_SCFLG_DONEINIT 0x0001 /* uhci_init() done */
typedef struct uhci_softc {
struct usbd_bus sc_bus; /* base device */
int sc_flags;
bus_space_tag_t iot;
bus_space_handle_t ioh;
bus_size_t sc_size;
@ -195,8 +198,8 @@ typedef struct uhci_softc {
usbd_status uhci_init(uhci_softc_t *);
int uhci_intr(void *);
#if defined(__NetBSD__) || defined(__OpenBSD__)
int uhci_detach(uhci_softc_t *, int);
#if defined(__NetBSD__) || defined(__OpenBSD__)
int uhci_activate(device_ptr_t, enum devact);
#endif

View file

@ -124,12 +124,13 @@ USB_DECLARE_DRIVER_INIT(uhub,
devclass_t uhubroot_devclass;
Static device_method_t uhubroot_methods[] = {
DEVMETHOD(bus_child_detached, uhub_child_detached),
DEVMETHOD(device_probe, uhub_match),
DEVMETHOD(device_attach, uhub_attach),
DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_str),
DEVMETHOD(bus_child_location_str, uhub_child_location_str),
/* detach is not allowed for a root hub */
DEVMETHOD(device_detach, uhub_detach),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
DEVMETHOD(device_shutdown, bus_generic_shutdown),

View file

@ -127,6 +127,9 @@ int usb_noexplore = 0;
struct usb_softc {
USBBASEDEVICE sc_dev; /* base device */
#ifdef __FreeBSD__
struct cdev *sc_usbdev; /* /dev/usbN device */
#endif
usbd_bus_handle sc_bus; /* USB controller */
struct usbd_port sc_port; /* dummy port for root hub */
@ -162,11 +165,20 @@ struct cdevsw usb_cdevsw = {
#endif
Static void usb_discover(void *);
#ifdef __FreeBSD__
Static bus_child_detached_t usb_child_detached;
#endif
Static void usb_create_event_thread(void *);
Static void usb_event_thread(void *);
Static void usb_task_thread(void *);
Static struct proc *usb_task_thread_proc = NULL;
#ifdef __FreeBSD__
Static struct cdev *usb_dev; /* The /dev/usb device. */
Static int usb_ndevs; /* Number of /dev/usbN devices. */
Static int usb_taskcreated; /* USB task thread exists. */
#endif
#define USB_MAX_EVENTS 100
struct usb_event_q {
struct usb_event ue;
@ -185,6 +197,7 @@ Static int usb_get_next_event(struct usb_event *);
Static const char *usbrev_str[] = USBREV_STR;
USB_DECLARE_DRIVER_INIT(usb,
DEVMETHOD(bus_child_detached, usb_child_detached),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
DEVMETHOD(device_shutdown, bus_generic_shutdown)
@ -207,7 +220,6 @@ USB_ATTACH(usb)
#elif defined(__FreeBSD__)
struct usb_softc *sc = device_get_softc(self);
void *aux = device_get_ivars(self);
static int global_init_done = 0;
#endif
usbd_device_handle dev;
usbd_status err;
@ -307,13 +319,12 @@ USB_ATTACH(usb)
usb_create_event_thread(sc);
/* The per controller devices (used for usb_discover) */
/* XXX This is redundant now, but old usbd's will want it */
make_dev(&usb_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR,
0660, "usb%d", device_get_unit(self));
if (!global_init_done) {
sc->sc_usbdev = make_dev(&usb_cdevsw, device_get_unit(self), UID_ROOT,
GID_OPERATOR, 0660, "usb%d", device_get_unit(self));
if (usb_ndevs++ == 0) {
/* The device spitting out events */
make_dev(&usb_cdevsw, USB_DEV_MINOR, UID_ROOT, GID_OPERATOR,
0660, "usb");
global_init_done = 1;
usb_dev = make_dev(&usb_cdevsw, USB_DEV_MINOR, UID_ROOT,
GID_OPERATOR, 0660, "usb");
}
#endif
@ -324,7 +335,6 @@ void
usb_create_event_thread(void *arg)
{
struct usb_softc *sc = arg;
static int created = 0;
if (usb_kthread_create1(usb_event_thread, sc, &sc->sc_event_thread,
"%s", USBDEVNAME(sc->sc_dev))) {
@ -332,8 +342,8 @@ usb_create_event_thread(void *arg)
USBDEVNAME(sc->sc_dev));
panic("usb_create_event_thread");
}
if (!created) {
created = 1;
if (usb_taskcreated == 0) {
usb_taskcreated = 1;
TAILQ_INIT(&usb_all_tasks);
if (usb_kthread_create2(usb_task_thread, NULL,
&usb_task_thread_proc, "usbtask")) {
@ -440,7 +450,7 @@ usb_task_thread(void *arg)
DPRINTF(("usb_task_thread: start\n"));
s = splusb();
for (;;) {
while (usb_ndevs > 0) {
task = TAILQ_FIRST(&usb_all_tasks);
if (task == NULL) {
tsleep(&usb_all_tasks, PWAIT, "usbtsk", 0);
@ -455,6 +465,13 @@ usb_task_thread(void *arg)
s = splusb();
}
}
splx(s);
usb_taskcreated = 0;
wakeup(&usb_taskcreated);
DPRINTF(("usb_event_thread: exit\n"));
kthread_exit(0);
}
#if defined(__NetBSD__) || defined(__OpenBSD__)
@ -865,11 +882,11 @@ usb_activate(device_ptr_t self, enum devact act)
}
return (rv);
}
#endif
int
usb_detach(device_ptr_t self, int flags)
USB_DETACH(usb)
{
struct usb_softc *sc = (struct usb_softc *)self;
USB_DETACH_START(usb, sc);
struct usb_event ue;
DPRINTF(("usb_detach: start\n"));
@ -889,6 +906,17 @@ usb_detach(device_ptr_t self, int flags)
DPRINTF(("usb_detach: event thread dead\n"));
}
#ifdef __FreeBSD__
destroy_dev(sc->sc_usbdev);
if (--usb_ndevs == 0) {
destroy_dev(usb_dev);
usb_dev = NULL;
wakeup(&usb_all_tasks);
if (tsleep(&usb_taskcreated, PWAIT, "usbtdt", hz * 60))
printf("usb task thread didn't die\n");
}
#endif
usbd_finish();
#ifdef USB_USE_SOFTINTR
@ -907,18 +935,17 @@ usb_detach(device_ptr_t self, int flags)
return (0);
}
#elif defined(__FreeBSD__)
int
usb_detach(device_t self)
{
DPRINTF(("%s: unload, prevented\n", USBDEVNAME(self)));
return (EINVAL);
}
#endif
#if defined(__FreeBSD__)
Static void
usb_child_detached(device_t self, device_t child)
{
struct usb_softc *sc = device_get_softc(self);
/* XXX, should check it is the right device. */
sc->sc_port.device = NULL;
}
DRIVER_MODULE(usb, ohci, usb_driver, usb_devclass, 0, 0);
DRIVER_MODULE(usb, uhci, usb_driver, usb_devclass, 0, 0);
DRIVER_MODULE(usb, ehci, usb_driver, usb_devclass, 0, 0);