Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's
write&pew->pew_work, instead. It is more defensive and persuasive.
Make miscellaneous changes in support of tearing down arbitrary
stacks of filesystems and devices during shutdown:
1 Move struct shutdown_state, shutdown_first(), and shutdown_next(),
from kern_pmf.c to subr_autoconf.c. Rename detach_all() to
config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c.
Export all of those routines.
2 In pmf_system_shutdown(), do not suspend user process scheduling, and
do not detach all devices: I am going to do that in cpu_reboot(),
instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still
call PMF shutdown hooks.
3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting
config_detach() at the bottom, alldevs_nwrite had better not be 0,
because config_detach() is a writer of the device list.
4 In deviter_release(), check to see if we're iterating the device list
for reading, *first*, and if so, decrease the number of readers. Used
to be that if we happened to be reading during shutdown, we ran the
shutdown branch. Thus the number of writers reached 0, the number
of readers remained > 0, and no writer could iterate again. Under
certain circumstances that would cause a hang during shutdown.
2009-06-26 23:30:45 +04:00
|
|
|
/* $NetBSD: kern_pmf.c,v 1.27 2009/06/26 19:30:45 dyoung Exp $ */
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's
write&pew->pew_work, instead. It is more defensive and persuasive.
Make miscellaneous changes in support of tearing down arbitrary
stacks of filesystems and devices during shutdown:
1 Move struct shutdown_state, shutdown_first(), and shutdown_next(),
from kern_pmf.c to subr_autoconf.c. Rename detach_all() to
config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c.
Export all of those routines.
2 In pmf_system_shutdown(), do not suspend user process scheduling, and
do not detach all devices: I am going to do that in cpu_reboot(),
instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still
call PMF shutdown hooks.
3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting
config_detach() at the bottom, alldevs_nwrite had better not be 0,
because config_detach() is a writer of the device list.
4 In deviter_release(), check to see if we're iterating the device list
for reading, *first*, and if so, decrease the number of readers. Used
to be that if we happened to be reading during shutdown, we ran the
shutdown branch. Thus the number of writers reached 0, the number
of readers remained > 0, and no writer could iterate again. Under
certain circumstances that would cause a hang during shutdown.
2009-06-26 23:30:45 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.27 2009/06/26 19:30:45 dyoung Exp $");
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/param.h>
|
Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's
write&pew->pew_work, instead. It is more defensive and persuasive.
Make miscellaneous changes in support of tearing down arbitrary
stacks of filesystems and devices during shutdown:
1 Move struct shutdown_state, shutdown_first(), and shutdown_next(),
from kern_pmf.c to subr_autoconf.c. Rename detach_all() to
config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c.
Export all of those routines.
2 In pmf_system_shutdown(), do not suspend user process scheduling, and
do not detach all devices: I am going to do that in cpu_reboot(),
instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still
call PMF shutdown hooks.
3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting
config_detach() at the bottom, alldevs_nwrite had better not be 0,
because config_detach() is a writer of the device list.
4 In deviter_release(), check to see if we're iterating the device list
for reading, *first*, and if so, decrease the number of readers. Used
to be that if we happened to be reading during shutdown, we ran the
shutdown branch. Thus the number of writers reached 0, the number
of readers remained > 0, and no writer could iterate again. Under
certain circumstances that would cause a hang during shutdown.
2009-06-26 23:30:45 +04:00
|
|
|
#include <sys/kmem.h>
|
2007-12-09 23:27:42 +03:00
|
|
|
#include <sys/buf.h>
|
|
|
|
#include <sys/callout.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/pmf.h>
|
|
|
|
#include <sys/queue.h>
|
2008-06-17 20:17:21 +04:00
|
|
|
#include <sys/sched.h>
|
2007-12-09 23:27:42 +03:00
|
|
|
#include <sys/syscallargs.h> /* for sys_sync */
|
|
|
|
#include <sys/workqueue.h>
|
|
|
|
#include <prop/proplib.h>
|
2008-03-05 10:09:18 +03:00
|
|
|
#include <sys/condvar.h>
|
|
|
|
#include <sys/mutex.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/reboot.h> /* for RB_NOSYNC */
|
|
|
|
#include <sys/sched.h>
|
2007-12-09 23:27:42 +03:00
|
|
|
|
2008-02-21 01:52:55 +03:00
|
|
|
/* XXX ugly special case, but for now the only client */
|
|
|
|
#include "wsdisplay.h"
|
|
|
|
#if NWSDISPLAY > 0
|
|
|
|
#include <dev/wscons/wsdisplayvar.h>
|
|
|
|
#endif
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
#ifdef PMF_DEBUG
|
|
|
|
int pmf_debug_event;
|
|
|
|
int pmf_debug_idle;
|
|
|
|
int pmf_debug_transition;
|
|
|
|
|
|
|
|
#define PMF_EVENT_PRINTF(x) if (pmf_debug_event) printf x
|
|
|
|
#define PMF_IDLE_PRINTF(x) if (pmf_debug_idle) printf x
|
|
|
|
#define PMF_TRANSITION_PRINTF(x) if (pmf_debug_transition) printf x
|
|
|
|
#define PMF_TRANSITION_PRINTF2(y,x) if (pmf_debug_transition>y) printf x
|
|
|
|
#else
|
|
|
|
#define PMF_EVENT_PRINTF(x) do { } while (0)
|
|
|
|
#define PMF_IDLE_PRINTF(x) do { } while (0)
|
|
|
|
#define PMF_TRANSITION_PRINTF(x) do { } while (0)
|
|
|
|
#define PMF_TRANSITION_PRINTF2(y,x) do { } while (0)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* #define PMF_DEBUG */
|
|
|
|
|
|
|
|
MALLOC_DEFINE(M_PMF, "pmf", "device pmf messaging memory");
|
|
|
|
|
|
|
|
static prop_dictionary_t pmf_platform = NULL;
|
|
|
|
static struct workqueue *pmf_event_workqueue;
|
|
|
|
|
|
|
|
typedef struct pmf_event_handler {
|
|
|
|
TAILQ_ENTRY(pmf_event_handler) pmf_link;
|
|
|
|
pmf_generic_event_t pmf_event;
|
|
|
|
void (*pmf_handler)(device_t);
|
|
|
|
device_t pmf_device;
|
|
|
|
bool pmf_global;
|
|
|
|
} pmf_event_handler_t;
|
|
|
|
|
|
|
|
static TAILQ_HEAD(, pmf_event_handler) pmf_all_events =
|
|
|
|
TAILQ_HEAD_INITIALIZER(pmf_all_events);
|
|
|
|
|
|
|
|
typedef struct pmf_event_workitem {
|
|
|
|
struct work pew_work;
|
|
|
|
pmf_generic_event_t pew_event;
|
|
|
|
device_t pew_device;
|
|
|
|
} pmf_event_workitem_t;
|
|
|
|
|
2008-03-05 10:09:18 +03:00
|
|
|
|
|
|
|
|
2008-03-07 10:03:06 +03:00
|
|
|
static bool pmf_device_resume_locked(device_t PMF_FN_PROTO);
|
|
|
|
static bool pmf_device_suspend_locked(device_t PMF_FN_PROTO);
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
static void
|
|
|
|
pmf_event_worker(struct work *wk, void *dummy)
|
|
|
|
{
|
|
|
|
pmf_event_workitem_t *pew;
|
|
|
|
pmf_event_handler_t *event;
|
|
|
|
|
|
|
|
pew = (void *)wk;
|
|
|
|
KASSERT(wk == &pew->pew_work);
|
|
|
|
KASSERT(pew != NULL);
|
|
|
|
|
|
|
|
TAILQ_FOREACH(event, &pmf_all_events, pmf_link) {
|
|
|
|
if (event->pmf_event != pew->pew_event)
|
|
|
|
continue;
|
2007-12-22 02:49:55 +03:00
|
|
|
if (event->pmf_device == pew->pew_device || event->pmf_global)
|
2007-12-09 23:27:42 +03:00
|
|
|
(*event->pmf_handler)(event->pmf_device);
|
|
|
|
}
|
|
|
|
|
Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's
write&pew->pew_work, instead. It is more defensive and persuasive.
Make miscellaneous changes in support of tearing down arbitrary
stacks of filesystems and devices during shutdown:
1 Move struct shutdown_state, shutdown_first(), and shutdown_next(),
from kern_pmf.c to subr_autoconf.c. Rename detach_all() to
config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c.
Export all of those routines.
2 In pmf_system_shutdown(), do not suspend user process scheduling, and
do not detach all devices: I am going to do that in cpu_reboot(),
instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still
call PMF shutdown hooks.
3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting
config_detach() at the bottom, alldevs_nwrite had better not be 0,
because config_detach() is a writer of the device list.
4 In deviter_release(), check to see if we're iterating the device list
for reading, *first*, and if so, decrease the number of readers. Used
to be that if we happened to be reading during shutdown, we ran the
shutdown branch. Thus the number of writers reached 0, the number
of readers remained > 0, and no writer could iterate again. Under
certain circumstances that would cause a hang during shutdown.
2009-06-26 23:30:45 +04:00
|
|
|
kmem_free(pew, sizeof(*pew));
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
pmf_check_system_drivers(void)
|
|
|
|
{
|
|
|
|
device_t curdev;
|
|
|
|
bool unsupported_devs;
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_t di;
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
unsupported_devs = false;
|
2008-03-05 10:09:18 +03:00
|
|
|
for (curdev = deviter_first(&di, 0); curdev != NULL;
|
|
|
|
curdev = deviter_next(&di)) {
|
2007-12-09 23:27:42 +03:00
|
|
|
if (device_pmf_is_registered(curdev))
|
|
|
|
continue;
|
|
|
|
if (!unsupported_devs)
|
|
|
|
printf("Devices without power management support:");
|
|
|
|
printf(" %s", device_xname(curdev));
|
|
|
|
unsupported_devs = true;
|
|
|
|
}
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_release(&di);
|
2007-12-09 23:27:42 +03:00
|
|
|
if (unsupported_devs) {
|
|
|
|
printf("\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-12-14 04:29:29 +03:00
|
|
|
bool
|
2008-03-05 10:09:18 +03:00
|
|
|
pmf_system_bus_resume(PMF_FN_ARGS1)
|
2007-12-14 04:29:29 +03:00
|
|
|
{
|
|
|
|
bool rv;
|
|
|
|
device_t curdev;
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_t di;
|
2007-12-14 04:29:29 +03:00
|
|
|
|
|
|
|
aprint_debug("Powering devices:");
|
|
|
|
/* D0 handlers are run in order */
|
|
|
|
rv = true;
|
2008-03-05 10:09:18 +03:00
|
|
|
for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); curdev != NULL;
|
|
|
|
curdev = deviter_next(&di)) {
|
|
|
|
if (!device_pmf_is_registered(curdev))
|
|
|
|
continue;
|
|
|
|
if (device_is_active(curdev) ||
|
|
|
|
!device_is_enabled(curdev))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
aprint_debug(" %s", device_xname(curdev));
|
|
|
|
|
|
|
|
if (!device_pmf_bus_resume(curdev PMF_FN_CALL)) {
|
|
|
|
rv = false;
|
|
|
|
aprint_debug("(failed)");
|
2007-12-14 04:29:29 +03:00
|
|
|
}
|
|
|
|
}
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_release(&di);
|
2007-12-14 04:29:29 +03:00
|
|
|
aprint_debug("\n");
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
bool
|
2008-03-05 10:09:18 +03:00
|
|
|
pmf_system_resume(PMF_FN_ARGS1)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
|
|
|
bool rv;
|
|
|
|
device_t curdev, parent;
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_t di;
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
if (!pmf_check_system_drivers())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
aprint_debug("Resuming devices:");
|
|
|
|
/* D0 handlers are run in order */
|
|
|
|
rv = true;
|
2008-03-05 10:09:18 +03:00
|
|
|
for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); curdev != NULL;
|
|
|
|
curdev = deviter_next(&di)) {
|
|
|
|
if (device_is_active(curdev) ||
|
|
|
|
!device_is_enabled(curdev))
|
|
|
|
continue;
|
|
|
|
parent = device_parent(curdev);
|
|
|
|
if (parent != NULL &&
|
|
|
|
!device_is_active(parent))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
aprint_debug(" %s", device_xname(curdev));
|
|
|
|
|
|
|
|
if (!pmf_device_resume(curdev PMF_FN_CALL)) {
|
|
|
|
rv = false;
|
|
|
|
aprint_debug("(failed)");
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
}
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_release(&di);
|
2007-12-09 23:27:42 +03:00
|
|
|
aprint_debug(".\n");
|
|
|
|
|
2008-02-21 01:52:55 +03:00
|
|
|
KERNEL_UNLOCK_ONE(0);
|
|
|
|
#if NWSDISPLAY > 0
|
|
|
|
if (rv)
|
|
|
|
wsdisplay_handlex(1);
|
|
|
|
#endif
|
2007-12-09 23:27:42 +03:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2008-03-05 10:09:18 +03:00
|
|
|
pmf_system_suspend(PMF_FN_ARGS1)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
|
|
|
device_t curdev;
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_t di;
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
if (!pmf_check_system_drivers())
|
|
|
|
return false;
|
2008-02-21 01:52:55 +03:00
|
|
|
#if NWSDISPLAY > 0
|
|
|
|
if (wsdisplay_handlex(0))
|
|
|
|
return false;
|
|
|
|
#endif
|
2009-04-16 11:47:16 +04:00
|
|
|
KERNEL_LOCK(1, NULL);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Flush buffers only if the shutdown didn't do so
|
|
|
|
* already and if there was no panic.
|
|
|
|
*/
|
|
|
|
if (doing_shutdown == 0 && panicstr == NULL) {
|
|
|
|
printf("Flushing disk caches: ");
|
|
|
|
sys_sync(NULL, NULL, NULL);
|
|
|
|
if (buf_syncwait() != 0)
|
|
|
|
printf("giving up\n");
|
|
|
|
else
|
|
|
|
printf("done\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
aprint_debug("Suspending devices:");
|
|
|
|
|
2008-03-05 10:09:18 +03:00
|
|
|
for (curdev = deviter_first(&di, DEVITER_F_LEAVES_FIRST);
|
|
|
|
curdev != NULL;
|
|
|
|
curdev = deviter_next(&di)) {
|
|
|
|
if (!device_is_active(curdev))
|
|
|
|
continue;
|
2007-12-09 23:27:42 +03:00
|
|
|
|
2008-03-05 10:09:18 +03:00
|
|
|
aprint_debug(" %s", device_xname(curdev));
|
2007-12-09 23:27:42 +03:00
|
|
|
|
2008-03-05 10:09:18 +03:00
|
|
|
/* XXX joerg check return value and abort suspend */
|
|
|
|
if (!pmf_device_suspend(curdev PMF_FN_CALL))
|
|
|
|
aprint_debug("(failed)");
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_release(&di);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
aprint_debug(".\n");
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-04-18 00:45:09 +04:00
|
|
|
static bool
|
|
|
|
shutdown_all(int how)
|
|
|
|
{
|
|
|
|
static struct shutdown_state s;
|
|
|
|
device_t curdev;
|
|
|
|
bool progress = false;
|
2007-12-09 23:27:42 +03:00
|
|
|
|
2008-03-05 10:09:18 +03:00
|
|
|
for (curdev = shutdown_first(&s); curdev != NULL;
|
|
|
|
curdev = shutdown_next(&s)) {
|
2009-04-18 00:45:09 +04:00
|
|
|
aprint_debug(" shutting down %s, ", device_xname(curdev));
|
|
|
|
if (!device_pmf_is_registered(curdev))
|
|
|
|
aprint_debug("skipped.");
|
2008-02-28 17:25:12 +03:00
|
|
|
#if 0 /* needed? */
|
2008-03-05 10:09:18 +03:00
|
|
|
else if (!device_pmf_class_shutdown(curdev, how))
|
2009-04-18 00:45:09 +04:00
|
|
|
aprint_debug("failed.");
|
2008-02-28 17:25:12 +03:00
|
|
|
#endif
|
2008-03-05 10:09:18 +03:00
|
|
|
else if (!device_pmf_driver_shutdown(curdev, how))
|
2009-04-18 00:45:09 +04:00
|
|
|
aprint_debug("failed.");
|
2008-03-05 10:09:18 +03:00
|
|
|
else if (!device_pmf_bus_shutdown(curdev, how))
|
2009-04-18 00:45:09 +04:00
|
|
|
aprint_debug("failed.");
|
|
|
|
else {
|
|
|
|
progress = true;
|
|
|
|
aprint_debug("success.");
|
|
|
|
}
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
2009-04-18 00:45:09 +04:00
|
|
|
return progress;
|
|
|
|
}
|
2007-12-09 23:27:42 +03:00
|
|
|
|
2009-04-18 00:45:09 +04:00
|
|
|
void
|
|
|
|
pmf_system_shutdown(int how)
|
|
|
|
{
|
|
|
|
aprint_debug("Shutting down devices:");
|
|
|
|
shutdown_all(how);
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
pmf_set_platform(const char *key, const char *value)
|
|
|
|
{
|
|
|
|
if (pmf_platform == NULL)
|
|
|
|
pmf_platform = prop_dictionary_create();
|
|
|
|
if (pmf_platform == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return prop_dictionary_set_cstring(pmf_platform, key, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
pmf_get_platform(const char *key)
|
|
|
|
{
|
|
|
|
const char *value;
|
|
|
|
|
|
|
|
if (pmf_platform == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!prop_dictionary_get_cstring_nocopy(pmf_platform, key, &value))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2008-02-28 17:25:12 +03:00
|
|
|
pmf_device_register1(device_t dev,
|
2008-03-05 07:54:24 +03:00
|
|
|
bool (*suspend)(device_t PMF_FN_PROTO),
|
|
|
|
bool (*resume)(device_t PMF_FN_PROTO),
|
2008-02-28 17:25:12 +03:00
|
|
|
bool (*shutdown)(device_t, int))
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
2008-03-05 07:54:24 +03:00
|
|
|
if (!device_pmf_driver_register(dev, suspend, resume, shutdown))
|
|
|
|
return false;
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
if (!device_pmf_driver_child_register(dev)) {
|
|
|
|
device_pmf_driver_deregister(dev);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pmf_device_deregister(device_t dev)
|
|
|
|
{
|
|
|
|
device_pmf_class_deregister(dev);
|
|
|
|
device_pmf_bus_deregister(dev);
|
|
|
|
device_pmf_driver_deregister(dev);
|
|
|
|
}
|
|
|
|
|
Use device_t and its accessors throughout. Use aprint_*_dev().
Improve PMF-ability.
Add a 'flags' argument to suspend/resume handlers and
callers such as pmf_system_suspend().
Define a flag, PMF_F_SELF, which indicates to PMF that a
device is suspending/resuming itself. Add helper routines,
pmf_device_suspend_self(dev) and pmf_device_resume_self(dev),
that call pmf_device_suspend(dev, PMF_F_SELF) and
pmf_device_resume(dev, PMF_F_SELF), respectively. Use
PMF_F_SELF to suspend/resume self in ath(4), audio(4),
rtw(4), and sip(4).
In ath(4) and in rtw(4), replace the icky sc_enable/sc_disable
callbacks, provided by the bus front-end, with
self-suspension/resumption. Also, clean up the bus
front-ends. Make sure that the interrupt handler is
disestablished during suspension. Get rid of driver-private
flags (e.g., RTW_F_ENABLED, ath_softc->sc_invalid); use
device_is_active()/device_has_power() calls, instead.
In the network-class suspend handler, call if_stop(, 0)
instead of if_stop(, 1), because the latter is superfluous
(bus- and driver-suspension hooks will 'disable' the NIC),
and it may cause recursion.
In the network-class resume handler, prevent infinite
recursion through if_init() by getting out early if we are
self-suspending (PMF_F_SELF).
rtw(4) improvements:
Destroy rtw(4) callouts when we detach it. Make rtw at
pci detachable. Print some more information with the "rx
frame too long" warning.
Remove activate() methods:
Get rid of rtw_activate() and ath_activate(). The device
activate() methods are not good for much these days.
Make ath at cardbus resume with crypto functions intact:
Introduce a boolean device property, "pmf-powerdown". If
pmf-powerdown is present and false, it indicates that a
bus back-end should not remove power from a device.
Honor this property in cardbus_child_suspend().
Set this property to 'false' in ath_attach(), since removing
power from an ath at cardbus seems to lobotomize the WPA
crypto engine. XXX Should the pmf-powerdown property
propagate toward the root of the device tree?
Miscellaneous ath(4) changes:
Warn if ath(4) tries to write crypto keys to suspended
hardware.
Reduce differences between FreeBSD and NetBSD in ath(4)
multicast filter setup.
Make ath_printrxbuf() print an rx descriptor's status &
key index, to help debug crypto errors.
Shorten a staircase in ath_ioctl(). Don't check for
ieee80211_ioctl() return code ERESTART, it never happens.
2008-03-12 21:02:21 +03:00
|
|
|
bool
|
|
|
|
pmf_device_suspend_self(device_t dev)
|
|
|
|
{
|
|
|
|
return pmf_device_suspend(dev, PMF_F_SELF);
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
bool
|
2008-03-05 10:09:18 +03:00
|
|
|
pmf_device_suspend(device_t dev PMF_FN_ARGS)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
2008-03-07 10:03:06 +03:00
|
|
|
bool rc;
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
PMF_TRANSITION_PRINTF(("%s: suspend enter\n", device_xname(dev)));
|
|
|
|
if (!device_pmf_is_registered(dev))
|
|
|
|
return false;
|
2008-03-07 10:03:06 +03:00
|
|
|
|
|
|
|
if (!device_pmf_lock(dev PMF_FN_CALL))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
rc = pmf_device_suspend_locked(dev PMF_FN_CALL);
|
|
|
|
|
|
|
|
device_pmf_unlock(dev PMF_FN_CALL);
|
|
|
|
|
|
|
|
PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev)));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
pmf_device_suspend_locked(device_t dev PMF_FN_ARGS)
|
|
|
|
{
|
Use device_t and its accessors throughout. Use aprint_*_dev().
Improve PMF-ability.
Add a 'flags' argument to suspend/resume handlers and
callers such as pmf_system_suspend().
Define a flag, PMF_F_SELF, which indicates to PMF that a
device is suspending/resuming itself. Add helper routines,
pmf_device_suspend_self(dev) and pmf_device_resume_self(dev),
that call pmf_device_suspend(dev, PMF_F_SELF) and
pmf_device_resume(dev, PMF_F_SELF), respectively. Use
PMF_F_SELF to suspend/resume self in ath(4), audio(4),
rtw(4), and sip(4).
In ath(4) and in rtw(4), replace the icky sc_enable/sc_disable
callbacks, provided by the bus front-end, with
self-suspension/resumption. Also, clean up the bus
front-ends. Make sure that the interrupt handler is
disestablished during suspension. Get rid of driver-private
flags (e.g., RTW_F_ENABLED, ath_softc->sc_invalid); use
device_is_active()/device_has_power() calls, instead.
In the network-class suspend handler, call if_stop(, 0)
instead of if_stop(, 1), because the latter is superfluous
(bus- and driver-suspension hooks will 'disable' the NIC),
and it may cause recursion.
In the network-class resume handler, prevent infinite
recursion through if_init() by getting out early if we are
self-suspending (PMF_F_SELF).
rtw(4) improvements:
Destroy rtw(4) callouts when we detach it. Make rtw at
pci detachable. Print some more information with the "rx
frame too long" warning.
Remove activate() methods:
Get rid of rtw_activate() and ath_activate(). The device
activate() methods are not good for much these days.
Make ath at cardbus resume with crypto functions intact:
Introduce a boolean device property, "pmf-powerdown". If
pmf-powerdown is present and false, it indicates that a
bus back-end should not remove power from a device.
Honor this property in cardbus_child_suspend().
Set this property to 'false' in ath_attach(), since removing
power from an ath at cardbus seems to lobotomize the WPA
crypto engine. XXX Should the pmf-powerdown property
propagate toward the root of the device tree?
Miscellaneous ath(4) changes:
Warn if ath(4) tries to write crypto keys to suspended
hardware.
Reduce differences between FreeBSD and NetBSD in ath(4)
multicast filter setup.
Make ath_printrxbuf() print an rx descriptor's status &
key index, to help debug crypto errors.
Shorten a staircase in ath_ioctl(). Don't check for
ieee80211_ioctl() return code ERESTART, it never happens.
2008-03-12 21:02:21 +03:00
|
|
|
PMF_TRANSITION_PRINTF2(1, ("%s: self suspend\n", device_xname(dev)));
|
|
|
|
device_pmf_self_suspend(dev, flags);
|
2007-12-09 23:27:42 +03:00
|
|
|
PMF_TRANSITION_PRINTF2(1, ("%s: class suspend\n", device_xname(dev)));
|
2008-03-05 10:09:18 +03:00
|
|
|
if (!device_pmf_class_suspend(dev PMF_FN_CALL))
|
2007-12-09 23:27:42 +03:00
|
|
|
return false;
|
|
|
|
PMF_TRANSITION_PRINTF2(1, ("%s: driver suspend\n", device_xname(dev)));
|
2008-03-05 10:09:18 +03:00
|
|
|
if (!device_pmf_driver_suspend(dev PMF_FN_CALL))
|
2007-12-09 23:27:42 +03:00
|
|
|
return false;
|
|
|
|
PMF_TRANSITION_PRINTF2(1, ("%s: bus suspend\n", device_xname(dev)));
|
2008-03-05 10:09:18 +03:00
|
|
|
if (!device_pmf_bus_suspend(dev PMF_FN_CALL))
|
2007-12-09 23:27:42 +03:00
|
|
|
return false;
|
2008-03-07 10:03:06 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Use device_t and its accessors throughout. Use aprint_*_dev().
Improve PMF-ability.
Add a 'flags' argument to suspend/resume handlers and
callers such as pmf_system_suspend().
Define a flag, PMF_F_SELF, which indicates to PMF that a
device is suspending/resuming itself. Add helper routines,
pmf_device_suspend_self(dev) and pmf_device_resume_self(dev),
that call pmf_device_suspend(dev, PMF_F_SELF) and
pmf_device_resume(dev, PMF_F_SELF), respectively. Use
PMF_F_SELF to suspend/resume self in ath(4), audio(4),
rtw(4), and sip(4).
In ath(4) and in rtw(4), replace the icky sc_enable/sc_disable
callbacks, provided by the bus front-end, with
self-suspension/resumption. Also, clean up the bus
front-ends. Make sure that the interrupt handler is
disestablished during suspension. Get rid of driver-private
flags (e.g., RTW_F_ENABLED, ath_softc->sc_invalid); use
device_is_active()/device_has_power() calls, instead.
In the network-class suspend handler, call if_stop(, 0)
instead of if_stop(, 1), because the latter is superfluous
(bus- and driver-suspension hooks will 'disable' the NIC),
and it may cause recursion.
In the network-class resume handler, prevent infinite
recursion through if_init() by getting out early if we are
self-suspending (PMF_F_SELF).
rtw(4) improvements:
Destroy rtw(4) callouts when we detach it. Make rtw at
pci detachable. Print some more information with the "rx
frame too long" warning.
Remove activate() methods:
Get rid of rtw_activate() and ath_activate(). The device
activate() methods are not good for much these days.
Make ath at cardbus resume with crypto functions intact:
Introduce a boolean device property, "pmf-powerdown". If
pmf-powerdown is present and false, it indicates that a
bus back-end should not remove power from a device.
Honor this property in cardbus_child_suspend().
Set this property to 'false' in ath_attach(), since removing
power from an ath at cardbus seems to lobotomize the WPA
crypto engine. XXX Should the pmf-powerdown property
propagate toward the root of the device tree?
Miscellaneous ath(4) changes:
Warn if ath(4) tries to write crypto keys to suspended
hardware.
Reduce differences between FreeBSD and NetBSD in ath(4)
multicast filter setup.
Make ath_printrxbuf() print an rx descriptor's status &
key index, to help debug crypto errors.
Shorten a staircase in ath_ioctl(). Don't check for
ieee80211_ioctl() return code ERESTART, it never happens.
2008-03-12 21:02:21 +03:00
|
|
|
bool
|
|
|
|
pmf_device_resume_self(device_t dev)
|
|
|
|
{
|
|
|
|
return pmf_device_resume(dev, PMF_F_SELF);
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
bool
|
2008-03-05 10:09:18 +03:00
|
|
|
pmf_device_resume(device_t dev PMF_FN_ARGS)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
2008-03-07 10:03:06 +03:00
|
|
|
bool rc;
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
PMF_TRANSITION_PRINTF(("%s: resume enter\n", device_xname(dev)));
|
|
|
|
if (!device_pmf_is_registered(dev))
|
|
|
|
return false;
|
2008-03-07 10:03:06 +03:00
|
|
|
|
|
|
|
if (!device_pmf_lock(dev PMF_FN_CALL))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
rc = pmf_device_resume_locked(dev PMF_FN_CALL);
|
|
|
|
|
|
|
|
device_pmf_unlock(dev PMF_FN_CALL);
|
|
|
|
|
|
|
|
PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev)));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
pmf_device_resume_locked(device_t dev PMF_FN_ARGS)
|
|
|
|
{
|
2007-12-09 23:27:42 +03:00
|
|
|
PMF_TRANSITION_PRINTF2(1, ("%s: bus resume\n", device_xname(dev)));
|
2008-03-05 10:09:18 +03:00
|
|
|
if (!device_pmf_bus_resume(dev PMF_FN_CALL))
|
2007-12-09 23:27:42 +03:00
|
|
|
return false;
|
|
|
|
PMF_TRANSITION_PRINTF2(1, ("%s: driver resume\n", device_xname(dev)));
|
2008-03-05 10:09:18 +03:00
|
|
|
if (!device_pmf_driver_resume(dev PMF_FN_CALL))
|
2007-12-09 23:27:42 +03:00
|
|
|
return false;
|
|
|
|
PMF_TRANSITION_PRINTF2(1, ("%s: class resume\n", device_xname(dev)));
|
2008-03-05 10:09:18 +03:00
|
|
|
if (!device_pmf_class_resume(dev PMF_FN_CALL))
|
2007-12-09 23:27:42 +03:00
|
|
|
return false;
|
Use device_t and its accessors throughout. Use aprint_*_dev().
Improve PMF-ability.
Add a 'flags' argument to suspend/resume handlers and
callers such as pmf_system_suspend().
Define a flag, PMF_F_SELF, which indicates to PMF that a
device is suspending/resuming itself. Add helper routines,
pmf_device_suspend_self(dev) and pmf_device_resume_self(dev),
that call pmf_device_suspend(dev, PMF_F_SELF) and
pmf_device_resume(dev, PMF_F_SELF), respectively. Use
PMF_F_SELF to suspend/resume self in ath(4), audio(4),
rtw(4), and sip(4).
In ath(4) and in rtw(4), replace the icky sc_enable/sc_disable
callbacks, provided by the bus front-end, with
self-suspension/resumption. Also, clean up the bus
front-ends. Make sure that the interrupt handler is
disestablished during suspension. Get rid of driver-private
flags (e.g., RTW_F_ENABLED, ath_softc->sc_invalid); use
device_is_active()/device_has_power() calls, instead.
In the network-class suspend handler, call if_stop(, 0)
instead of if_stop(, 1), because the latter is superfluous
(bus- and driver-suspension hooks will 'disable' the NIC),
and it may cause recursion.
In the network-class resume handler, prevent infinite
recursion through if_init() by getting out early if we are
self-suspending (PMF_F_SELF).
rtw(4) improvements:
Destroy rtw(4) callouts when we detach it. Make rtw at
pci detachable. Print some more information with the "rx
frame too long" warning.
Remove activate() methods:
Get rid of rtw_activate() and ath_activate(). The device
activate() methods are not good for much these days.
Make ath at cardbus resume with crypto functions intact:
Introduce a boolean device property, "pmf-powerdown". If
pmf-powerdown is present and false, it indicates that a
bus back-end should not remove power from a device.
Honor this property in cardbus_child_suspend().
Set this property to 'false' in ath_attach(), since removing
power from an ath at cardbus seems to lobotomize the WPA
crypto engine. XXX Should the pmf-powerdown property
propagate toward the root of the device tree?
Miscellaneous ath(4) changes:
Warn if ath(4) tries to write crypto keys to suspended
hardware.
Reduce differences between FreeBSD and NetBSD in ath(4)
multicast filter setup.
Make ath_printrxbuf() print an rx descriptor's status &
key index, to help debug crypto errors.
Shorten a staircase in ath_ioctl(). Don't check for
ieee80211_ioctl() return code ERESTART, it never happens.
2008-03-12 21:02:21 +03:00
|
|
|
PMF_TRANSITION_PRINTF2(1, ("%s: self resume\n", device_xname(dev)));
|
|
|
|
device_pmf_self_resume(dev, flags);
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2008-03-05 10:09:18 +03:00
|
|
|
pmf_device_recursive_suspend(device_t dv PMF_FN_ARGS)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
2008-03-05 10:09:18 +03:00
|
|
|
bool rv = true;
|
2007-12-09 23:27:42 +03:00
|
|
|
device_t curdev;
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_t di;
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
if (!device_is_active(dv))
|
|
|
|
return true;
|
|
|
|
|
2008-03-05 10:09:18 +03:00
|
|
|
for (curdev = deviter_first(&di, 0); curdev != NULL;
|
|
|
|
curdev = deviter_next(&di)) {
|
2007-12-09 23:27:42 +03:00
|
|
|
if (device_parent(curdev) != dv)
|
|
|
|
continue;
|
2008-03-05 10:09:18 +03:00
|
|
|
if (!pmf_device_recursive_suspend(curdev PMF_FN_CALL)) {
|
|
|
|
rv = false;
|
|
|
|
break;
|
|
|
|
}
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_release(&di);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
2008-03-05 10:09:18 +03:00
|
|
|
return rv && pmf_device_suspend(dv PMF_FN_CALL);
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2008-03-05 10:09:18 +03:00
|
|
|
pmf_device_recursive_resume(device_t dv PMF_FN_ARGS)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
|
|
|
device_t parent;
|
|
|
|
|
|
|
|
if (device_is_active(dv))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
parent = device_parent(dv);
|
|
|
|
if (parent != NULL) {
|
2008-03-05 10:09:18 +03:00
|
|
|
if (!pmf_device_recursive_resume(parent PMF_FN_CALL))
|
2007-12-09 23:27:42 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-03-05 10:09:18 +03:00
|
|
|
return pmf_device_resume(dv PMF_FN_CALL);
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2009-02-06 04:19:33 +03:00
|
|
|
pmf_device_resume_descendants(device_t dv PMF_FN_ARGS)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
2008-03-05 10:09:18 +03:00
|
|
|
bool rv = true;
|
2007-12-09 23:27:42 +03:00
|
|
|
device_t curdev;
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_t di;
|
2007-12-09 23:27:42 +03:00
|
|
|
|
2008-03-05 10:09:18 +03:00
|
|
|
for (curdev = deviter_first(&di, 0); curdev != NULL;
|
|
|
|
curdev = deviter_next(&di)) {
|
2007-12-09 23:27:42 +03:00
|
|
|
if (device_parent(curdev) != dv)
|
|
|
|
continue;
|
2008-03-05 10:09:18 +03:00
|
|
|
if (!pmf_device_resume_subtree(curdev PMF_FN_CALL)) {
|
|
|
|
rv = false;
|
|
|
|
break;
|
|
|
|
}
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
2008-03-05 10:09:18 +03:00
|
|
|
deviter_release(&di);
|
|
|
|
return rv;
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
|
2009-02-06 04:19:33 +03:00
|
|
|
bool
|
|
|
|
pmf_device_resume_subtree(device_t dv PMF_FN_ARGS)
|
|
|
|
{
|
|
|
|
if (!pmf_device_recursive_resume(dv PMF_FN_CALL))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return pmf_device_resume_descendants(dv PMF_FN_CALL);
|
|
|
|
}
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
#include <net/if.h>
|
|
|
|
|
|
|
|
static bool
|
2008-03-05 10:09:18 +03:00
|
|
|
pmf_class_network_suspend(device_t dev PMF_FN_ARGS)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = device_pmf_class_private(dev);
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splnet();
|
Use device_t and its accessors throughout. Use aprint_*_dev().
Improve PMF-ability.
Add a 'flags' argument to suspend/resume handlers and
callers such as pmf_system_suspend().
Define a flag, PMF_F_SELF, which indicates to PMF that a
device is suspending/resuming itself. Add helper routines,
pmf_device_suspend_self(dev) and pmf_device_resume_self(dev),
that call pmf_device_suspend(dev, PMF_F_SELF) and
pmf_device_resume(dev, PMF_F_SELF), respectively. Use
PMF_F_SELF to suspend/resume self in ath(4), audio(4),
rtw(4), and sip(4).
In ath(4) and in rtw(4), replace the icky sc_enable/sc_disable
callbacks, provided by the bus front-end, with
self-suspension/resumption. Also, clean up the bus
front-ends. Make sure that the interrupt handler is
disestablished during suspension. Get rid of driver-private
flags (e.g., RTW_F_ENABLED, ath_softc->sc_invalid); use
device_is_active()/device_has_power() calls, instead.
In the network-class suspend handler, call if_stop(, 0)
instead of if_stop(, 1), because the latter is superfluous
(bus- and driver-suspension hooks will 'disable' the NIC),
and it may cause recursion.
In the network-class resume handler, prevent infinite
recursion through if_init() by getting out early if we are
self-suspending (PMF_F_SELF).
rtw(4) improvements:
Destroy rtw(4) callouts when we detach it. Make rtw at
pci detachable. Print some more information with the "rx
frame too long" warning.
Remove activate() methods:
Get rid of rtw_activate() and ath_activate(). The device
activate() methods are not good for much these days.
Make ath at cardbus resume with crypto functions intact:
Introduce a boolean device property, "pmf-powerdown". If
pmf-powerdown is present and false, it indicates that a
bus back-end should not remove power from a device.
Honor this property in cardbus_child_suspend().
Set this property to 'false' in ath_attach(), since removing
power from an ath at cardbus seems to lobotomize the WPA
crypto engine. XXX Should the pmf-powerdown property
propagate toward the root of the device tree?
Miscellaneous ath(4) changes:
Warn if ath(4) tries to write crypto keys to suspended
hardware.
Reduce differences between FreeBSD and NetBSD in ath(4)
multicast filter setup.
Make ath_printrxbuf() print an rx descriptor's status &
key index, to help debug crypto errors.
Shorten a staircase in ath_ioctl(). Don't check for
ieee80211_ioctl() return code ERESTART, it never happens.
2008-03-12 21:02:21 +03:00
|
|
|
(*ifp->if_stop)(ifp, 0);
|
2007-12-09 23:27:42 +03:00
|
|
|
splx(s);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2008-03-05 07:54:24 +03:00
|
|
|
pmf_class_network_resume(device_t dev PMF_FN_ARGS)
|
2007-12-09 23:27:42 +03:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = device_pmf_class_private(dev);
|
|
|
|
int s;
|
|
|
|
|
Use device_t and its accessors throughout. Use aprint_*_dev().
Improve PMF-ability.
Add a 'flags' argument to suspend/resume handlers and
callers such as pmf_system_suspend().
Define a flag, PMF_F_SELF, which indicates to PMF that a
device is suspending/resuming itself. Add helper routines,
pmf_device_suspend_self(dev) and pmf_device_resume_self(dev),
that call pmf_device_suspend(dev, PMF_F_SELF) and
pmf_device_resume(dev, PMF_F_SELF), respectively. Use
PMF_F_SELF to suspend/resume self in ath(4), audio(4),
rtw(4), and sip(4).
In ath(4) and in rtw(4), replace the icky sc_enable/sc_disable
callbacks, provided by the bus front-end, with
self-suspension/resumption. Also, clean up the bus
front-ends. Make sure that the interrupt handler is
disestablished during suspension. Get rid of driver-private
flags (e.g., RTW_F_ENABLED, ath_softc->sc_invalid); use
device_is_active()/device_has_power() calls, instead.
In the network-class suspend handler, call if_stop(, 0)
instead of if_stop(, 1), because the latter is superfluous
(bus- and driver-suspension hooks will 'disable' the NIC),
and it may cause recursion.
In the network-class resume handler, prevent infinite
recursion through if_init() by getting out early if we are
self-suspending (PMF_F_SELF).
rtw(4) improvements:
Destroy rtw(4) callouts when we detach it. Make rtw at
pci detachable. Print some more information with the "rx
frame too long" warning.
Remove activate() methods:
Get rid of rtw_activate() and ath_activate(). The device
activate() methods are not good for much these days.
Make ath at cardbus resume with crypto functions intact:
Introduce a boolean device property, "pmf-powerdown". If
pmf-powerdown is present and false, it indicates that a
bus back-end should not remove power from a device.
Honor this property in cardbus_child_suspend().
Set this property to 'false' in ath_attach(), since removing
power from an ath at cardbus seems to lobotomize the WPA
crypto engine. XXX Should the pmf-powerdown property
propagate toward the root of the device tree?
Miscellaneous ath(4) changes:
Warn if ath(4) tries to write crypto keys to suspended
hardware.
Reduce differences between FreeBSD and NetBSD in ath(4)
multicast filter setup.
Make ath_printrxbuf() print an rx descriptor's status &
key index, to help debug crypto errors.
Shorten a staircase in ath_ioctl(). Don't check for
ieee80211_ioctl() return code ERESTART, it never happens.
2008-03-12 21:02:21 +03:00
|
|
|
if ((flags & PMF_F_SELF) != 0)
|
|
|
|
return true;
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
s = splnet();
|
|
|
|
if (ifp->if_flags & IFF_UP) {
|
|
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
2008-05-05 04:12:49 +04:00
|
|
|
if ((*ifp->if_init)(ifp) != 0)
|
|
|
|
aprint_normal_ifnet(ifp, "resume failed\n");
|
2007-12-09 23:27:42 +03:00
|
|
|
(*ifp->if_start)(ifp);
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pmf_class_network_register(device_t dev, struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
device_pmf_class_register(dev, ifp, pmf_class_network_suspend,
|
|
|
|
pmf_class_network_resume, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
pmf_event_inject(device_t dv, pmf_generic_event_t ev)
|
|
|
|
{
|
|
|
|
pmf_event_workitem_t *pew;
|
|
|
|
|
Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's
write&pew->pew_work, instead. It is more defensive and persuasive.
Make miscellaneous changes in support of tearing down arbitrary
stacks of filesystems and devices during shutdown:
1 Move struct shutdown_state, shutdown_first(), and shutdown_next(),
from kern_pmf.c to subr_autoconf.c. Rename detach_all() to
config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c.
Export all of those routines.
2 In pmf_system_shutdown(), do not suspend user process scheduling, and
do not detach all devices: I am going to do that in cpu_reboot(),
instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still
call PMF shutdown hooks.
3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting
config_detach() at the bottom, alldevs_nwrite had better not be 0,
because config_detach() is a writer of the device list.
4 In deviter_release(), check to see if we're iterating the device list
for reading, *first*, and if so, decrease the number of readers. Used
to be that if we happened to be reading during shutdown, we ran the
shutdown branch. Thus the number of writers reached 0, the number
of readers remained > 0, and no writer could iterate again. Under
certain circumstances that would cause a hang during shutdown.
2009-06-26 23:30:45 +04:00
|
|
|
pew = kmem_alloc(sizeof(pmf_event_workitem_t), KM_NOSLEEP);
|
2007-12-09 23:27:42 +03:00
|
|
|
if (pew == NULL) {
|
|
|
|
PMF_EVENT_PRINTF(("%s: PMF event %d dropped (no memory)\n",
|
|
|
|
dv ? device_xname(dv) : "<anonymous>", ev));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
pew->pew_event = ev;
|
|
|
|
pew->pew_device = dv;
|
|
|
|
|
Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's
write&pew->pew_work, instead. It is more defensive and persuasive.
Make miscellaneous changes in support of tearing down arbitrary
stacks of filesystems and devices during shutdown:
1 Move struct shutdown_state, shutdown_first(), and shutdown_next(),
from kern_pmf.c to subr_autoconf.c. Rename detach_all() to
config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c.
Export all of those routines.
2 In pmf_system_shutdown(), do not suspend user process scheduling, and
do not detach all devices: I am going to do that in cpu_reboot(),
instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still
call PMF shutdown hooks.
3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting
config_detach() at the bottom, alldevs_nwrite had better not be 0,
because config_detach() is a writer of the device list.
4 In deviter_release(), check to see if we're iterating the device list
for reading, *first*, and if so, decrease the number of readers. Used
to be that if we happened to be reading during shutdown, we ran the
shutdown branch. Thus the number of writers reached 0, the number
of readers remained > 0, and no writer could iterate again. Under
certain circumstances that would cause a hang during shutdown.
2009-06-26 23:30:45 +04:00
|
|
|
workqueue_enqueue(pmf_event_workqueue, &pew->pew_work, NULL);
|
2007-12-09 23:27:42 +03:00
|
|
|
PMF_EVENT_PRINTF(("%s: PMF event %d injected\n",
|
|
|
|
dv ? device_xname(dv) : "<anonymous>", ev));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
pmf_event_register(device_t dv, pmf_generic_event_t ev,
|
|
|
|
void (*handler)(device_t), bool global)
|
|
|
|
{
|
|
|
|
pmf_event_handler_t *event;
|
|
|
|
|
Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's
write&pew->pew_work, instead. It is more defensive and persuasive.
Make miscellaneous changes in support of tearing down arbitrary
stacks of filesystems and devices during shutdown:
1 Move struct shutdown_state, shutdown_first(), and shutdown_next(),
from kern_pmf.c to subr_autoconf.c. Rename detach_all() to
config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c.
Export all of those routines.
2 In pmf_system_shutdown(), do not suspend user process scheduling, and
do not detach all devices: I am going to do that in cpu_reboot(),
instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still
call PMF shutdown hooks.
3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting
config_detach() at the bottom, alldevs_nwrite had better not be 0,
because config_detach() is a writer of the device list.
4 In deviter_release(), check to see if we're iterating the device list
for reading, *first*, and if so, decrease the number of readers. Used
to be that if we happened to be reading during shutdown, we ran the
shutdown branch. Thus the number of writers reached 0, the number
of readers remained > 0, and no writer could iterate again. Under
certain circumstances that would cause a hang during shutdown.
2009-06-26 23:30:45 +04:00
|
|
|
event = kmem_alloc(sizeof(*event), KM_SLEEP);
|
2007-12-09 23:27:42 +03:00
|
|
|
event->pmf_event = ev;
|
|
|
|
event->pmf_handler = handler;
|
|
|
|
event->pmf_device = dv;
|
|
|
|
event->pmf_global = global;
|
|
|
|
TAILQ_INSERT_TAIL(&pmf_all_events, event, pmf_link);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pmf_event_deregister(device_t dv, pmf_generic_event_t ev,
|
|
|
|
void (*handler)(device_t), bool global)
|
|
|
|
{
|
|
|
|
pmf_event_handler_t *event;
|
|
|
|
|
2007-12-24 02:19:11 +03:00
|
|
|
TAILQ_FOREACH(event, &pmf_all_events, pmf_link) {
|
2007-12-09 23:27:42 +03:00
|
|
|
if (event->pmf_event != ev)
|
|
|
|
continue;
|
|
|
|
if (event->pmf_device != dv)
|
|
|
|
continue;
|
|
|
|
if (event->pmf_global != global)
|
|
|
|
continue;
|
|
|
|
if (event->pmf_handler != handler)
|
|
|
|
continue;
|
|
|
|
TAILQ_REMOVE(&pmf_all_events, event, pmf_link);
|
Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's
write&pew->pew_work, instead. It is more defensive and persuasive.
Make miscellaneous changes in support of tearing down arbitrary
stacks of filesystems and devices during shutdown:
1 Move struct shutdown_state, shutdown_first(), and shutdown_next(),
from kern_pmf.c to subr_autoconf.c. Rename detach_all() to
config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c.
Export all of those routines.
2 In pmf_system_shutdown(), do not suspend user process scheduling, and
do not detach all devices: I am going to do that in cpu_reboot(),
instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still
call PMF shutdown hooks.
3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting
config_detach() at the bottom, alldevs_nwrite had better not be 0,
because config_detach() is a writer of the device list.
4 In deviter_release(), check to see if we're iterating the device list
for reading, *first*, and if so, decrease the number of readers. Used
to be that if we happened to be reading during shutdown, we ran the
shutdown branch. Thus the number of writers reached 0, the number
of readers remained > 0, and no writer could iterate again. Under
certain circumstances that would cause a hang during shutdown.
2009-06-26 23:30:45 +04:00
|
|
|
kmem_free(event, sizeof(*event));
|
2007-12-24 02:19:11 +03:00
|
|
|
return;
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct display_class_softc {
|
|
|
|
TAILQ_ENTRY(display_class_softc) dc_link;
|
|
|
|
device_t dc_dev;
|
|
|
|
};
|
|
|
|
|
|
|
|
static TAILQ_HEAD(, display_class_softc) all_displays;
|
|
|
|
static callout_t global_idle_counter;
|
|
|
|
static int idle_timeout = 30;
|
|
|
|
|
|
|
|
static void
|
|
|
|
input_idle(void *dummy)
|
|
|
|
{
|
|
|
|
PMF_IDLE_PRINTF(("Input idle handler called\n"));
|
|
|
|
pmf_event_inject(NULL, PMFE_DISPLAY_OFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
input_activity_handler(device_t dv, devactive_t type)
|
|
|
|
{
|
|
|
|
if (!TAILQ_EMPTY(&all_displays))
|
|
|
|
callout_schedule(&global_idle_counter, idle_timeout * hz);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pmf_class_input_deregister(device_t dv)
|
|
|
|
{
|
|
|
|
device_active_deregister(dv, input_activity_handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
pmf_class_input_register(device_t dv)
|
|
|
|
{
|
|
|
|
if (!device_active_register(dv, input_activity_handler))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
device_pmf_class_register(dv, NULL, NULL, NULL,
|
|
|
|
pmf_class_input_deregister);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pmf_class_display_deregister(device_t dv)
|
|
|
|
{
|
|
|
|
struct display_class_softc *sc = device_pmf_class_private(dv);
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splsoftclock();
|
|
|
|
TAILQ_REMOVE(&all_displays, sc, dc_link);
|
|
|
|
if (TAILQ_EMPTY(&all_displays))
|
|
|
|
callout_stop(&global_idle_counter);
|
|
|
|
splx(s);
|
|
|
|
|
Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's
write&pew->pew_work, instead. It is more defensive and persuasive.
Make miscellaneous changes in support of tearing down arbitrary
stacks of filesystems and devices during shutdown:
1 Move struct shutdown_state, shutdown_first(), and shutdown_next(),
from kern_pmf.c to subr_autoconf.c. Rename detach_all() to
config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c.
Export all of those routines.
2 In pmf_system_shutdown(), do not suspend user process scheduling, and
do not detach all devices: I am going to do that in cpu_reboot(),
instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still
call PMF shutdown hooks.
3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting
config_detach() at the bottom, alldevs_nwrite had better not be 0,
because config_detach() is a writer of the device list.
4 In deviter_release(), check to see if we're iterating the device list
for reading, *first*, and if so, decrease the number of readers. Used
to be that if we happened to be reading during shutdown, we ran the
shutdown branch. Thus the number of writers reached 0, the number
of readers remained > 0, and no writer could iterate again. Under
certain circumstances that would cause a hang during shutdown.
2009-06-26 23:30:45 +04:00
|
|
|
kmem_free(sc, sizeof(*sc));
|
2007-12-09 23:27:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
pmf_class_display_register(device_t dv)
|
|
|
|
{
|
|
|
|
struct display_class_softc *sc;
|
|
|
|
int s;
|
|
|
|
|
Switch to kmem(9).
(void *)pew is one way to get a struct work *, but let's
write&pew->pew_work, instead. It is more defensive and persuasive.
Make miscellaneous changes in support of tearing down arbitrary
stacks of filesystems and devices during shutdown:
1 Move struct shutdown_state, shutdown_first(), and shutdown_next(),
from kern_pmf.c to subr_autoconf.c. Rename detach_all() to
config_detach_all(), and move it from kern_pmf.c to subr_autoconf.c.
Export all of those routines.
2 In pmf_system_shutdown(), do not suspend user process scheduling, and
do not detach all devices: I am going to do that in cpu_reboot(),
instead. (Soon I will do it in an MI cpu_reboot() routine.) Do still
call PMF shutdown hooks.
3 In config_detach(), add a DIAGNOSTIC assertion: if we're exiting
config_detach() at the bottom, alldevs_nwrite had better not be 0,
because config_detach() is a writer of the device list.
4 In deviter_release(), check to see if we're iterating the device list
for reading, *first*, and if so, decrease the number of readers. Used
to be that if we happened to be reading during shutdown, we ran the
shutdown branch. Thus the number of writers reached 0, the number
of readers remained > 0, and no writer could iterate again. Under
certain circumstances that would cause a hang during shutdown.
2009-06-26 23:30:45 +04:00
|
|
|
sc = kmem_alloc(sizeof(*sc), KM_SLEEP);
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
s = splsoftclock();
|
|
|
|
if (TAILQ_EMPTY(&all_displays))
|
|
|
|
callout_schedule(&global_idle_counter, idle_timeout * hz);
|
|
|
|
|
|
|
|
TAILQ_INSERT_HEAD(&all_displays, sc, dc_link);
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
device_pmf_class_register(dv, sc, NULL, NULL,
|
|
|
|
pmf_class_display_deregister);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pmf_init(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
KASSERT(pmf_event_workqueue == NULL);
|
|
|
|
err = workqueue_create(&pmf_event_workqueue, "pmfevent",
|
2007-12-11 04:20:06 +03:00
|
|
|
pmf_event_worker, NULL, PRI_NONE, IPL_VM, 0);
|
2007-12-09 23:27:42 +03:00
|
|
|
if (err)
|
|
|
|
panic("couldn't create pmfevent workqueue");
|
|
|
|
|
|
|
|
callout_init(&global_idle_counter, 0);
|
|
|
|
callout_setfunc(&global_idle_counter, input_idle, NULL);
|
|
|
|
}
|