Desc: How to make a new driver to support another UPS
File: new-drivers.txt
Date: 29 July 2002
Auth: Russell Kroll <rkroll@exploits.org>

Smart vs. Contact-closure
=========================

If your UPS only does contact closure readings, then go straight to the
genericups.txt document for information on adding support.  It's a lot
easier to add a few lines to a header file than it is to create a whole
new driver.

Skeleton driver
===============

Familiarize yourself with the design of skel.c in the drivers directory.
All drivers now use that scheme.  The individual files (apcsmart.c,   
belkin.c, and so on) provide support functions, and main.c calls them.

Design principles
=================

As seen in the design document, data is passed between the driver and
upsd in a state file.  Most of the time the data lives in the actual
file, which may be mmapped.

Some systems may use shared memory instead.  Shared memory was the
default for awhile, but that has changed to mmap for portability and
simplicity.  In the event of shared memory, the state file on disk will
only contain the identifier of the shared memory segment.

When not in shared memory mode, the state file must be updated
regularly.  If it is not kept fresh, upsd will detect "staleness" and
will flag it unusable.  The state file is updated by calling 
writeinfo().

The info array
==============

The data lives in an array of type "itype" which is defined in shared.h.
There is no set upper limit on the size of this array aside from what
you tell main at startup.  main will call your upsdrv_infomax()
function, which must return an int.  If your driver could ever need 32
slots, you must return at least 32 here.

Every call to addinfo() consumes an entry in the array.  Plan ahead.
If you run out of room, you will start getting complaints in the syslog.

Data types
==========

To be of any use, you must supply STATUS data.  That is the minimum
needed to let upsmon do its job.  Whenever possible, you should also
provide anything else that can be monitored by the driver.  Some obvious
things are the manufacturer name and model name, voltage data, and so
on.

If you can't figure out some value automatically, use the ups.conf
options to let the user tell you.  This can be useful when a driver
needs to support many similar hardware models but can't probe to see
what is actually attached.

Manipulating the data
=====================

upscommon provides all of the functions you will need to store your
data.  You will not be able to manipulate the info array directly.

 - void addinfo(int type, char *value, int flags, int auxdata)

   type is one of the INFO #defines from shared.h.

   value is an initial setting for this type, if known.  If you don't
   know what it is initially, just set it to "".  You can put the real
   value in there later with setinfo().

   flags again come from shared.h.  See the #define FLAG_* entries.
   Most variables have no flags.  FLAG_STRING and FLAG_ENUM are only
   used with FLAG_RW.

   auxdata depends on what the type is.  This can be a length in the case
   of a read-write string, a ENUM_* value for the INFO_ENUM type, and
   more.

   Example: addinfo(INFO_STATUS, "", 0, 0);

 - void setinfo(int infotype, const char *fmt, ...)

   Set some variable to a given value.  This takes a format string just
   like printf, so you don't have to do any bouncing through temporary
   buffers with snprintf in your driver.

   Example: setinfo(INFO_STATUS, "OL");

 - void setflag(int infotype, int newflags)

   Attach a flag to an existing info type.  This is usually used when
   a variable can be read-only or read-write depending on the hardware.
   What you do is add it with no flags, and then add the new flags if
   it is later determined to be writable.

   Example: setflag(INFO_HIGHXFER, FLAG_RW | FLAG_ENUM);

 - void writeinfo(void)

   Update the state file.  You must call this regularly or upsd will
   stop serving data to clients.

   Do not call this if you can't contact the UPS.  If the UPS becomes
   unreachable, you must let the state file go stale so that upsd and
   the clients won't trust the data any more.

Serial port handling
====================

 - void open_serial_simple(const char *port, speed_t speed, int flags)

   This will open the named serial port at a given speed and will
   set up the control lines according to 'flags'.  This does almost
   no manipulation of the tty layer and probably will not work for
   RS-232 data unless you fix the port up yourself.

 - void open_serial(const char *port, speed_t speed)

   This is like open_serial_simple, but it configures the tty layer for
   data passing.  You will probably need to use this one for any 
   driver that expects to send and receive normal serial data.

 - int upsrecv(char *buf, int buflen, char endchar, const char *ignchars)
   
   This will wait up to 3 seconds for data to arrive from the UPS.
   It ignores any characters in ignchars, and returns when endchar is
   received.  Data is stored in buf, up to buflen bytes.

 - int upsrecvchars(char *buf, int buflen)

   This is a simpler receive function to bring in a fixed number of
   bytes.  A 3 second timeout is also used, but no other parsing of
   characters occurs.

 - int upssendchar(char data)

   Send a single character to the UPS.

 - int upssend(const char *fmt, ...)

   Send a formatted buffer to the UPS.

 - void upsflushin(int expected, int debugit, const char *ignchars)

   Use this to clear out the input queue.  This can be useful when the
   hardware is known to return unexpected data in between polls.

Info array member formatting
============================

The data contained in the value for each member of the array must conform
to the standard or it will be impossible for client programs to parse.  
All values are stored in string form and should be null terminated.

Occasionally there are numeric values shown as nnn.n.  These can actually
be less (nn.n or even n.n).  The format shown is the usual one that will
occur.

The basics
----------

 Name         | Format details 
--------------+--------------------------------------------------------------
INFO_MEMBERS  | Defines how many members of the array exist including the
              | one containing INFO_MEMBERS.  Must be stored in position 0.
              | Must exist unless using shared memory mode - see INFO_SHMID.
              |
              | The easiest way to handle this is to define #INFOMAX to
              | some value and then use it for all references to this count.
              | Many modules have a variable number of variables - leave
              | unused entries set to the type of INFO_UNUSED.
--------------+--------------------------------------------------------------
INFO_SHMID    | Defines the shared memory ID where the information is
              | really found when using shared memory mode.  Must be
              | stored in position 0 with no other members.  The array
              | must start with either INFO_MEMBERS or INFO_SHMID.
--------------+--------------------------------------------------------------
INFO_MSGID    | Defines the SysV message queue ID.  This is used internally
              | by upscommon and upsd.  Normally never accessed directly by
              | drivers.  Stick to the createmsgq() interface to handle this.
--------------+--------------------------------------------------------------
INFO_MFR      | Manufacturer's name.                   Example: APC
--------------+--------------------------------------------------------------
INFO_MODEL    | UPS model name.                        Example: SMART-UPS 700
--------------+--------------------------------------------------------------
INFO_UTILITY  | Voltage that UPS is receiving - nnn[.n] format  Ex: 116.7
--------------+--------------------------------------------------------------
INFO_BATTPCT  | UPS battery charge in percent - nnn[.n] format  Ex: 056.5
--------------+--------------------------------------------------------------
INFO_STATUS   | Current internal UPS power status - contains abbreviations
              | separated by spaces when necessary
              |
              | OFF   - UPS is off (not supplying power)
              | OL    - UPS is online (supplying power from the line/mains)
              | OB    - UPS is on battery
              | LB    - UPS battery is low (with OB = shutdown situation)
              | CAL   - UPS is performing calibration
              | TRIM  - UPS is trimming incoming voltage (APC "SmartTrim")
              | BOOST - UPS is boosting incoming voltage (APC "SmartBoost")
              | OVER  - UPS is overloaded
              | RB    - UPS battery needs to be replaced
              | FSD   - UPS is in forced shutdown state (slaves take note)
              |
              | Things like "OL" and "OB" should not ever occur together.
              | Likewise, "OFF" and "OL" or "OB" are also mutually exclusive.
              |
              | Example for on battery + low battery: OB LB
--------------+--------------------------------------------------------------
INFO_UPSTEMP  | UPS internal temperature in degrees C - nnn[.n] format
              | Example: 037.0
--------------+--------------------------------------------------------------
INFO_ACFREQ   | Incoming frequency in Hz - nn[.nn] format   Example: 60.00
--------------+--------------------------------------------------------------
INFO_LOADPCT  | Load on UPS in percent - nnn[.n] format     Example: 023.4
--------------+--------------------------------------------------------------
INFO_LOWXFER  | UPS transfers to battery when incoming voltage drops below
              | this value - nnn[.n] format                 Example: 103
--------------+--------------------------------------------------------------
INFO_HIGHXFER | UPS transfers to battery when incoming voltage rises above
              | this value - nnn[.n] format                 Example: 132
--------------+--------------------------------------------------------------
INFO_AMBTEMP  | Current temperature outside the UPS.  Not to be confused
              | with INFO_UPSTEMP which is the _internal_ UPS temperature.
              | Degrees C - nnn[.n] format                  Example: 050.8
--------------+--------------------------------------------------------------
INFO_AMBHUMID | Current humidity outside the UPS.  Percentage.
              | nn.nn format                                Example: 25.40
--------------+--------------------------------------------------------------
INFO_CONTACTS | Current status of dry contacts on the UPS.  Hex chars.
              | hh format                                   Example: F0
--------------+--------------------------------------------------------------
INFO_UPSIDENT | Internal UPS identification.                Example: EXPLOITS
--------------+--------------------------------------------------------------
INFO_WAKEDELAY| Seconds that the UPS will wait before powering up.
              | nnn format                                  Example: 020
--------------+--------------------------------------------------------------
INFO_LINESENS | Defines the UPS's sensitivity to line fluctuations.
              | char - L = low, M = medium, H = high, A = auto   Ex: H
--------------+--------------------------------------------------------------
INFO_BATTVOLT | Voltage of the UPS's battery or batteries.  Example: 27.87
--------------+--------------------------------------------------------------
INFO_OUTVOLT  | AC voltage being supplied by the UPS to the load.
              |                                             Example: 114.4
--------------+--------------------------------------------------------------
INFO_BATTDATE | Text string: last date the battery was replaced.
              | Date format may be forced by the UPS hardware.
              |                                             Example: 11/24/00
--------------+--------------------------------------------------------------
INFO_WAKETHRSH| Defines percentage of battery charge required before UPS
              | will power up after powerdown. ie 015 025 035 etc
--------------+--------------------------------------------------------------
INFO_OUTVOLT  | Output voltage of UPS.  Theoretically this can be changed
              | but implemented as RO.  3 digit voltage - ie 230
--------------+--------------------------------------------------------------
INFO_LOBATTIME| Remaining run time when UPS switches to LB.  UPS will set
              | this up when calibrated - ie 005 etc
--------------+--------------------------------------------------------------
INFO_PDNGRACE | Grace time after UPS is ordered to shutdown until it does
              | so.  In seconds - 020, 060, 120 etc
--------------+--------------------------------------------------------------
INFO_ALRMDELAY| Delay between power outage and UPS alerting you (to prevent
              | glitches causing alarms).  Seconds ie 005
--------------+--------------------------------------------------------------
INFO_SLFTSTINT| Intervals between selftests.  In hours
              | 
--------------+--------------------------------------------------------------

This is just the beginning.  See shared-tables.h and shared.h for the
current set of variables.

System level INFO values
------------------------

These are special members of the info array that define some "behind the
scenes" data used by the driver and upsd.  You don't get to access
these directly.

--------------+--------------------------------------------------------------
INFO_INSTCMD  | Indicates support for an instant command in auxdata.
              | Set auxdata to a CMD_* value from shared.h.
--------------+--------------------------------------------------------------
INFO_MSGID    | Internal value used to keep track of the SysV message ID
              | used by upsd and the driver to send things like SET
              | and INSTCMD around.
--------------+--------------------------------------------------------------
INFO_ENUM     | Indicates a possible enumerated value for one of the
              | variables elsewhere in the array.
              | 
              | auxdata is the INFO_ value for that variable.
              | value should be the actual enumerated value possibility.
--------------+--------------------------------------------------------------

New variable support
====================

To add support for a new variable, the following changes must happen:

1. Edit shared.h to give it an INFO_* identifier for the itype array

2. Edit shared-tables.h to match the INFO_* identifier to a text name

3. Make your module supply data for that variable

For sanity's sake, all variables have INFO_ as a prefix as their itype
array identifier.  A variable called FOO would be type INFO_FOO in the
array.

To keep things together, new variables should be submitted for inclusion
into the main source tree.

Message passing support
-----------------------

See commands.txt.

Enumerated types
----------------

Enumerated types consume more than one entry in the array.  The first
position is just a normal variable like any other, with the exception
of the ENUM flag.  To create one of these, use addinfo as follows:

	addinfo(INFO_LOWXFER, "", FLAG_RW | FLAG_ENUM, 4);

Here we're adding a LOWXFER type that's read/write, enumerated, and has
4 possibilities.  Those 4 possibilities must then be added too.

	addinfo(INFO_ENUM, somevalue, 0, INFO_HIGHXFER);

Just call addinfo using that example and it will store the possible
values for you.  Don't worry about flagging which one is "SELECTED" -
upsd handles that by itself when the clients ask about it.

Strings
-------

Add these like any other variable, only you tack on the RW and STRING
flags, along with the length.

	addinfo(INFO_UPSIDENT, "", FLAG_RW | FLAG_STRING, 8);

Easy.

Instant commands
----------------

These work just like enumerated variables seen above.

	addinfo(INFO_INSTCMD, "", 0, CMD_FPTEST);
