[source] apmdisp.c: control APM mode for display

If your APM display time-out screws with Photon, you
usually just disable it in the BIOS;

but the motivation for this tool is I wanted the laptop’s
display time-out enabled for non-photon (console) mode, and
disable it when entering Photon.

Usage: apmdisp display disable

(Not tested on non-APM machine)

kabe

#if shell
THIS=basename $0 .c
set -x
make $THIS
usemsg $THIS $0
exit
#endif
/*

  • apmdisp.c
  • disable/enable APM power control for display
  • (needed for Photon)
  • XXX: Enabling display APM in Photon may freeze the display instantly
  •  if BIOS has internally fired timeout
    
  • 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/
/* for BX /
#define APM_DEV_APM_BIOS 0x0000
#define APM_DEV_DISP_ALL 0x01FF /All display/
/
for AX /
#define APM_INSTALLATION_CHECK 0x5300
#define APM_32BIT_CONNECT 0x5303
#define APM_DISCONNECT 0x5304
#define APM_SET_POWER_STATE 0x5307
#define APM_GET_POWER_STATE 0x530c
#define APM_GET_POWER_STATUS 0x530a
#define APM_ENABLE_DEVICE_PM 0x530d /enable/disable/
#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 - Enable/Disable APM for display

%C [-nv] [display off|on|query]
Options:
-v Verbose
-n Dryrun
display {on|off|query} enable/disable APM for display device
#endif

struct {
int verbose;
int dryrun;
enum {Off=0, On=1, Query=9, NoSpec=99} pm_disp;
} option = {0,0,NoSpec};

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
apm_connect()
{
struct _v86reg reg;
int s;

/* installation check /
DPRINTF((“APM: Installation Check\n”));
memset(&reg, 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, &reg, NULL,0);
if (s || reg.efl&PSL_C) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
DUMPREGS(&reg);/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",
(int)BH(reg), (int)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(&reg, 0, sizeof(reg));
reg.eax = APM_DISCONNECT;
reg.ebx = APM_DEV_APM_BIOS; /APM BIOS/
s = _intr_v86(APM_SYSTEM_BIOS, &reg, 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(&reg, 0, sizeof(reg));
reg.eax = APM_32BIT_CONNECT;
reg.ebx = APM_DEV_APM_BIOS; /APM BIOS/
s = _intr_v86(APM_SYSTEM_BIOS, &reg, 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(&reg);
/

  • 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", (int)reg.eax, (int)reg.ebx, (int)reg.esi&0xffff));
    DPRINTF((" 16Bit entry=%04X:%04X (len %04X+1)\n", (int)reg.ecx, (int)reg.ebx, (int)((reg.esi>>16)&0xffff)));
    DPRINTF((" Data region=%04X:( 0) (len %04X+1)\n", (int)reg.edx, (int)reg.edi));
    return s;
    }

int
apm_disconnect()
{
struct _v86reg reg;
int s;

DPRINTF((“APM: Disconnect\n”));
memset(&reg, 0, sizeof(reg));
reg.eax = APM_DISCONNECT;
reg.ebx = APM_DEV_APM_BIOS; /APM BIOS/
s = _intr_v86(APM_SYSTEM_BIOS, &reg, NULL,0);
if (s) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
return s;
}

int
main(int argc, char *argv[])
{
struct _v86reg reg;
int s;
int c;

while (EOF != (c = getopt(argc, argv, “vn”))) {
switch (c) {
case ‘v’:
option.verbose++;
break;
case ‘n’:
option.dryrun++;
break;
case ‘?’:
fprintf(stderr, “Unknown option -%c\n”, c);
exit(1);
}
}
if (optind == argc) {
/* no arg … default to “display query” */
option.pm_disp = Query;
}
while (optind < argc) {
if (!strcmp(“display”, argv[optind])) {
optind++;
if (optind >= argc) goto noarg;
if (!stricmp(“off”, argv[optind]) || !stricmp(“disable”, argv[optind])) {
option.pm_disp = Off;
} else if (!stricmp(“on”, argv[optind]) || !stricmp(“enable”, argv[optind])) {
option.pm_disp = On;
} else if (!stricmp(“query”, argv[optind])) {
option.pm_disp = Query;
} else {
fprintf(stderr, “Specify: display {On|Off}\n”);
exit(1);
}
optind++;
} else {
fprintf(stderr, “Unknown option %s\n”, argv[optind]);
exit(1);
noarg:
fprintf(stderr, “Argument missing\n”);
exit(1);
}
continue;
}

s = apm_connect();

/* 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(&reg, 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, &reg, 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", (int)(reg.eax>>8)&255, (int)reg.eax&255 ));
    }

/* Enable/Disable Power Management on Display */
if (option.pm_disp == On || option.pm_disp == Off) {
char actionstr;
switch (option.pm_disp) {
case On: actionstr=“Enable”; break;
case Off: actionstr=“Disable”; break;
default: /NOTREACHED/ actionstr="?DoWhat?";
}
DPRINTF((“APM: %s APM on Display\n”, actionstr));
memset(&reg, 0, sizeof(reg));
reg.eax = APM_ENABLE_DEVICE_PM;
reg.ebx = APM_DEV_DISP_ALL;
reg.ecx = option.pm_disp;
if (!option.dryrun) {
s = _intr_v86(APM_SYSTEM_BIOS, &reg, NULL,0);
}
/
AH: errcode */
if (s) { fprintf(stderr,“error on _intr_v86\n”); exit(s); }
if (reg.efl&PSL_C) {
fprintf(stderr, “APM: %s Display PM failed (code 0x%02X)\n”, actionstr, (int)(reg.eax>>8));
} else {
fprintf(stderr, “APM: Display PM changed to %s\n”, actionstr);
}
}/disp.enable/

/* Query Display Power status /
if (option.pm_disp == Query) {
memset(&reg, 0, sizeof(reg));
reg.eax = APM_GET_POWER_STATE;
reg.ebx = APM_DEV_DISP_ALL & 0xff00; /
Primary Display /
DPRINTF((“APM: Query Display Power status on display %d\n”, (int)reg.ebx&255));
s = _intr_v86(APM_SYSTEM_BIOS, &reg, NULL,0);
if (s) { fprintf(stderr, “error on _intr_v86\n”); exit(s); }
/
AH: errcode */
if (reg.efl&PSL_C) {
switch((reg.eax>>8)&0xff) {
case 0x01: fprintf(stderr, “APM: Display PM is currently Disabled\n”); break;
case 0x09: fprintf(stderr, “APM: Unrecognized device\n”); break;
case 0x86: fprintf(stderr, “APM: no APM\n”); break;
default: fprintf(stderr, “APM: Unknown Error %d\n”, (int)reg.eax>>8);
}
} else {
char *str;
switch(reg.ecx&0xffff) {
case 0: str=“is APM Enabled”; break;
case 1: str=“is in Standby”; break;
case 2: str=“is in Suspend”; break;
case 3: str=“is Off”; break;
default: { static char s[32];
sprintf(s, “State Unknown %04X”, (int)reg.ecx&0xffff);
str = s;
}
}
fprintf(stderr, “APM: Display %s\n”, str);
}
}/disp.Query/

disconnect:
return apm_disconnect();
}