[source] apmoff.c: powerdown APM machine

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(&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",
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(&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", 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(&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", (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(&reg, 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, &reg, 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(&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;
}

<kabe@sra-tohoku.co.jp> wrote in message news:aaae68$fs2$1@inn.qnx.com

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?)

This is for QNX 4.25 (reboot vector), maybe there is sth. similiar also in
QNX 6.

Pavol Kycina

  • proc_hint():
  • You can now define software interrupt handlers by OR-ing
    0x100 to the desired interrupt vector.

Interrupt vector map:

0x00…0x20- intel defined (0x1a - PCI vector).
0x70…0x77- hw intr 8…15.
0xe0 - bios vector.
0xe8…0xef- hw intr 0…7.
0xf0 - 16bit kernel interface.
0xf1 - kernel semaphore functions.
0xf2 - kernel calls.
0xf3 - kernel debugger.
0xf4 - panic vector (invoked on crash)
0xf5 - reboot vector.
0xfa - trace vector.
0xfb - monitor vector.