I’ve wanted to just glean the battery status of the APM laptop,
so here’s a little tool to just query the APM BIOS about it.
The next step is to turn this into a resource manager and
attatch permanently as an “APM Driver”, but this needs a
public API to provide for i.e device drivers to i.e send pulses
on Sleep event.
Is there any QSSL under-development APIs for power management to follow?
References: Advanced Power Management v1.2
URL:http://www.microsoft.com/hwdev/archive/BUSBIOS/amp_12.asp
kabe
/*
- apm.c
- for QNX RTP(x86)
/
#if QNXNTO && X86
/ Ok, QNXRTP on x86 platform */
#else
#error Only for x86 architecture.
#endif
#include <stdio.h>
#include <sys/types.h> /u_short/
#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_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) printf x
/*#define DUMPREGS(x) dumpregs(x)//
#define DUMPREGS(x) //
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()
{
struct _v86reg reg;
int s;
/* 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”));
/* 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%02X)\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_info.apm_driver_version = 0x0101; / 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”, reg.eax>>8);
return 1;
}
DPRINTF((" Connected as version %x.%x\n", (reg.eax>>8)&255, reg.eax&255 ));
/* get capabilities (1.2) /
#if 1 /{/
DPRINTF((“APM: Get capabilities\n”));
if (apm_info.apm_version < 0x0102 ||
apm_info.apm_driver_version < 0x0102) {
DPRINTF((" Version (driver %x.%x BIOS %x.%x) < 1.2, no function\n",
(apm_info.apm_driver_version>>8)&255,
apm_info.apm_driver_version&255,
(apm_info.apm_version>>8)&255,
apm_info.apm_version&255
));
goto no_cap;
}
memset(®, 0, sizeof(reg));
reg.eax = APM_GET_CAPABILITIES;
reg.ebx = APM_DEV_APM_BIOS;
s = _intr_v86(APM_SYSTEM_BIOS, ®, NULL,0);
if (s) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
DUMPREGS(®);
if (reg.efl&PSL_C) {
fprintf(stderr, “APM: GetCapabilities failed (code 0x%02X)\n”, reg.eax);
return 1;
}
DPRINTF((" Batteries: %d\n", reg.ebx&255));
DPRINTF((" Other caps: 0x%02X\n", reg.ecx));
#endif /}*/
no_cap:
/* get current power status (1.1) /
DPRINTF((“APM: Get Power Status\n”));
memset(®, 0, sizeof(reg));
reg.eax = APM_GET_POWER_STATUS;
reg.ebx = APM_DEV_APM_BIOS +1;
s = _intr_v86(APM_SYSTEM_BIOS, ®, NULL,0);
/
- BH: AC status (0,1,2,0xFF)
- BL: Batttery status (0,1,2,3,0xFF)
- CH: Battery Flag
- CL: Battery remaining % (0-100,0xFF)
- DX: Battery remaining time
*/
if (s) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
if (reg.efl&PSL_C) {
fprintf(stderr, “APM: Get Power Status failed (code 0x%02X)\n”, reg.eax>>8);
goto no_cur_pow;
}
DPRINTF((" AC status:"));
switch(BH(reg)) {
case 0x00: DPRINTF((" Offline\n")); break;
case 0x01: DPRINTF((" Online\n")); break;
case 0x02: DPRINTF((" On Backup Power\n")); break;
case 0xFF:
default: DPRINTF((" (Unknown)\n")); break;
}
DPRINTF((" Battery status: “));
switch(BL(reg)) {
case 0x00: DPRINTF((“High”)); break;
case 0x01: DPRINTF((“Low”)); break;
case 0x02: DPRINTF((“Critical”)); break;
case 0x03: DPRINTF((“Charging”)); break;
case 0xFF:
default: DPRINTF((”(unknown)")); break;
} DPRINTF(("\n"));
DPRINTF((" Battery flag: 0x%02X (", CH(reg)));
if (CH(reg) != 0xff) {
int ch = CH(reg);
if (ch&1) DPRINTF((“High,”));
if (ch&2) DPRINTF((“Low,”));
if (ch&4) DPRINTF((“Critical,”));
if (ch&8) DPRINTF((“Charging,”));
if (ch&16) DPRINTF((“NoSelectedBatt,”));
if (ch&128) DPRINTF((“NoBatt”));
}
DPRINTF((")\n"));
if (CL(reg)<=100) DPRINTF((" Battery remain: %d%%\n", CL(reg)));
if ((reg.edx&0xffff)!=0xffff)
DPRINTF((" Battery remain: %d:%02d (%s)\n",
(reg.edx&0x7fff)/60, (reg.edx&0x7fff)%60,
(reg.edx&0x8000)?“hour:min”:“min:sec”));
no_cur_pow:
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;
}