diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/pc98/apm/apm.c | 98 |
1 files changed, 96 insertions, 2 deletions
diff --git a/sys/pc98/apm/apm.c b/sys/pc98/apm/apm.c index 0ad8df5..925d616 100644 --- a/sys/pc98/apm/apm.c +++ b/sys/pc98/apm/apm.c @@ -32,9 +32,11 @@ #include <sys/uio.h> #include <sys/signalvar.h> #include <sys/sysctl.h> +#include <sys/power.h> #include <machine/apm_bios.h> #include <machine/segments.h> #include <machine/clock.h> +#include <machine/stdarg.h> #include <vm/vm.h> #include <vm/vm_param.h> #include <vm/pmap.h> @@ -58,6 +60,8 @@ static void apm_resume __P((void)); static int apm_bioscall(void); static int apm_check_function_supported __P((u_int version, u_int func)); +static int apm_pm_func(u_long, void*, ...); + static u_long apm_version; int apm_evindex; @@ -802,6 +806,30 @@ apm_not_halt_cpu(void) /* device driver definitions */ /* + * Module event + */ + +static int +apm_modevent(struct module *mod, int event, void *junk) +{ + + switch (event) { + case MOD_LOAD: + if (!cold) + return (EPERM); + break; + case MOD_UNLOAD: + if (!cold && power_pm_get_type() == POWER_PM_TYPE_APM) + return (EBUSY); + break; + default: + break; + } + + return (0); +} + +/* * Create "connection point" */ static void @@ -809,6 +837,11 @@ apm_identify(driver_t *driver, device_t parent) { device_t child; + if (!cold) { + printf("Don't load this driver from userland!!\n"); + return; + } + child = BUS_ADD_CHILD(parent, 0, "apm", 0); if (child == NULL) panic("apm_identify"); @@ -840,6 +873,12 @@ apm_probe(device_t dev) return ENXIO; } + if (power_pm_get_type() != POWER_PM_TYPE_NONE && + power_pm_get_type() != POWER_PM_TYPE_APM) { + printf("apm: Other PM system enabled.\n"); + return ENXIO; + } + if (resource_int_value("apm", 0, "flags", &flags) != 0) flags = 0; @@ -1154,10 +1193,13 @@ apm_attach(device_t dev) EVENTHANDLER_REGISTER(shutdown_final, apm_power_off, NULL, SHUTDOWN_PRI_LAST); + /* Register APM again to pass the correct argument of pm_func. */ + power_pm_register(POWER_PM_TYPE_APM, apm_pm_func, sc); + sc->initialized = 1; sc->suspending = 0; - make_dev(&apm_cdevsw, 0, 0, 5, 0660, "apm"); + make_dev(&apm_cdevsw, 0, 0, 5, 0664, "apm"); make_dev(&apm_cdevsw, 8, 0, 5, 0660, "apmctl"); return 0; } @@ -1431,4 +1473,56 @@ static driver_t apm_driver = { static devclass_t apm_devclass; -DRIVER_MODULE(apm, nexus, apm_driver, apm_devclass, 0, 0); +DRIVER_MODULE(apm, nexus, apm_driver, apm_devclass, apm_modevent, 0); +MODULE_VERSION(apm, 1); + +static int +apm_pm_func(u_long cmd, void *arg, ...) +{ + int state, apm_state; + int error; + va_list ap; + + error = 0; + switch (cmd) { + case POWER_CMD_SUSPEND: + va_start(ap, arg); + state = va_arg(ap, int); + va_end(ap); + + switch (state) { + case POWER_SLEEP_STATE_STANDBY: + apm_state = PMST_STANDBY; + break; + case POWER_SLEEP_STATE_SUSPEND: + case POWER_SLEEP_STATE_HIBERNATE: + apm_state = PMST_SUSPEND; + break; + default: + error = EINVAL; + goto out; + } + + apm_suspend(apm_state); + break; + + default: + error = EINVAL; + goto out; + } + +out: + return (error); +} + +static void +apm_pm_register(void *arg) +{ + int disabled = 0; + + resource_int_value("apm", 0, "disabled", &disabled); + if (disabled == 0) + power_pm_register(POWER_PM_TYPE_APM, apm_pm_func, NULL); +} + +SYSINIT(power, SI_SUB_KLD, SI_ORDER_ANY, apm_pm_register, 0); |