A modified apm.c previously posted here, just to power-down
(not just “it is now safe to reboot your computer”)
APM-capable machines.
(Didn’t test on ACPI-only machines)
compile:
make apmoff; usemsg apmoff apmoff.c
Now, how should I fire this after “shutdown” “phshutdown” ?
(daemonize, trap SIGPWR, wait 9 secs then powerdown?)
–
kabe
/*
- apmoff.c
- APM power shutdown
- for QNX RTP(x86)
/
#if QNXNTO && X86
/ Ok, QNXRTP on x86 platform */
#else
#error Only for x86 architecture.
#endif
#include <stdio.h>
#include <unistd.h> /getopt/
#include <sys/types.h> /u_short/
#include <string.h> /strcmp/
#include <x86/v86.h>
/NetBSD-ish defines/
/APM/
#define APM_SYSTEM_BIOS 0x15 /int 15h/
#define APM_DEV_APM_BIOS 0x0000 /for BX in int 15h BIOS call/
#define APM_INSTALLATION_CHECK 0x5300 /for AX/
#define APM_32BIT_CONNECT 0x5303
#define APM_DISCONNECT 0x5304
#define APM_SET_POWER_STATE 0x5307
#define APM_GET_POWER_STATUS 0x530a
#define APM_DRIVER_VERSION 0x530e /set version of this driver/
#define APM_GET_CAPABILITIES 0x5310
struct {
u_short apm_flags;
u_short apm_version; /* BCD xx.xx /
u_short apm_driver_version; / BCD xx.xx */
} apm_info;
/* for struct _v86reg /
#define PSL_C 1 / carry flag mask */
#define BH(reg) ((((reg).ebx)>>8)&0xff)
#define BL(reg) (((reg).ebx)&0xff)
#define CH(reg) ((((reg).ecx)>>8)&0xff)
#define CL(reg) (((reg).ecx)&0xff)
#define DPRINTF(x) if (option.verbose) { printf x; } //
/*#define DUMPREGS(x) dumpregs(x)//
#define DUMPREGS(x) /**/
#ifdef __USAGE
%C - Shutdown the power on APM platform
%C [-v] [-S off|standby|suspend]
-v Verbose
-S Next state (default: off)
#endif
struct {
int verbose;
int dryrun;
enum {Standby=1, Suspend=2, Off=3} setstate;
} option = {0,0,Off};
void
dumpregs(const struct _v86reg *reg)
{
DPRINTF((“AX=%04X BX=%04X CX=%04X DX=%04X\n”,
(int)reg->eax, (int)reg->ebx, (int)reg->ecx, (int)reg->edx));
DPRINTF((“FL=%04X ESI=%08lX\n”, (int)reg->efl, reg->esi));
}
int
main(int argc, char *argv[])
{
struct _v86reg reg;
int s;
int c;
while (EOF != (c = getopt(argc, argv, “vS:n”))) {
switch (c) {
case ‘v’:
option.verbose++;
break;
case ‘S’:
if (!strcmpi(optarg, “shutdown”) || !strcmpi(optarg, “off”)) {
option.setstate = Off;
} else if (!strcmpi(optarg, “suspend”)) {
option.setstate = Suspend;
} else if (!strcmpi(optarg, “standby”)) {
option.setstate = Standby;
} else {
fprintf(stderr, “Unknown option -%c %s\n”, c, optarg);
exit(1);
}
break;
case ‘n’:
option.dryrun++;
break;
case ‘?’:
fprintf(stderr, “Unknown option -%c\n”, c);
exit(1);
}
}
/* installation check /
DPRINTF((“APM: Installation Check\n”));
memset(®, 0, sizeof(reg));
reg.eax = APM_INSTALLATION_CHECK; /APM Installation Check/
reg.ebx = APM_DEV_APM_BIOS; /APM BIOS/
s = _intr_v86(APM_SYSTEM_BIOS, ®, NULL,0);
if (s || reg.efl&PSL_C) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
DUMPREGS(®);/XXX/
/
- AH.AL: APM version number (BCD)
- BH.BL: “PM”
- CX: 0 has16bit
-
1 has32bit
-
2 Idle slows CPU
-
3 disable
-
4 disengage
*/
DPRINTF((" APM Supported: %s\n", (reg.efl&PSL_C)?“no”:“yes”));
apm_info.apm_version = reg.eax;
apm_info.apm_flags = reg.ecx;
DPRINTF((" BX signature=<%c%c>%s\n",
BH(reg), BL(reg),
((reg.ebx&0xffff)==(‘P’<<8)+‘M’)?" (ok)":" (MISMATCH)"));
DPRINTF((" BIOS APM version %x.%x\n",
(apm_info.apm_version>>8)&255, apm_info.apm_version&255 ));
if(apm_info.apm_flags & 1) { DPRINTF((" 16bit mode interface\n")); }
if(apm_info.apm_flags & 2) { DPRINTF((" 32bit mode interface\n")); }
if(apm_info.apm_flags & 4) { DPRINTF((" Slow CPU on Idle\n")); }
DPRINTF((" BIOS PM %s\n", (apm_info.apm_flags&8)?“disabled”:“enabled”));
DPRINTF((" BIOS PM %s\n", (apm_info.apm_flags&16)?“disengaged”:“engaged”));
if ((reg.ebx&0xffff) != (‘P’<<8)+‘M’) {
fprintf(stderr, “APM is not supported on this machine.\n”);
exit(1);
}
/* disconnect first, in case somebody was connected */
memset(®, 0, sizeof(reg));
reg.eax = APM_DISCONNECT;
reg.ebx = APM_DEV_APM_BIOS; /APM BIOS/
s = _intr_v86(APM_SYSTEM_BIOS, ®, NULL,0);
if (s) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
/if (reg.efl&PSL_C) { fprintf(stderr, “APM: disconnect failed\n”); exit(1); }/
/* connect 32 /
DPRINTF((“APM: Connect32\n”));
memset(®, 0, sizeof(reg));
reg.eax = APM_32BIT_CONNECT;
reg.ebx = APM_DEV_APM_BIOS; /APM BIOS/
s = _intr_v86(APM_SYSTEM_BIOS, ®, NULL,0);
if (s) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
if (reg.efl&PSL_C) {
fprintf(stderr, “APM: Connect32 failed (code 0x%02lX)\n”, reg.eax);
return 1;
}
DUMPREGS(®);
/
- AX: code32 realmode CS
- (E)BX: entry point offset
- CX: code16 realmode CS
- ESI: 16 code32 length-1
-
16 code16 length-1
- DX: data realmode DS
- DI: data length
*/
DPRINTF((" 32Bit entry=%04X:%04X (len %04X+1)\n", reg.eax, reg.ebx, reg.esi&0xffff));
DPRINTF((" 16Bit entry=%04X:%04X (len %04X+1)\n", reg.ecx, reg.ebx, (reg.esi>>16)&0xffff));
DPRINTF((" Data region=%04X:( 0) (len %04X+1)\n", reg.edx, reg.edi));
/* set my version (needed to access 1.1- BIOS calls) (APM 1.1) /
apm_info.apm_driver_version = 0x0101; / set to APM 1.1 /
DPRINTF((“APM: set Driver Version to %x.%x\n”,
(apm_info.apm_driver_version>>8)&255, apm_info.apm_driver_version&255
));
memset(®, 0, sizeof(reg));
reg.eax = APM_DRIVER_VERSION;
reg.ebx = APM_DEV_APM_BIOS;
reg.ecx = apm_info.apm_driver_version;
s = _intr_v86(APM_SYSTEM_BIOS, ®, NULL,0);
/
- AH.AL: APM connection version
/
if (s) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
if (reg.efl&PSL_C) {
fprintf(stderr, “APM: set Driver Version failed (code 0x%02X)\n”, (int)(reg.eax>>8));
/ what should I do? /
/ some BIOS seems to accept 1.1 calls regardess of error, - so just continue for now /
/exit(1);/
} else {
DPRINTF((" Connected as version %x.%x\n", (reg.eax>>8)&255, reg.eax&255 ));
}
/ set power status (to OFF etc) (APM 1.1) /
DPRINTF((“APM: Set Power State (All, %d)\n”, option.setstate));
memset(®, 0, sizeof(reg));
reg.eax = APM_SET_POWER_STATE;
reg.ebx = APM_DEV_APM_BIOS +0x0001; / all devices */
reg.ecx = option.setstate;
if (!option.dryrun) {
s = _intr_v86(APM_SYSTEM_BIOS, ®, NULL,0);
}
if (s) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
if (reg.efl&PSL_C) {
fprintf(stderr, “APM: Set Power State failed (code 0x%02X)\n”, (int)(reg.eax>>8));
goto off_fail;
}
off_fail:
/* NOTREACHED */
goto disconnect;
return 0;
disconnect:
DPRINTF((“APM: Disconnect\n”));
memset(®, 0, sizeof(reg));
reg.eax = APM_DISCONNECT;
reg.ebx = APM_DEV_APM_BIOS; /APM BIOS/
s = _intr_v86(APM_SYSTEM_BIOS, ®, NULL,0);
if (s) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
return s;
}