| |
|
UPS Server I used to work in a building with bad power, and would come in in the morning to find that up to half of the 17 servers scattered round the building had died during the night leaving corrupted databases behind. So we invested in American Power Conversion UPS's .. and I wrote this... A TSR that re-programs the 8250 chip, stuffs the keyboard buffer, messes with the interrupt enable register, and generally does things calculated to give Microsoft control freaks the heebie jeebies TSR's have gone out of fashion, but it's entirely possible to have them in a Win32 environment, so maybe this code will rise again...
// Copyright : ZDS Corporation 1992
// Proprietary information.
// For use by ZDS Corporation employees only.
// System : Tsr
// Program : Upserver
// Function : Designed to run on a (Novell) server-workstation equipped with
// a special cable to intercept battery low status indication from
// an American Power UPS and deal with it BEFORE Novell becomes
// aware of it.
// Author : Dave Goodall, ZDS Corporation (416) 845-3653
// Written : 14 Dec 90
// History : DD MMM YY By Ver Description
// 14 Dec 90 Dave Goodall 1.01 New Code (Son of Ups0cy)
// 18 Dec 90 Dave Goodall 1.02 Revised as result of Testing
// to use special Serial Cable
// 21 Dec 90 Dave Goodall 1.03 Add D-N-S state control
// logic agreed with R. Stubbs
// 22 Dec 90 Dave Goodall 1.04 Add Kbd buffer stuff logic
// 02 Jan 90 Dave Goodall 1.05 Add - O/P Esc to Keyboard
// at end of Delay Time
// Move - O/P of SHIFT-ENTER
// to end of Novell Time
// Test esc with Rbase.
//__________________________________________________________________________
//
// Invoke : upserial /D=nn /N=nn /S=nn /R=nn
// Defaults : 15 5 2 12
//
// /D=nn is Delay in MINUTES after Pin 2 on the UPS goes from
// low (-8v) to Hi (+12v) to indicate Commercial Power
// is off before the program increments the SYSTEM YEAR.
//
// Note - If Pin two goes Low again during this period
// to indicate power has been restored, the count down of
// D is halted, and then incremented back by 1 for every
// R minutes that pass with commercial power back on.
//
// This ensures that if closely spaced power outages occur
// the program allows for the fact that the battery has
// drained down to some extent, and has not been fully re-
// charged in the intervening power up period.
//
// Note therefore that at any point in time D may be less
// than the default or override time supplied when the
// background program was originally loaded, depending on
// the extent to which the battery has been drawn down.
//
// /N=nn is Delay in Minutes after program increments SYSTEM YEAR
// before program passes on the bad news to Novell that the
// UPS has lost Commercial Power.
//
// This delay is intended to allow time for Rbase programs
// to become aware of the imminent power fail situation
// via the SYSTEM YEAR change, and close down gracefully.
//
// /S=nn is Delay in Minutes after program passes on the Bad
// news to Novell, that the program shuts down the UPS
// inverter to prevent the battery being drained to total
// dead flat.
//
// /R=nn is the recharge time in minutes that the UPS has to be
// back on Commercial power to put back in in the battery
// One minute of battery powering the inverter time.
//__________________________________________________________________________
//
/* Ups ..... Mouse Port Y Cable Specification
:.. Serial Port
............................
: UPS DB9 Female Port :
: SD LF LF LB SC GND :
: ho ia ia oa wo :
: uw ni ni wt cm :
: tn el el :
: : : : :
: RS232C : :./.: :
: :...../.: :
:..........................:
: : : : : :
............................
: Male DB9 Cable End :
: 1 2 3 4 5 9 :
:..........................:
^ v v v v v
: : : : : :
...............................................: : : : : :
: : : : : :
: ...........................................: : : : :
: : : : : :
: : ........................... : : : :
: : : : : : : :
: : : .... To 9 > : : : : ..:
: : : : : : : : :
: : : : v : : : :
: : : : ............................
: : : : : Female DB9 Cable End :
: : : : : 1 2 3 4 5 9 :
: : : : :..........................:
: : : :
: : : : ............................
: : : : : Male DB9 Cable End :
: : : : :..........................:
: : : : |
: : : : |
: : : : |
^ v ^ : |
Female PS2/60 Serial Port 25 Pin Cable End PS2/60 Mouse Port
................................... ...........................
: 4 20 5 8 22 6 2 7 : : 1 2 3 4 5 9 :
: RTS DTR CTS CD RI DSR TD SG : : :
:.................................: :.........................:
^ ^ v ^
: : : :........................
: : : :
: : :................................. :
: : : : :
: : : : :
^ ^ v v :
: : :
Modem Modem Status Register : :
Control : : :
Register v v ^
........... ................................... :
: RTS DTR : : CD RI DSR CTS CD RI DSR CTS : :
: : : del del del del : :
: Bit Bit : : Bit Bit Bit Bit Bit Bit Bit Bit : :
: 1 0 : : 7 6 5 4 3 2 1 0 : :
:.........: :.................................: :
v v :
: : :
........:...............: :
: :
v ^
Line Control Register :
................................... :
: DL SET STK EVN PAR STP DAT DAT : :
: AB BRK PAR PAR ENB BIT BIT BIT : :
: Bit Bit Bit Bit Bit Bit Bit Bit : :
: 7 6 5 4 3 2 1 0 : :
:.................................: :
v :
:...............................:
Note : Refer to American Power Conversion Owners Manual Models
// 100VX/800RT for UPS Remote Signal Connnector Pinout diagram.
*/
//__________________________________________________________________________
//
// Function : -Goes resident and runs as a background program.
// -Programs the 8250 chip as follows:
//
// -Checks the Modem Status Register for a change in CTS, which
// has been wired to the UPS Pin 2 which is set up for RS232C
// operation ie:
// - Low (-8v) if Commercial Power is on
// - High (+12v) if Commercial Power is off. Note that the UPS
// will 'ride-through' 18 seconds of Commercial power out or
// transient low voltage before it raises the alarm, so if
// we get this, we probably have a serious outage.
//
// - The cable is wired so as to pass all the lines from the UPS
// through to the PC Mouseport aka Novell, except lines 1 and 2.
//
// When we get a Commercial power out signal we start counting
// down minutes from the Delay value D. If we get commercial
// power back before we reach D=0, then we start incrementing
// D back to the original default or override value, but more
// slowly, as it take longer to recharge the battery than to
// use it! (See explanation of /D=nn for more detail).
//
//
// - When the Delay time has expired, we :
// - Increment the system YEAR by 1.
// - Eat any characters in the keyboard buffer and then
// stuff an 'Esc' into it ie we simulate the depression
// of the 'Esc' key on the keyboard.
//
// - Rbase programs active at this time will be in one of two
// states:
// a. Sitting in a CHOOSE or PAUSE instruction waiting for
// the operator to make a selection (this is the most
// common state usually).
// b. Running through a loop of instructions which don't
// require a user response.
//
// If they are in state 1 waiting for operator I/O the 'Esc'
// will kick the Rbase program out of the CHOOSE or PAUSE
// instruction.
//
// After each such instruction, the program should call a
// routine to see if the system YEAR has changed since the
// program started (we'll assume you saved the start-up value).
// If it has goto a standard routine to put up a panel warning
// the operator that power failure is imminent, then jump to
// the normal Rbase close files and shutdown logic.
// then goto DOS.
//
// If you have state 2 loops which may go on for a long time
// before they return to a type 1 menu situation, then put a
// check of the system year into the loop so that it gets
// called at least every 15 minutes which is a bit less than
// the UPS on-battery-support up time.
//
// - To cover the Rbase programs with sufficient time to cycle
// into the year check logic and shutdown, we start counting
// down a further delay time of N minutes.
//
// This is because on once Novell gets to hear about the
// problem it starts broadcasting messages to all and sundry,
// and flushing buffers, something we don't want it doing while
// we're busy closing down open Rbases.
//
// - When the N minutes is up, we then use the Line Control
// Register to pull up Pin 2 on the serial interface which
// is wired through to Pin 2 on the Mouseport.
//
// After we're good and ready therefore (ie D+N) minutes after
// initial power loss, Novell gets to hear the bad news and will
// start broadcasting it. However, by that time, all the action
// is over, and our Rbase files tucked safely up in bed.
//
// There is a restriction on this, in that if a long Rbase task
// is in execution, we cannot recall it, as there is no known way
// to checkpoint an Rbase 2.1 task, and run the year check
// routine. However the majority of crashes should now be
// evitable.
//
// - Lastly, as soon as the program has passed the bad noos onto
// Novell, it starts a final countdown of S minutes.
//
// When this is up, it uses the Modem Control Register to
// pull up pin 4 on the serial interface and leaves it up.
// Pin 4 is wired to Pin 1 on the UPS port, and the appearance
// of a hi voltage on this pin for a minimum of 5 seconds causes
// the UPS to shut the inverter down.
//
// Once we're into the 'S' state, the program is set up to shut
// down the UPS when S-time is up no matter whether commercial
// power is restored or not.
//
// After this we don't care as we're dead, but the Battery
// will thank us (thank you, thank you), as otherwise the
// inverter will continue to pull power from the battery till
// the battery is drained dead flat.
//
// Which as anyone who has left his lights on knows is not good
// for battery life. By the way, you are sure you turned your
// lights off aren't you?
//
// Note - we should find out if, after the inverter is shut
// down via Pin 1, if the battery starts recharging if power
// is restored, or if the UPS just lays there till the reset
// switch is pressed.
//
//__________________________________________________________________________
//
// Pins : The UPS appears to put out +12v on Pin 8 of the UPS port
// permanently. This may be useful if you need to have a plus
// voltage.
//
// Serial pin 2 was chosen to tickle the mouseport Pin 2 because
// a master reset according to the IBM 8250 documentation always
// pulls it low ie safe.
//
// Serial pin 4, was chosen to tickle the UPS shutdown Pin 1
// because although a master reset pulls it up, this is only
// an extermely transient up, much too short to trigger inverter
// shut-down.
//
// Serial Pin 20 (DTR) is still available to control something
// if needed ie if APC see the light and, on future models of
// their UPS's handle LOW BATTERY properly by providing the
// 10c worth of transistors required to provide RS232C control
// of that condition! (presumably on Pin 6).
//
//__________________________________________________________________________
//
// 8250 Register Map and Usage REG8250.WKS
// Port Addresses and Functions
//
//
// MSB ----------------------Register------------------------------ LSB
// Bits
// 7 6 5 4 3 2 1 0
//
// _____________________________________________________________________
//
// PORT : COM1 = 0x3f8 COM2 = 0x2f8 COM3 = 0x3e8 COM4 = 0x2e8
//
// Receiver Buffer Register (Read onlIf Line Control Register dlab=0
// Data Data Data Data Data Data Data Data
// Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
//
//
// OR
// Transmitter Holding Reg (WRite onlIf Line Control Register dlab=0
// Data Data Data Data Data Data Data Data
// Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
//
//
// OR
// LSB of Baud Rate Divisor Value If Line Control Register dlab=1
// Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
// _____________________________________________________________________
//
// In Which Case
//
//
// PORT + 1 : COM1 = 0x3f9 COM2 = 0x2f9 COM3 = 0x3e9 COM4 = 0x2e9
// MSB of Baud Rate Divisor Value If Line Control Register dlab=1
// Bit 15 Bit 14 Bit 13 Bit 12 Bit 11 Bit 10 Bit 9 Bit 8
//
//
// Alternatively PORT+1 is
// Interrupt Enable Register If Line Control Register dlab=0
//
// 0 0 0 0 Enable Enable Enable Enable
// Modem Received Transmit Received
// Status Line Holding Data
// Int Status Register Available
// Empty
// _____________________________________________________________________
//
// PORT + 2 : COM1 = 0x3fa COM2 = 0x2fa COM3 = 0x3ea COM4 = 0x2ea
// Interrupt Identification Register
//
// 0 0 0 0 0 InterruptInterrupt 0
// ID ID If Int
// Bit 1 Bit 0 Pending
// _____________________________________________________________________
//
// PORT + 3 : COM1 = 0x3fb COM2 = 0x2fb COM3 = 0x3eb COM4 = 0x2eb
// Line Control Register
//
// D Divisor Set Stick Even Parity Number Word Word
// L Latch Break Parity Parity Enable Of Length Length
// A Access Select Stop Select Select
// B Bit Bits Bit 1 Bit 0
// _____________________________________________________________________
//
// PORT + 4 : COM1 = 0x3fc COM2 = 0x2fc COM3 = 0x3ec COM4 = 0x2ec
// Modem Control Register
//
// 0 0 0 Loop Out 2 Out 1 RTS DTR
//
//
// _____________________________________________________________________
//
// PORT + 5 : COM1 = 0x3fd COM2 = 0x2fd COM3 = 0x3ed COM4 = 0x2ed
// Line Status Register
//
// 0 Transmit Transmit Break Framing Parity Overrun Data
// Shift Holding Int- Error Error Error Ready
// Register Register errupt
// Empty Empty
// _____________________________________________________________________
//
// PORT + 6 : COM1 = 0x3fe COM2 = 0x2fe COM3 = 0x3ee COM4 = 0x2ee
// Modem Status Register
// Recv'd Ring Data Clear Delta Trailing Delta Delta
// Line Indicator Set To Receive Edge Data Clear
// Signal Ready Send Line Ring Set To
// Detect Signal Indicator Ready Send
// Det
// _____________________________________________________________________
//
// Function A: To set the baud rate for the chip:
//
// "Open the Dlab"
// - eg set the most significant bit of the Line Control Register to 1
// by reading line control register byte, OR'ing it with 0x80 (10000000),
// and writing the byte back to the register.
//
// - Turbo C: #define SERIO_BASEPORT 0x03f8 /* COM1 Base Port */
// outportb(SERIO_BASEPORT+3,inportb(SERIO_BASEPORT+3 | 0x80);
// Zortech: outp(SERIO_BASEPORT+3,inp(SERIO_BASEPORT+3) | 0x80);
//
// Put the Baud rate into PORT (LSB) and PORT+1 (MSB)
//
// - Turbo C: int baud = 0x000c /* 9600 baud */
// outport(SERIO_BASEPORT,baud);
// outport(SERIO_BASEPORT+1,baud>>8); /* right shift high
// order bits */
// "Close the Dlab"
// - eg reset msb of lct to 0 so that the Port & Port +1 registers
// revert to their usual (data register) functions.
//
// - Turbo C: outportb(SERIO_BASEPORT+3,inportb(SERIO_BASEPORT+3) & 0x7f~);
// Zortech: outp(SERIO_BASEPORT+3,inp(SERIO_BASEPORT+3) & 0x7f~);
//
// Function B: Set the parity, bits per char, stop bits
//
// - Turbo C: #define NOPARITY 0x0000
// #define EIGHTBITS 0x0003
// #define ONESTOPBIT 0x0000
// outportb(SERIO_BASEPORT+3,NOPARITY|EIGHTBITS|ONESTOPBIT);
//
// Function C: Set Interrupt Enable
//
// - Turbo C: outport(SERIO_BASEPORT+1,0x01);
//
// Function D: Set Modem Control
//
// - Turbo C: outport(SERIO_BASEPORT+4, 0x0f);
// _____________________________________________________________________
//
// 8259 Interrupt Controller Registers (8 bits each)
//
// Interrupt Mask Register (IMR) BIOS INTA01
//
// Parallel Diskette Fixed COM1 COM2 RESERVED Keyboard Timer
// Printer Disk PrimarySecondary
// Serial Serial
// SDLC SDLC
//
// Interrupt Request Register (IRR) BIOS INTA00
//
// Edge,Sngl,buffered, 8086 mode
// _____________________________________________________________________
//
//
// Masking:
//
// 0=Interrupt Enabled 1=Interrupt Disabled eg
// ff 11111111 Disable all device interrupts
// fe 11111110 Disable all except Timer Interrupt
// bf 10111111 Enable diskette interrupt only
//
// Bitwise Logical Operations
//
// OR | XOR ^
// 0 + 0 = 0 0 + 0 = 0
// 0 + 1 = 1 0 + 1 = 1
// 1 + 0 = 1 1 + 0 = 1
// 1 + 1 = 1 1 + 1 = 0
//
// AND & ~ Ones complement
// 0 + 0 = 0
// 0 + 1 = 0
// 1 + 0 = 0
// 1 + 1 = 1
//
//__________________________________________________________________________
//
// Usage : The object of the exercise is to ensure that active Rbase
// programs become aware of imminent power failure, and close
// open Rbase files to prevent the corruption that inevitably
// occurs if the system dies with them open.
//
// To make use of the background monitoring program any
// program (Not just R:Base) programs that wants to should
// save the system YEAR somewhere when it starts up.
//
// Such programs should then check the system date at any
// convenient point (eg when cycling in a loop waiting for
// keyboard input). If the year has changed, they can put
// up the appropriate error message, close their files, and
// shutdown.
//
//__________________________________________________________________________
//
// Compiler : Zortech C++ V2.1
//
// On the tsr_install the argument is TIMESLICE, as we want
// popmain called every 18.2th of a second.
//
// Leave the TSR_DEBUG option in PERMANENTLY.
// There's no space penalty involved, and given the very low
// usage of the routine, no performance penalty either. If
// you try & do no-no things in the handler, the TSR package
// will popup appropriate naughty boy messages.
//
//__________________________________________________________________________
//
// ZORTECH LIBRARY INCLUSIONS
// (pared down to minimise program size)
//
# include <dos.hx> // ESSENTIAL DECLS ONLY !!!!!
# include <stdio.hx> // puts, fcloseall ONLY!!!!
# include <string.hx> // strncmp ONLY !!!
# include <tsr.hx> // ESSENTIAL DECLS ONLY !!!!!
# include <intvec.hpp>
# include <bios.h>
# include <disp.h>
# include <sound.h>
# include <stdlib.h> // atoi,exit
# include <ctype.h> // toupper
//__________________________________________________________________________
// Function prototypes
int _cdecl d_state(void);
int _cdecl n_state(void);
int _cdecl s_state(void);
int _cdecl save_cursor(void);
int _cdecl restore_cursor(void);
int _cdecl inc_year(void);
int _cdecl warn1(void);
int _cdecl halt(void);
int _cdecl diag(void);
int _cdecl open_display(void);
int _cdecl close_display(void);
int _cdecl save_cursor(void);
int _cdecl restore_cursor(void);
//__________________________________________________________________________
// Serial Port Constants
#define SEVENBITS 0x0002
#define EIGHTBITS 0x0003
#define ONESTOP 0x0000
#define TWOSTOP 0x0004
#define NOPARITY 0x0000
#define ODDPARITY 0x0008
#define EVENPARITY 0x0018
#define NOBREAK 0x0000
#define SETBREAK 0x0040
#define BAUD150 0x0300
#define BAUD300 0x0180
#define BAUD600 0x00c0
#define BAUD1200 0x0060
#define BAUD2400 0x0030
#define BAUD4800 0x0018
#define BAUD9600 0x000c
#define BAUD19200 0x0006
#define COM1_INT 0x0c // COM1 Interrupt Number
#define COM1_BASEPORT 0x03f8 // COM1 Base Port Address
#define COM1_MASK 0xef // COM1 Interrupt Mask 8259 11101111
#define COM2_INT 0x0b // COM2 Interrupt Number
#define COM2_BASEPORT 0x02f8 // COM2 Base Port Address
#define COM2_MASK 0xf7 // COM2 Interrupt Mask 8259 11110111
// Serial Port Defaults
int baud = BAUD150;
int line_break = NOBREAK;
int parity = NOPARITY;
int stopbits = ONESTOP;
int databits = EIGHTBITS;
int comx_int = COM1_INT; // COM Interrupt Number
int comx_baseport = COM1_BASEPORT; // COM Base Port Address
int comx_mask = COM1_MASK; // COM Interrupt Mask 8259
//__________________________________________________________________________
// Command Line Parameter Defaults
int delay_mins = 15; // Delay Time after Comercial power out
int novell_mins = 5; // Further delay to tell Novell
int shutdown_mins = 2; // Further delay till shutdown
int recharge_mins = 12; // Mins to recharge 1 battery use minute
// State-Counters
char state = 'D'; // State - Can be 'D','N', or 'S'
int d_sec; // Default/Override Delay time in secs
int d_cnt; // Dynamic Delay time in secs
int n_sec; // Default/Override Novell time in secs
int n_cnt; // Dynamic Novell time in secs
int s_sec; // Default/Override Shutdown time in secs
int s_cnt; // Dynamic Shutdown time in secs
int r_cnt; // Recharge counter
//__________________________________________________________________________
// Bios Keyboard
//
// Addresses Function
//
// 0x417 Keyboard Shift Status Byte 1
// Bit> 7 6 5 4 3 2 1 0
// Use> INS CAPL NUML SCRL ALT CTRL LSHFT RSHFT
//
// 1=Active 0=Inactive
//
// 0x418 Keyboard Shift Status Byte 2
// Bit> 7 6 5 4 3 2 1 0
// Use> INS CAPL NUML SCRL HOLD PCJR NOT NOT
// down down down down actv click used used
// actv
//
// 0x419 Alt Keypad Entry
//
// 0x41a Pointer to head of keyboard buffer (Value 1E thru 3C)
// 0x41b Pointer to head of keyboard byffer (Value always 0x00)
//
// 0x41c Pointer to tail of keyboard buffer (Value 1E thru 3C)
// 0x41d Pointer to tail of keyboard byffer (Value always 0x00)
//
// Keyboard circular buffer : 16 x 2 byte entries
// Each two byte entry representing a character is composed of
// Byte 1 - The Ascii code value of the character
// Byte 2 - The Scan code key value for the character
//
// Pointer
// 0x41e Posn 1 Ascii Code byte + Scan Code key byte 0x1e
// 0x420 2 0x20
// 0x422 3 0x22
// 0x424 4 0x24
// 0x426 5 0x26
// 0x428 6 0x28
// 0x42a 7 0x2a
// 0x42c 8 0x2c
// 0x42e 9 0x2e
// 0x430 10 0x30
// 0x432 11 0x32
// 0x434 12 0x34
// 0x436 13 0x36
// 0x438 14 0x38
// 0x43a 15 0x3a
// 0x43c 16 0x3c
//
// ...Pointer to Tail....
// : :
// Eg 0x41a 0x41b 0x41c 0c41d 0x41e 0x41f ... etc more buff pairs
// 1E 00 20 00 1B 01
// : [1st Buf Pos]
// : [Ascii Scan]
// :...Pointer to Head..........:
//
//
// Note : If the head pointer = the tail pointer then the circular
// keyboard buffer is empty.
int key; // scan byte + ascii byte
char shift; // Keyboard Shift Status byte at 0x417
// 7 6 5 4 3 2 1 0
// INS CAPL NUML SCRL ALT CTRL LS RS
// 6 byte constants to overlay addresses 0x41A thru 0x41f
//
char ESC_KEY[7] = {0x1E,0x00,0x20,0x00,0x1B,0x01}; //Esc in Buf bytes 1&2
char ENTER[7] = {0x1E,0x00,0x20,0x00,0x0D,0x1C}; //Enter in Buf bytes 1&2
//__________________________________________________________________________
// Working Variables
unsigned scbuff[4000]; // Screen Buffer
unsigned cur_pg,cur_p,cur_s; // Screen Buffer
union REGS regs; // Input-Output Registers
int cts; // Modem Status Register - CTS Bit 4
int rts; // Modem Control Register -0 RTS Bit 1
int s; // second change
int X=20; // Warning panel exit count-down const
int x; // panel exit count_down
//________________________________________________________________________
unsigned TSR_HOTSHIFT = CTRL+LSHIFT; // Your hotkey LSHIFT/RSHIFT/CTRL/ALT
char TSR_HOTSCAN = SCAN_Q; // combo SCAN_A thru SCAN_Z
char tsr_fprint[20] = "Upserver V1.05"; // unique fingerprint string
extern unsigned _tsr_timeslice; // REQUIRED FOR BACKGROUND PROGRAMS
extern int _okbigbuf = 0; // minimise tsr memory requirement
//________________________________________________________________________
main(argc,argv)
int argc;
char *argv[];
{
// Parse Command Line switches (Or use defaults if none supplied)
int j;
for (j=1;j<argc;++j)
{ if (argv[j][0]=='/' && argv[j][2]=='=')
{ switch(toupper(argv[j][1]))
{ case 'D':
delay_mins=atoi(argv[j]+3);
break;
case 'N':
novell_mins=atoi(argv[j]+3);
break;
case 'S':
shutdown_mins=atoi(argv[j]+3);
break;
case 'R':
recharge_mins=atoi(argv[j]+3);
break;
default:
printf("Parameter not recognised\n");
exit(1);
break;
}
}
}
//________________________________________________________________________
// Re-Program the 8250 Serial chip
int_off();
// Open the Divisor Latch Address Bit (DLAB)
//
outp(comx_baseport+3,inp(comx_baseport+3) | 0x80);
// Set the Baud rate
outp(comx_baseport,baud); // LSB bits
outp(comx_baseport+1,baud>>8); // MSB bits
// Close the DLAB
outp(comx_baseport+3,inp(comx_baseport+3) & 0x7f);
// Set Line Control Register Line-Break, Parity, Stopbits, Databits
outp(comx_baseport+3,line_break | parity | databits | stopbits);
// Set (Clear) Modem Control Register
outp(comx_baseport+4,0x00); // Force RTS DTR low
// printf("Modem Control Reg = %x\n",inp(comx_baseport+4));
// Set (Clear) Modem Status Register
outp(comx_baseport+6,0x00); //
// Set the Interrupt Enable Register to enable Modem Status Interrupts
outp(comx_baseport+1,0x08); // 0001000
// printf("Int Enable Reg = %x\n",inp(comx_baseport+1));
// Set Interrupt ID Register
outp(comx_baseport+2,0x00); // Set Bits 2-1 & 0 to 00 0
// printf("Int Ident Reg = %x\n",inp(comx_baseport+2));
//________________________________________________________________________
// Set 8259 Interrupt Controller to enable Serial Port Interrupts.
// Get the Interrupt Mask Register from Port 0x21, Set Bit 4 low
// to Enable the COM 1 serial port interrupt.
// Note: For COM2 it's bit 3.
outp(0x21,inp(0x21) & comx_mask); // 11101111
// printf("IMR Port 0x21 = %x\n",inp(0x021));
int_on();
// Initialise second equivalent comparison constant
// and dynamic second counters
d_sec=delay_mins*60; // Secs for comparison
n_sec=novell_mins*60; // Secs for comparison
s_sec=shutdown_mins*60; // Secs for comparison
d_cnt=d_sec; // delay counter
n_cnt=n_sec; // novell counter
s_cnt=s_sec; // shutdown counter
//________________________________________________________________________
// install tsr
puts("Installing UPSERIAL power down background monitoring");
printf("Parameters D=%i N=%i S=%i R=%i\n",delay_mins,novell_mins,\
shutdown_mins,recharge_mins);
int i;
i=tsr_install(TIMESLICE|TSR_DEBUG);
// if it returns, installation error has occured
//
if(i==1)
puts("Can not load, program already loaded!\n");
else
puts("Failed to install");
}
//________________________________________________________________________
// POPMAIN is a special "reserved name", function, which the
// TSR routines invoke when the hotkeys are pressed or the
// timeslice occurs approx every 1/18th second (If the TIMESLICE
// tsr_install option was set).
void popmain(popmain)
{
// In here on timeslice every 18.2th of a second
// If it's not an even second bounce right out again
// so as to keep the overhead as low as possble
regs.h.ah=0x2c; // Function 42 Get Time
intdos(®s,®s); // Perform DOS 0x21 system call
// returns ch=hh cl=mm dh=ss dl=100'ths
if (s==regs.h.dh)
return 1; // Bounce out
else
s=regs.h.dh;
// On the second
switch(state)
{ case 'D':
d_state(); // D-Time processing
break;
case 'N':
n_state(); // N-Time processing
break;
case 'S':
s_state(); // Show-Time processing
break;
default:
break;
}
return 1; // retain our handler
}
//________________________________________________________________________
// Delay state processing
d_state()
{
cts=inp(comx_baseport+6) & 0x10; // Get MSR Bit 4 CTS
if (cts==0x10) // If the battery is low
{ if (d_cnt==0) // and we've used all the delay time
{ inc_year(); // increment the year
state='N'; // Set Novell state
warn1(); // Display Warning panel
// Flush the fifo keyboard buffer
while (bioskey(1)) // While keys are available
key=bioskey(0); // Read the key
poke(0,0x41A,ESC_KEY,6); // Poke ESC into Keyboard buffer
return 0; // we're outta here
}
else
d_cnt=d_cnt-1; // decrement Delay (second) counter
}
else // If the battery is ok
{ if (d_cnt==d_sec) // and we havent used any battery time
return 0; // we're outta here
else
{ r_cnt=r_cnt+1; // increment recharge counter
if (r_cnt==recharge_mins) // Yes - min is just a RATIO
{ // After r_min secs the battery has
d_cnt=d_cnt+1; // recharged 1 discharge second's worth
r_cnt=0; // clear recharge counter
}
}
}
}
//________________________________________________________________________
// Novell state processing
//
// When the novell buffer covering Rbase shut-down is used up
// Flush the keyboard buffer, stuff the keyboard status byte with
// the Ctrl-Shift needed to clear the Novell error message :
// '>> BATTERY LOW: CITYHALL will go down in 1 minute Press Ctrl Shift'
// and wake Novell up.
n_state()
{
cts=inp(comx_baseport+6) & 0x10; // Get MSR Bit 4 CTS
if (cts==0x10) // If the battery is low
{ if (n_cnt==0) // and we've used all the novell time
// Flush the Bios Keyboard Buffer
{ while (bioskey(1)) // While keys are available
key=bioskey(0); // Read the key
peek(0,0x417,&shift,1); // Get Kb Shift Status byte
shift=shift |= 0x04; // Set CTRL bit ON
poke(0,0x417,&shift,1); // Put it back
poke(0,0x41A,ENTER,6); // Poke ENTER into Keyboard buffer
// Pass on Battery Low via Pin 2 to
// the Mouseport by forcing
// LCR Bit 6 (Set Break) High
outp(comx_baseport+3,inp(comx_baseport+3) | 0x40);
state='S'; // Set Shutdown state
return 0; // we're outta here
}
else
n_cnt=n_cnt-1; // decrement Delay (second) counter
}
else // If the battery is ok
{
rts=inp(comx_baseport+3) & 0x40; // Get LCR Bit 6 (Set Break)
if (rts==0x40) // If we told Novell it was low before
// now commercial power is back again,
// Pass on Battery Ok to
// the Mouseport by forcing Pin 2 via
// LCR Bit 6 (Set Break) Low
outp(comx_baseport+3,inp(comx_baseport+3) & 0xbf);
if (n_cnt==n_sec) // and we havent used any battery time
{
state='D'; // Recharged back to 'D' state
return 0; // we're outta here
}
else
{ r_cnt=r_cnt+1; // increment recharge counter
if (r_cnt==recharge_mins) // Yes - min is just a RATIO
{ // After r_min secs the battery has
n_cnt=n_cnt+1; // recharged 1 discharge second's worth
r_cnt=0; // clear recharge counter
}
}
}
}
//________________________________________________________________________
// Shutdown state processing
s_state()
{
halt(); // Imminent shutdown panel
if (s_cnt==0) // If S-countdown complete
// Signal UPS to shutdown the inverter
// by forcing RTS (Pin 4) high by
// setting Bit 1 of Modem Control Reg
{ outp(comx_baseport+4,inp(comx_baseport+4) | 0x02);
goto loop; // wait for inverter shut-down
}
else
{
s_cnt=s_cnt-1; // countdown
return 0; // exit function
}
loop:
goto loop; // loop till inverter shuts down
// and kills us.
}
//________________________________________________________________________
// The heart of the matter
// increment the year field of the system date on UPS power down warning
//
inc_year()
{
regs.h.ah=0x2a; // Function 42 Get Date
intdos(®s,®s); // Perform DOS 0x21 system call
// returns dh=mm dl=dd cx=yy al=wkday
regs.x.cx = regs.x.cx+1; // Increment year
regs.h.ah=0x2b; // Function 42 Set Date
intdos(®s,®s); // Perform DOS 0x21 system call
// with dh=mm dl=dd cx=yy al=wkday
}
//________________________________________________________________________
// Display management
//
// Processor Halted Display
//
warn1()
{
sound_tone(5000,50,50); // Sound off!
open_display(); // Open display
disp_box(0,31,5,49,21,76);
disp_fillbox(DISP_NORMAL+32,6,50,20,75); // Blank out box
disp_move(7,51);
disp_printf("ATTENTION!!");
disp_move(9,51);
disp_printf("THE BATTERY IN");
disp_move(10,51);
disp_printf("THE UPS IS");
disp_move(11,51);
disp_printf("RUNNING LOW.");
disp_move(13,51);
disp_printf("PLEASE :");
disp_move(14,51);
disp_printf(" - END PROGRAM");
disp_move(15,51);
disp_printf(" - EXIT TO DOS");
x=X; // Set countdown delay
while (x!=0)
{
regs.h.ah=0x2c; // Function 42 Get Time
intdos(®s,®s); // Perform DOS 0x21 system call
// returns ch=hh cl=mm dh=ss dl=100'ths
if (s!=regs.h.dh)
{
disp_move(17,51);
sound_tone(5000,50,50); // Sound off!
disp_printf("%i ",x);
s=regs.h.dh;
x=x-1; // decrement countdown
}
}
close_display(); // Close display
}
halt()
{
sound_tone(5000,50,50); // Sound off!
open_display(); // Open display
disp_box(0,31,5,49,21,76);
disp_fillbox(DISP_NORMAL+32,6,50,20,75); // Blank out box
disp_move(7,51);
disp_printf("FINAL WARNING!");
disp_move(9,51);
disp_printf("UPS SHUT DOWN");
disp_move(10,51);
disp_printf("WILL TAKE PLACE");
disp_move(11,51);
disp_printf("IN %i MINUTES",shutdown_mins);
}
// Diagnostic Display
diag()
{
sound_tone(5000,50,50); // Sound off!
open_display(); // Open display
disp_box(0,31,5,49,21,76);
disp_fillbox(DISP_NORMAL+32,6,50,20,75); // Blank out box
disp_move(6,51);
disp_printf("Battery is ");
disp_move(6,62);
if (cts==0x10)
disp_printf("LOW");
else
disp_printf("OK");
disp_move(8,51);
disp_printf("State=%c Second=%i",state,s);
disp_move(10,51);
disp_printf("D M=%i S=%i C=%i",delay_mins,d_sec,d_cnt);
disp_move(11,51);
disp_printf("N M=%i S=%i C=%i",novell_mins,n_sec,n_cnt);
disp_move(12,51);
disp_printf("S M=%i S=%i C=%i",shutdown_mins,s_sec,s_cnt);
disp_move(13,51);
disp_printf("R R=%i (Ratio)",recharge_mins);
disp_move(15,51);
regs.h.ah=0x2a; // Function 42 Get Date
intdos(®s,®s); // Perform DOS 0x21 system call
// returns dh=mm dl=dd cx=yy al=wkday
disp_printf("System Year = %i",regs.x.cx);
disp_move(17,51);
disp_printf("Line Control Reg3 = %x",inp(comx_baseport+3));
disp_move(18,51);
disp_printf("Modem Control Reg4 = %x",inp(comx_baseport+4));
disp_move(19,51);
disp_printf("Modem Status Reg6 = %x",inp(comx_baseport+6));
disp_move(20,51);
disp_printf(" cts = %x",cts);
bioskey(0); // Operator response
close_display(); // Close display
}
open_display()
{
save_cursor();
disp_open();
peek(disp_base,0,&scbuff,4000);
}
close_display()
{
poke(disp_base,0,&scbuff,4000);
disp_close();
restore_cursor();
}
save_cursor()
{
regs.x.ax=15*256;
int86(0x10,®s,®s);
cur_pg = regs.x.bx;
regs.x.ax=3*256;
int86(0x10,®s,®s);
cur_p = regs.x.dx;
cur_s = regs.x.cx;
regs.x.dx=(24*256)+80;
regs.x.ax=2*256;
regs.x.bx=cur_pg;
int86(0x10,®s,®s);
}
restore_cursor()
{
regs.x.ax=256;
regs.x.bx=cur_pg;
regs.x.cx=cur_s;
int86(0x10,®s,®s);
regs.x.dx=cur_p;
regs.x.ax=2*256;
int86(0x10,®s,®s);
}
|