Hardware Driver

I bought an Energizer UPS in 2003, in response to the Great Blackout; while rotating/rolling blackouts remained a possibility, I did not wish to risk bringing my primary server back up online without the ability to survive a power outage with an orderly shutdown.

The UPS came with a Windows-only control program and a USB port. What follows is the README file that I wrote after I successfully managed to connect to the UPS from Linux with a simple control program I wrote, that can shut down Linux safely when the power goes out. You can download the package by right-clicking this link. The package is released under the GNU open-source license.

I DO NOT RECOMMEND THAT YOU USE THESE TOOLS ON YOUR SYSTEM. Consider using instead NUT, the Network UPS Tools package, which contains my driver for the Energizer UPS family. That said, these tools may be useful for diagnosing/debugging problems that are harder to identify when the whole NUT package is loaded and running.

NEWUpdate (August 16, 2005): I recently picked up yet another Energizer UPS at a local store... it was too cheap (CAD 29.99) to leave behind, and I thought I'd use it to experiment, perhaps to find out why my driver was reportedly misbehaving on Linux 2.6.11.
Well, to make a long story short, it appears that the good folks at Energizer completely changed the interface in newer versions of their UPS. The model numbers haven't changed (the model I picked up is an ER-HM450, which was reportedly working with my driver previously) but the Windows driver software is very different and the USB behavior, while probably still nonstandard, is also different.
In other words, my Energizer driver will NOT work with recent versions of this UPS.
For reference, the UPS that does work comes with a CD-ROM that bears the label (tiny black print on a dark brown blackground), "Personal edition Version 4.2". The newer UPS that has the altered interface has "Personal edition Version 5.0" on the CD-ROM.

Update (November 13, 2004): This code is now tested with kernel version 2.6.9. I implemented a nasty but functional workaround for a problem that caused the kernel to eat the leading 8 characters from the UPS response string. I also submitted corresponding changes to Russell Kroll, maintainer of the NUT package; hopefully, the new driver will find its way into the next NUT release.

Update (January 13, 2004): It appears that it is not necessary after all to patch the kernel in order for this driver to work, only to recompile it with the correct kernel options. Thanks to Don Gaffney for pointing my nose in the right direction!

Update (January 13, 2004): Don Gaffney also reports that my code works well with the ER-HM450.

Update (September 2, 2003): A driver I wrote for the Energizer UPS is now officially part of the NUT (Network UPS Tools) system (starting with version 1.5.4). It is recommended that you use NUT instead of the experimental code below with your Energizer UPS.

Update (August 30, 2003): The code also works with the ER-HMOF600.


This is a very simplistic implementation of a UPS monitor for the Energizer ER-OF800 UPS.

THIS CODE IS PRELIMINARY AND EXPERIMENTAL. SUITABILITY FOR ANY PARTICULAR PURPOSE IS NOT GUARANTEED. USE IT AT YOUR OWN RISK.

In other words, this is not a finished product, just a "first crack" at making something work. It was also put together without the benefit of vendor support or vendor-supplied documentation.

COMPATIBILITY

This implementation works with Linux kernel versions 2.4.x (tested most recently with 2.4.27) and 2.6.x (tested with 2.6.9.) A problem was found with the 2.6.x kernel eating some of the characters sent by the UPS, but the code now contains the necessary workaround.

INSTALLATION

1. Patching the kernel

Patching the kernel is not necessary. However, you may have to recompile the Linux kernel with the correct options. Specifically, you must be using a kernel that is compiled with the CONFIG_USB_HID and CONFIG_USB_HIDDEV options on, but the CONFIG_USB_HIDINPUT option must be off.

This implementation requires access to the UPS through the hiddev interface. Unfortunately, the Linux kernel refuses to install an hiddev interface for an HID that it recognizes as an input device. Therefore, the kernel must be patched. See the BACKGROUND section below for more information. The patch consists of commenting out a single line in hiddev.c, after which you can recompile your kernel modules (make modules), and then reinstall them (make modules_install; depmod -a). WITHOUT THIS CHANGE, THE CODE WILL NOT FUNCTION!

2. Loading modules

Make sure you have the following driver modules loaded:

hid
usb-uhci
usbcore

(You may also have the alternate module uhci loaded in place of usb-uhci, or perhaps another host controller module that is more appropriate for your computer.)

As you load these modules, other modules may be loaded also, but their presence is not required.

3. Checking for the UPS

Verify that the UPS is present on your system. For this, you may want to mount the "preliminary USB filesystem" if it has not been mounted already:

mount none /proc/bus/usb -t usbfs

After this, you can view /proc/bus/usb/devices. The following lines should be present there:

T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 14 Spd=1.5 MxCh= 0
D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
P: Vendor=0f03 ProdID=0001 Rev= 0.01
S: Manufacturer=Ver 1.0
S: Product=USB To RS232 Interface (V1.0) BaudRate 2400bps
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=hid
E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=10ms

There may be slight differences of course, depending on your USB topology.

4. Compiling the suite

There's nothing to configure. Just do a 'make' in the directory where you unpacked this archive. This should compile qups.c.

If this step fails, you're on your own. Perhaps your system doesn't have the necessary kernel headers installed. Perhaps you have a broken compiler. Perhaps evil spirits possess your computer. Perhaps what I did works only on my system. Whatever it is, I probably won't be able to help you.

5. Testing the UPS

The qups program in this suite talks to the UPS. You can, for instance, request the identification string from the UPS by typing, as root:

./qups /dev/usb/hid/hiddev0 I

If all goes well, the UPS should respond with

#Energizer ER-OF800 A0

 

6. What, no /dev/usb/hid/hiddev0?

What, you have no /dev/usb/hid/hiddev0? Why, that's what mknod is for of course. Just do this (as root):

mkdir /dev/usb
mkdir /dev/usb/hid
mknod /dev/usb/hid/hiddev0 c 180 96
mknod /dev/usb/hid/hiddev1 c 180 97
mknod /dev/usb/hid/hiddev2 c 180 98
mknod /dev/usb/hid/hiddev3 c 180 99

This shall create four hiddev devices. (You don't need to make the directories if they already exist of course.) Please note that I do not use a /dev file system. If you do, the hiddev devices may appear automagically on your system.

There's also a chance that your /dev/usb/hid/hiddev0 is already in use, and that your UPS appeared as another device, say, /dev/usb/hid/hiddev1. Remember, /var/log/messages (or wherever your system logs important messages) is your friend.

7. Using the software

At this point, you can talk to the UPS. Just to make sure, do a

./qups /dev/usb/hid/hiddev0 Q1

to obtain a power reading. If this works, you're ready to start the shell script qmon.

Examine qmon first. Make sure the variable UPS is set to the correct hiddev device name. All other settings should probably left alone, except for the actual command lines near the very end. Because I'm a cautious person, I decided not to release a script that actually attempts to shut down your system or power down your UPS. As shipped, the script merely prints these commands on its standard output:

echo ./qups $UPS S002R000
echo shutdown -hf now

Removing the 'echo' part will make these commands "live". Make sure you know what you're doing! I accept no responsibility for unexpected shutdowns caused by this script.

If you feel truly courageous, you may put qmon in the background:

./qmon </dev/null >/dev/null 2>&1 &

The script will run until killed, logging power events as appropriate. (On my system, routine events go to /var/log/messages, abnormal events to /var/log/syslog.)

That's it. Please remember that all this code is preliminary and should be considered experimental.

BACKGROUND

This UPS has an RJ-48 data connector and a USB cable. The software CD that is packaged with the device contains a Windows control program, nothing more. No information is available on the programming interface of the device.

The control program, however, can also control a UPS over a serial port. By capturing its output over a virtualized serial port, I was able to ascertain that it uses a variation of a fairly standard protocol, the Megatec protocol.

With the USB cable, the device is registered as a Human Interface Device. It does not, however, appear to conform to the HID specifications for UPSs. Instead, it announces itself as a "game pad" device, with the following product name:

USB To RS232 Interface (V1.0) BaudRate 2400bps

This presented several problems. First, the HID driver in the Linux kernel (2.4.21) that I was using did not register as an hiddev a device that is recognized as an input device (e.g., a keyboard, a mouse, or a game pad.) I initially solved this problem by a simple kernel modification that removes this aggressive sanity check from hiddev.c.

$ diff drivers/usb-orig/hiddev.c drivers/usb/hiddev.c
580c580
< if (!IS_INPUT_APPLICATION(hid->application[i]))
---
> //vtt if (!IS_INPUT_APPLICATION(hid->application[i]))

NB: This patch is not required if you compile the kernel with CONFIG_HID_USBINPUT off. Thanks to Don Gaffney for this suggestion. (I also tried to communicate with the UPS through an input event interface, which would have made it unnecessary to have an hiddev interface, but I could not make it work.)

With a properly compiled kernel in place and the hid, usbcore, and USB host controller driver modules loaded, the UPS should become visible in /proc/bus/usb/devices. (Do a mount none /proc/bus/usb -t usbfs if your "preliminary USB file system" is not mounted.)

This half of the problem solved, another one remained: how to communicate with this misbegotten UPS? A test program I wrote, based on Vojtech Pavlik's event device test program, revealed that the UPS provides two HID reports, each with 64 usages, each of which can have two values, 0 and 1. (The input interface read these as 64 keys and 64 LEDs.)

Fortune favored me it seems, because at this point I came across an ST Microelectronics application note (AN1071) that explained how a particular ST circuit can be used to implement a low-speed USB-to-RS232 converter as an HID. I do not know if this UPS actually uses that ST chip or not, but it certainly looks like it! The ST specs suggest that what is being sent and/or received from the device are 8-byte blocks of data, and that data can be sent through the HID control interface.

Could this be it? Could it be that 8 character bytes are encoded, bit-by-bit, as 64 HID usages, and that when the device replies, it shows up as a series of HID events each signifying the change of an individual bit in an 8-character, 64-bit buffer?

To make a long story short, that is precisely how this interface works.

REFERENCES

The USB Web site: http://www.usb.org/
The HID page: http://www.lvr.com/hidpage.htm
USB on Linux: http://www.linux-usb.org/
Megatec Protocol: http://www.exploits.org/nut/library/protocols/megatec.html
ST AN1071: http://eu.st.com/stonline/books/ascii/docs/8530.htm