[Nut-upsdev] blazer_usb: compatibility with Ippon BackPro UPSes (Phoenix Power Co., Ltd usb-to-serial controller)

Alexey Loukianov mooroon2 at mail.ru
Wed Jan 6 15:56:56 UTC 2010


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Good day to all.

In one of the offices I'm administering there are about 15 UPS marked with the
brand "Ippon BackPro". This brand is widely popular in Russian Federation, while
by itself is a 'Made in PRC' megatec clone. Depending on the model this UPSes
have got either one standard serial RS-232C interface (cheaper models like Ippon
BackPro 400 and Ippon BackPro 600/700 that were manufactured back in about year
2005 or earlier) or dual-port control interface: one serial and one USB.

Ippon UPSes are by default shipped with Winpower java-based monitoring and
controlling solution which is said to be compatible with windows of any kind,
MacOS X and Linux. On workstations I used to use this software and it proved to
work quiet reliable under Windows XP while still require some additional
configuration to power the workstations down correctly before LowBattery
condition arises. It was always a big problem to run this software on server in
that office because of the server hardware that is available there. So-called
"server" is in reality an older workstation with the Celeron 2Ghz CPU and 128Mb
RAM acting as an office router and local mail server running CentOS4. So it was
almost impossible to run Winpower on this box due to Java memory requirements.

The only serial port available on this "server" box is occuped by local-specific
hardware (PBX logs control), so the only choice to connect UPSes to this server
was to use USB.

Back in 2007 I've been trying to configure NUT to use megatec_usb driver, but
that attempts were totally unsuccessful: driver was showing up "no response"
from UPS. Recently I've been able to do "upgrade" of a server adding additional
256Mb of RAM and it allowed me to install and run linux version of Winpower. It
was a big surprise for me that Winpower on linux successfully detects and
controls UPS connected through USB interface but there were some caveats
including huge CPU usage by Winpower main java process and tonns on "Process
java(1234) uses usb device without first claming it" showing up in dmesg. So I
have decided to give NUT one more chance.

Prebuilt binary RPM available for CentOS4 (nut-2.2.0-4.el4) ships with older
megatec_usb driver that still don't work with Ippon UPSes connected through USB.
So I have fetched latest revision of NUT from SVN and tried both megatec
protocol usb drivers from it: megatec_usb and blazer_usb.

megatec_usb driver in debug mode showed up that there's some king of connection
with UPS but correct status readings were obtained once in a 5-10 tries.
blazer_usb showed up no connections with UPS at all. As Winpower on the same
linux box was working perfectly my decision was to try to figure out what are
the differences in the way the USB UPS communication happens between the
Winpower, megatec_usb and blazer_usb.

My best friends in this process were DJ Java Decompiller 3.10 and IDA Pro 4.
Decompilation of Winpower java modules showed up that it uses native system
binary libraries to handle actual accesses to USB bus (System.loadLibrary),
which is called jusbMaxOSX/jusbMaxOSXi386 on MacOS X, jusb.dll on windows and
libusbLinux/libusbLinuxamd on i686/x86-64 linux.

Communication with UPS happens using megatec protocol through the single
function OrderUPS with C-style prototype:

int OrderUPS(char* cmd, char* return_buf, int return_buf_size, int timeout);

This prototype is perfectly self-describing, so I went on to IDA Pro and opened
up libusbLinux.so in it. Quick examination of this library showed up that this
is a PIC-enabled lib and is in fact a mix of libusb-0.1.x with some
Winpower-related funtions including OrderUPS. I'm not sure about the legal
status of this as AFAIR libusb-0.1.x is GNU GPL licensed so Winpower creators
should release full source of modified libusb they have used in their product.
Well, this might be just another GNU GPL violation example, but let me continue
to the interesting part. Disassembly of OrderUPS functions showed up that the
base algorithm of communications with USB UPS is as following:

1. Explore usbfs, find first device with ID 06da:0003 (Phoenixtec Power Co., Ltd).
2. Try to open this device using libusb.
3. In case of failure try to detach kernel driver from this device using libusb
and retry to open it.
4. If the device was opened up correctly set the configuration to 1 on it using:
usb_set_configuration(devfd, 0x1);
5. Send command to device using usb_control_msg function in 8-bytes chunks as
following:

- ------------------- cut --------------------------------------
int index = 0;
int count = strlen(cmd);
while(index < count) {
	int tosend = ((count - index) > 8) ? 8 : (count - index);
	char tmp[8];
	memset(tmp, 0, sizeof(tmp));
	memcpy(tmp, &cmd[index], tosend);
	int ret = usb_control_message(devfd, 0x21, 0x9, 0x2, 0, tmp, tosend, 1000);
	if((ret <= 0) || (ret != tosend)) {
		return_buf[0] = 0;
		return -1;
	}
	index += tosend;
}
- ------------------- cut --------------------------------------

6. Read back return string from UPS in one chunk using usb_interrupt_read:

- ------------------- cut --------------------------------------
char tmp[256];
memset(tmp, 0, sizeof(tmp));
int ret = usb_interrupt_read(devfd, 0x81, tmp, max_return_size, timeout);
if(ret <= 0) {
	return_buf[0] = 0;
	return -1;
}
int max = (max_return_size > 256) ? 256 : max_return_size;
memcpy(return_buf, tmp, max);
return_buf[max] = 0;
usb_close(devfd);
return max;
- ------------------- cut --------------------------------------

The differences between the way Winpower works with UPS and NUT works with UPS
are as follows:

1. megatec_usb and blazer_usb both read up data in 8-byte chunks. Winpower do it
in one large chunk.
2. blazer_usb tries to flush read buffer of controller before sending a command.
3. Both megatec_usb and blazer_usb use different USB message value in
usb_control_message call (Winpower uses 0x2, while blazer_usb uses 0x200 and
megatec_usb uses "ReportId+(0x03<<8)").

So I've changed blazer_usb.c to read up data in one large chunk instead of
8-byte chunks, to pass 0x2 instead of 0x200 in the usb_control_message call and
removed buffer-flushing code completely. At first glance blazer_usb still wasn't
working after this change but experiments showed up that this usb-to-serial
implementation hangs when max_return_size is set larger that 64 bytes. Limiting
return buffer size to 64 bytes in usb_interrupt_read call made blazer_usb to
work perfectly with Ippon UPS.

As for hangs: there are several ways to un-hang hanged UPS. You may replug USB
cable, you may reboot the server and you may use appropriate ioctl on usbfs node
to do a port-reset (libusb usb_reset also will do).

Side note: the device is not working with Winpower after it was hanged by
blazer_usb or megatec_usb.

In the end I've got working blazer_usb on CentOS4 with stock libusb (0.1.8). Key
point was to read up data in one 64 byte chunk. It is still unclear why does the
data got corrupted in megatec_usb when read up in 8-bytes chunks, and why does
the device hang when using original blazer_usb with buffer-flush and 8-byte
chunks read.

To try to make things more clear I had reverted blazer_usb.c back to SVN HEAD
and removed from the code the part that flushes the device buffer before reading
up a command. The result was the device returned back one correct reply on Q1
query but then hanged. Running blazer_usb under ltrace showed that there were
some additional characters left in device buffer after the '\r', so I had added
buffer flush code back but placed it after the buffer read. Nothing changed,
device still locked up after returning one correct answer on "Q1\r" query.

Next proposal was that libusb version shipped with RHEL4/CentOS4 is a bit
outdated and might be somewhat buggy. So I had downloaded libusb-0.1.12 SRPMS
from CentOS5, built binary RPM and installed it. After that the modified
blazer_usb with buffer flush taking place after the reading of reply no longer
made the device hang.

I had reverted blazer_usb back to SVN HEAD once again and it also started to
work normally. However megatec_usb data corruption on receive wasn't cured by
libusb update.

So, the conclusion is as following:

1. blazer_usb driver from SVN HEAD works correctly with USB Ippon UPSes on
CentOS4, but require libusb version update. Version 0.1.12 from CentOS5 will do.

2. megatec_usb driver somehow fails to read up correct data from USB device. The
cause of data corruption is unclear for me as it seems that the steps
megatec_usb driver takes to read up data are almost the same as blazer_usb take.

3. It is possible to make blazer_usb work with RHEL4 version of libusb (0.1.8),
but it require modifications to the way it work. The patch that works on my
setup is attached to this message.

Hope this info will help people to get Ippon UPSes with USB connection to a
server work normally in conjunction with RHEL4/CentOS4.

- -- 
Best regards,
Alexey Loukianov                          mailto:mooroon2 at mail.ru
System Engineer,                            Mob.:+7(926)218-1320
*nix Specialist

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iQEcBAEBAgAGBQJLRLLIAAoJEPB9BOdTkBUL0P0H/2rkUW5lKPR3whE5OKhMdyOh
XrrEh7gJjeIYUUjj+hPWgEdCBgwp7D/o3YpAh7PaCtysodqMVtgrPgt7pmOoRkQR
RAFNarQnmx+ctRffbd/+wTpHWglGJtsmuRw1Ontj9G57117F2k4hDphOsjJPWGNw
VTwKfQi8yXqqCK8wKSH7xj5iMmbbgHso+Tde5nue0kHdEEkSqkW54ZGAwtInoMMs
P2q2qfxyKC/hDJkGIVmTC74BtGVmSUQntwbKQ+VCKMGR0NYV0T/tTKNcoRQeJRMz
UILdWrfDZc8h4MBl7eh15TU5A8gPZX6asPIEx0qPSUX0c39fqIv0D53uZYK9T5o=
=iJR2
-----END PGP SIGNATURE-----
-------------- next part --------------
A non-text attachment was scrubbed...
Name: blazer_usb.c.patch
Type: text/x-patch
Size: 2586 bytes
Desc: not available
URL: <http://lists.alioth.debian.org/pipermail/nut-upsdev/attachments/20100106/f46cbbfe/attachment.bin>


More information about the Nut-upsdev mailing list