Desc: How to make a new driver to support another UPS
File: new-drivers.txt
Date: 14 July 2003
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.

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.

This is usually a good place to create variables like ups.mfr,
ups.model, ups.serial, and other "one time only" items.

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

Poll the hardware, and update any variables that you care about
monitoring.  Use dstate_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.

If your UPS hardware is slow or brain-damaged, you might use an 
odd/even approach to this function.  Half the time, set a flag and
return.  The other half, clear the flag and actually do the poll.

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.

Flags for main.c
================

Set experimental_driver to 1 if your driver is potentially broken.  This
will trigger a warning when it starts so the user doesn't take it for
granted.

Setting broken_driver to 1 will cause main to print an error and exit.
This is only used during conversions of the driver core to keep users
from using drivers which have not been converted.  Drivers in this state 
will be removed from the tree after some period if they are not fixed.

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

All status data lives in structures that are managed by the dstate
functions.  All access and modifications must happen through those
functions.  Any other changes are forbidden, as they will not pushed out
as updates to things like upsd.

Adding variables
----------------

	dstate_setinfo("ups.model", "Mega-Zapper 1500");

Many of these functions take format strings, so you can build the new
values right there:

	dstate_setinfo("ups.model", "Mega-Zapper %d", rating);

Setting flags
-------------

Some variables have special properties.  They can be writable, and some
are strings.  The ST_FLAG_* values can be used to tell upsd more about
what it can do.

	dstate_setflags("input.transfer.high", ST_FLAG_RW);

Status data
===========

UPS status flags like on line (OL) and on battery (OB) live in
ups.status.  Don't manipulate this by hand.  There are functions which
will do this for you.

        status_init() - before doing anything else

        status_set(val) - add a status word (OB, OL, etc)

        status_commit() - push out the update

Possible values for status_set:

 OL     - On line
 OB     - On battery (inverter is providing load power)
 LB     - Low battery
 RB     - The battery needs to be replaced
 BYPASS - UPS bypass circuit is active - no battery protection is available
 CAL    - UPS is currently performing runtime calibration (on battery)
 OFF    - UPS is offline and is not supplying power to the load
 OVER   - UPS is overloaded
 TRIM   - UPS is trimming incoming voltage (called "buck" in some hardware)
 BOOST  - UPS is boosting incoming voltage

Anything else will not be recognized by the usual clients.  Coordinate
with me before creating something new, since there will be duplication
and ugliness otherwise.

Note: upsd injects "FSD" by itself following that command by a master
upsmon process.  Drivers must not set that value.

Staleness control
=================

 - dstate_dataok() 

   You must call this regularly if polls are succeeding.  A good place
   to call this is the bottom of upsdrv_updateinfo().

   If you're not talking to a polled UPS, then you must ensure that it
   is still out there and is alive before calling dstate_dataok().  Even
   if nothing is changing, you should still "ping" it or do something
   else to ensure that it is really available.  If the attempts to
   contact the UPS fail, you must call dstate_datastale() to inform the
   server and clients.

 - dstate_datastale()

   You must call this if your status is unusable.  A good technique is
   to call this before exiting prematurely from upsdrv_updateinfo().

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

The two open_serial functions will handle locking for you.  You should
use them unless you have really strange needs in your driver.

 - 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.

Variable names
==============

PLEASE don't make up new variables and commands just because you can.
The new dstate functions give us the power to create just about
anything, but that is a privilege and not a right.  Imagine the mess
that would happen if every developer decided on their own way to
represent a common status element.

Check new-names.txt first to find the closest fit.  If nothing matches,
contact the upsdev list or mail me directly, and we'll figure it out.

Patches which introduce unlisted names may be modified or dropped.

Message passing support
=======================

See commands.txt.

Enumerated types
================

If you have a variable that can have several specific entries, it is
enumerated.  You should add each one to make it available to the client:

	dstate_addenum("input.transfer.low", "92");
	dstate_addenum("input.transfer.low", "95");
	dstate_addenum("input.transfer.low", "99");
	dstate_addenum("input.transfer.low", "105");

Writable strings
================

Strings that may be changed by the client should have the ST_FLAG_STRING
flag set, and a maximum length byte set in the auxdata.

	dstate_setinfo("ups.id", "Big UPS");
	dstate_setflags("ups.id", ST_FLAG_STRING | ST_FLAG_RW);
	dstate_setaux("ups.id", 8);

If the variable is not writable, don't bother with the flags or the
auxiliary data.  It won't be used.

Instant commands
================

If your hardware can support a command, register it.

	dstate_addcmd("load.on");
