void apm_set_ver(struct apm_softc *self) { struct apmregs regs; int rv = 0; bzero(®s, sizeof(regs)); regs.cx = APM_VERSION; if (APM_MAJOR(apm_flags) == 1 && APM_MINOR(apm_flags) == 2 && (rv = apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, ®s)) == 0) { apm_majver = APM_CONN_MAJOR(®s); apm_minver = APM_CONN_MINOR(®s); } else { #ifdef APMDEBUG if (rv) apm_perror("set version 1.2", ®s); #endif /* try downgrading to 1.1 */ bzero(®s, sizeof(regs)); regs.cx = 0x0101; if (apmcall(APM_DRIVER_VERSION, APM_DEV_APM_BIOS, ®s) == 0) { apm_majver = 1; apm_minver = 1; } else { #ifdef APMDEBUG apm_perror("set version 1.1", ®s); #endif /* stay w/ flags then */ apm_majver = APM_MAJOR(apm_flags); apm_minver = APM_MINOR(apm_flags); /* fix version for some endianess-challenged compaqs */ if (!apm_majver) { apm_majver = 1; apm_minver = 0; } } } printf(": Power Management spec V%d.%d", apm_majver, apm_minver); #ifdef DIAGNOSTIC if (apm_flags & APM_IDLE_SLOWS) printf(" (slowidle)"); if (apm_flags & APM_BIOS_PM_DISABLED) printf(" (BIOS management disabled)"); if (apm_flags & APM_BIOS_PM_DISENGAGED) printf(" (BIOS managing devices)"); #endif printf("\n"); }
void apm_disconnect(struct apm_softc *sc) { struct apmregs regs; bzero(®s, sizeof(regs)); if (apmcall(APM_SYSTEM_DEFAULTS, (apm_minver == 1 ? APM_DEV_ALLDEVS : APM_DEFAULTS_ALL), ®s)) apm_perror("system defaults failed", ®s); if (apmcall(APM_DISCONNECT, APM_DEV_APM_BIOS, ®s)) apm_perror("disconnect failed", ®s); else printf("%s: disconnected\n", sc->sc_dev.dv_xname); apm_flags |= APM_BIOS_PM_DISABLED; }
void apm_cpu_idle(void) { struct apmregs regs; static u_int64_t call_apm_idle = 0; /* * We call the bios APM_IDLE routine here only when we * have been idle for some time - otherwise we just hlt. */ if (call_apm_idle != curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]) { /* Always call BIOS halt/idle stuff */ bzero(®s, sizeof(regs)); if (apmcall(APM_CPU_IDLE, 0, ®s) != 0) { #ifdef DIAGNOSTIC apm_perror("set CPU idle", ®s); #endif } /* If BIOS did not halt, halt now! */ if (apm_flags & APM_IDLE_SLOWS) { __asm __volatile("sti;hlt"); } call_apm_idle = curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]; } else { __asm __volatile("sti;hlt"); } }
static int __inline apm_get_event(struct apmregs *r) { int rv; bzero(r, sizeof(*r)); rv = apmcall(APM_GET_PM_EVENT, 0, r); return rv; }
void apm_powmgt_enable(int onoff) { struct apmregs regs; bzero(®s, sizeof(regs)); regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE; if (apmcall(APM_PWR_MGT_ENABLE, (apm_minver? APM_DEV_APM_BIOS : APM_MGT_ALL), ®s) != 0) apm_perror("power management enable", ®s); }
void apm_powmgt_engage(int onoff, u_int dev) { struct apmregs regs; if (apm_minver == 0) return; bzero(®s, sizeof(regs)); regs.cx = onoff ? APM_MGT_ENGAGE : APM_MGT_DISENGAGE; if (apmcall(APM_PWR_MGT_ENGAGE, dev, ®s) != 0) printf("apm0: APM engage (device %x): %s (%d)\n", dev, apm_err_translate(APM_ERR_CODE(®s)), APM_ERR_CODE(®s)); }
void apm_devpowmgt_enable(int onoff, u_int dev) { struct apmregs regs; if (apm_minver == 0) return; /* enable is auto BIOS management. * disable is program control. */ bzero(®s, sizeof(regs)); regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE; if (apmcall(APM_DEVICE_MGMT_ENABLE, dev, ®s) != 0) printf("APM device engage (device %x): %s (%d)\n", dev, apm_err_translate(APM_ERR_CODE(®s)), APM_ERR_CODE(®s)); }
void apm_cpu_busy(void) { struct apmregs regs; if (!apm_slow_called) return; if (apm_flags & APM_IDLE_SLOWS) { bzero(®s, sizeof(regs)); if (apmcall(APM_CPU_BUSY, 0, ®s) != 0) { #ifdef DIAGNOSTIC apm_perror("set CPU busy", ®s); #endif } apm_slow_called = 0; } }
void apm_cpu_slow(void) { struct apmregs regs; static u_int64_t call_apm_slow = 0; if (call_apm_slow != curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]) { /* Always call BIOS halt/idle stuff */ bzero(®s, sizeof(regs)); if (apmcall(APM_CPU_IDLE, 0, ®s) != 0) { #ifdef DIAGNOSTIC apm_perror("set CPU slow", ®s); #endif } apm_slow_called = 1; call_apm_slow = curcpu()->ci_schedstate.spc_cp_time[CP_IDLE]; } }
int apm_set_powstate(u_int dev, u_int state) { struct apmregs regs; if (!apm_cd.cd_ndevs || (apm_minver == 0 && state > APM_SYS_OFF)) return EINVAL; bzero(®s, sizeof(regs)); regs.cx = state; if (apmcall(APM_SET_PWR_STATE, dev, ®s) != 0) { apm_perror("set power state", ®s); if (APM_ERR_CODE(®s) == APM_ERR_UNRECOG_DEV) return ENXIO; else return EIO; } return 0; }
int apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct apm_softc *sc; struct apmregs regs; int error = 0; /* apm0 only */ if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 || !(sc = apm_cd.cd_devs[APMUNIT(dev)])) return ENXIO; rw_enter_write(&sc->sc_lock); switch (cmd) { /* some ioctl names from linux */ case APM_IOC_STANDBY: if ((flag & FWRITE) == 0) error = EBADF; else apm_userstandbys++; break; case APM_IOC_SUSPEND: if ((flag & FWRITE) == 0) error = EBADF; else apm_suspends++; break; case APM_IOC_PRN_CTL: if ((flag & FWRITE) == 0) error = EBADF; else { int flag = *(int *)data; DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag )); switch (flag) { case APM_PRINT_ON: /* enable printing */ sc->sc_flags &= ~SCFLAG_PRINT; break; case APM_PRINT_OFF: /* disable printing */ sc->sc_flags &= ~SCFLAG_PRINT; sc->sc_flags |= SCFLAG_NOPRINT; break; case APM_PRINT_PCT: /* disable some printing */ sc->sc_flags &= ~SCFLAG_PRINT; sc->sc_flags |= SCFLAG_PCTPRINT; break; default: error = EINVAL; break; } } break; case APM_IOC_DEV_CTL: if ((flag & FWRITE) == 0) error = EBADF; else { struct apm_ctl *actl = (struct apm_ctl *)data; bzero(®s, sizeof(regs)); if (!apmcall(APM_GET_POWER_STATE, actl->dev, ®s)) printf("%s: dev %04x state %04x\n", sc->sc_dev.dv_xname, dev, regs.cx); error = apm_set_powstate(actl->dev, actl->mode); } break; case APM_IOC_GETPOWER: if (apm_get_powstat(®s) == 0) { struct apm_power_info *powerp = (struct apm_power_info *)data; bzero(powerp, sizeof(*powerp)); if (BATT_LIFE(®s) != APM_BATT_LIFE_UNKNOWN) powerp->battery_life = BATT_LIFE(®s); powerp->ac_state = AC_STATE(®s); switch (apm_minver) { case 0: if (!(BATT_FLAGS(®s) & APM_BATT_FLAG_NOBATTERY)) powerp->battery_state = BATT_STATE(®s); break; case 1: default: if (BATT_FLAGS(®s) & APM_BATT_FLAG_HIGH) powerp->battery_state = APM_BATT_HIGH; else if (BATT_FLAGS(®s) & APM_BATT_FLAG_LOW) powerp->battery_state = APM_BATT_LOW; else if (BATT_FLAGS(®s) & APM_BATT_FLAG_CRITICAL) powerp->battery_state = APM_BATT_CRITICAL; else if (BATT_FLAGS(®s) & APM_BATT_FLAG_CHARGING) powerp->battery_state = APM_BATT_CHARGING; else if (BATT_FLAGS(®s) & APM_BATT_FLAG_NOBATTERY) powerp->battery_state = APM_BATTERY_ABSENT; else powerp->battery_state = APM_BATT_UNKNOWN; if (BATT_REM_VALID(®s)) { powerp->minutes_left = BATT_REMAINING(®s); if (sc->be_batt) powerp->minutes_left = swap16(powerp->minutes_left); } } } else { apm_perror("ioctl get power status", ®s); error = EIO; } break; case APM_IOC_STANDBY_REQ: if ((flag & FWRITE) == 0) error = EBADF; /* only fails if no one cares. apmd at least should */ else if (apm_record_event(sc, APM_USER_STANDBY_REQ)) error = EINVAL; /* ? */ break; case APM_IOC_SUSPEND_REQ: if ((flag & FWRITE) == 0) error = EBADF; /* only fails if no one cares. apmd at least should */ else if (apm_record_event(sc, APM_USER_SUSPEND_REQ)) error = EINVAL; /* ? */ break; default: error = ENOTTY; } rw_exit_write(&sc->sc_lock); return error; }
int apm_handle_event(struct apm_softc *sc, struct apmregs *regs) { struct apmregs nregs; int ret = 0; switch (regs->bx) { case APM_NOEVENT: ret++; break; case APM_USER_STANDBY_REQ: if (apm_resumes || apm_op_inprog) break; DPRINTF(("user wants STANDBY--fat chance\n")); apm_op_inprog++; if (apm_record_event(sc, regs->bx)) { DPRINTF(("standby ourselves\n")); apm_userstandbys++; } break; case APM_STANDBY_REQ: if (apm_resumes || apm_op_inprog) break; DPRINTF(("standby requested\n")); if (apm_standbys || apm_suspends) { DPRINTF(("premature standby\n")); apm_error++; ret++; } apm_op_inprog++; if (apm_record_event(sc, regs->bx)) { DPRINTF(("standby ourselves\n")); apm_standbys++; } break; case APM_USER_SUSPEND_REQ: if (apm_resumes || apm_op_inprog) break; DPRINTF(("user wants suspend--fat chance!\n")); apm_op_inprog++; if (apm_record_event(sc, regs->bx)) { DPRINTF(("suspend ourselves\n")); apm_suspends++; } break; case APM_SUSPEND_REQ: if (apm_resumes || apm_op_inprog) break; DPRINTF(("suspend requested\n")); if (apm_standbys || apm_suspends) { DPRINTF(("premature suspend\n")); apm_error++; ret++; } apm_op_inprog++; if (apm_record_event(sc, regs->bx)) { DPRINTF(("suspend ourselves\n")); apm_suspends++; } break; case APM_POWER_CHANGE: DPRINTF(("power status change\n")); apm_get_powstat(&nregs); apm_record_event(sc, regs->bx); break; case APM_NORMAL_RESUME: DPRINTF(("system resumed\n")); apm_resume(sc, regs); break; case APM_CRIT_RESUME: DPRINTF(("system resumed without us!\n")); apm_resume(sc, regs); break; case APM_SYS_STANDBY_RESUME: DPRINTF(("system standby resume\n")); apm_resume(sc, regs); break; case APM_UPDATE_TIME: DPRINTF(("update time, please\n")); inittodr(time_second); apm_record_event(sc, regs->bx); break; case APM_CRIT_SUSPEND_REQ: DPRINTF(("suspend required immediately\n")); apm_record_event(sc, regs->bx); apm_suspend(APM_SYS_SUSPEND); break; case APM_BATTERY_LOW: DPRINTF(("Battery low!\n")); apm_battlow++; apm_record_event(sc, regs->bx); break; case APM_CAPABILITY_CHANGE: DPRINTF(("capability change\n")); if (apm_minver < 2) { DPRINTF(("adult event\n")); } else { if (apmcall(APM_GET_CAPABILITIES, APM_DEV_APM_BIOS, &nregs) != 0) { apm_perror("get capabilities", &nregs); } else { apm_get_powstat(&nregs); } } break; default: { #ifdef APMDEBUG char *p; switch (regs->bx >> 8) { case 0: p = "reserved system"; break; case 1: p = "reserved device"; break; case 2: p = "OEM defined"; break; default:p = "reserved"; break; } #endif DPRINTF(("apm_handle_event: %s event, code %d\n", p, regs->bx)); } } return ret; }