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

*** April 2003: this document is currently being revised for the new
                socket scheme and variable naming, so refer to driver
                source when in doubt...

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.

Overall concept
===============

The basic design of drivers is simple.  main.c handles most of the work
for you.  You don't have to worry about arguments, config files, or
anything else like that.  Your only concern is talking to the hardware
and providing data to the outside world.

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

Familiarize yourself with the design of skel.c in the drivers directory.
It shows a few examples of the functions that main will call to obtain
updated information from the hardware.

Essential functions
===================

upsdrv_initups
--------------

Open the port (device_path) and do any low-level things that it may need
to start using that port.  If you have to set DTR or RTS on a serial
port, do it here.

Don't do any sort of hardware detection here, since you may be going
into upsdrv_shutdown next.

upsdrv_initinfo
---------------

Try to detect what kind of UPS is out there, if any, assuming that's
possible for your hardware.  If there is a way to detect that hardware
and it doesn't appear to be connected, display an error and exit.

Otherwise, start calling setinfo() to establish the data you will be
monitoring.

upsdrv_updateinfo
-----------------

Poll the hardware, and update any variables that you care about
monitoring.  Use setinfo() to store the new values.

Do at most one pass of the variables.  You MUST return from this
function or upsd will be unable to read data from your driver.  main
will call this function at regular intervals.

upsdrv_shutdown
---------------

Do whatever you can to make the UPS power off the load but also return
after the power comes back on.  You may use a different command that
keeps the UPS off if the user has requested that with a configuration
setting.

You should attempt the UPS shutdown command even if the UPS detection
fails.  If the UPS does not shut down the load, then the user is
vulnerable to a race if the power comes back on during the shutdown
process.

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

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.

*** This list is going away soon, as the variables are switching to a
    dotted form (like ups.mfr) for expandability.

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

 Name         | Format details 
--------------+--------------------------------------------------------------
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
              | 
--------------+--------------------------------------------------------------

The full list can be found in files like shared-tables.h.

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_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
====================

Sit on it for now.  We're not adding new INFO_ values at the moment.

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