[Nut-upsdev] help writing a usb hid driver for existing ups

Michael Tokarev mjt at tls.msk.ru
Mon Jan 21 12:59:44 UTC 2008


I've got a Powercom Imperial UPS, with an internal
USB<=>serial converter.  It implements the same
protocol as other powercom devices implements, but
only when talking over serial port (there's no such
port on the device).  Someone wrote a draft version
of usbserial driver for it, and the UPS works with
this kernel-level driver and with powercom driver
from nut (using /dev/ttyUSBx device).

The UPS comes with OEM program, upsmon, that talks
to the device using /dev/hiddevX device node, i.e.
using USB HID protocol.  I captured some (s)traces
of it when at work(*).

The question is how to write proper driver for it
(because using kernel-mode usbserial driver looks
a bit.. too much and ugly).  I don't understand
how USB and HID works.

(megatec_usb doesn't work with it - the serial
protocol is different).

What's the place to start with all this stuff,
having in mind that I don't want to learn all
the HID stuff - just some basics (probably just
an example) necessary to implement the driver.

(*) their program is looping doing the following
at regular intervals:

654   time(NULL)                        = 1200921228
654   ioctl(3, HIDIOCGUSAGE, {report_type=1, report_id=0, field_index=0, usage_index=0, usage_code=ffa00001, value=240}) = 0
654   select(4, [3], NULL, NULL, {3, 0}) = 1 (in [3], left {2, 990000})
654   read(3, "\1\0\240\377\360\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0", 512) = 64
654   time(NULL)                        = 1200921228
654   ioctl(3, HIDIOCGUSAGE, {report_type=1, report_id=0, field_index=0, usage_index=0, usage_code=ffa00001, value=240}) = 0
654   select(4, [3], NULL, NULL, {3, 0}) = 1 (in [3], left {2, 990000})
654   read(3, "\1\0\240\377\360\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0\1\0\240\377\0\0\0\0", 512) = 64
654   time(NULL)                        = 1200921228
...

with some init stuff before (fd#3 is /dev/hiddev0).
What it reads looks pretty much the same as the stuff
returned by similar UPS connected over normal serial
port (but 4x more - there are 4 identical blocks of
data read, each of 16bytes in size).

Here's the init stuff:

653   open("/dev/hiddev0", O_RDONLY)    = 3
653   ioctl(3, HIDIOCAPPLICATION, 0)    = -6291455
653   ioctl(3, HIDIOCAPPLICATION, 0)    = -6291455
653   ioctl(3, HIDIOCGVERSION, 0xbfb09fdc) = 0
653   write(1, "hiddev driver version is 1.0.4\n", 31) = 31
653   ioctl(3, HIDIOCGDEVINFO, 0xbfb0a480) = 0
653   ioctl(3, HIDIOCAPPLICATION, 0)    = -6291455
653   write(1, "HID: vendor 0xd9f product 0x2 version 0x0 applications [1]: ffa00001\n", 69) = 69
653   write(1, "HID: bus: 2 devnum: 4 ifnum: 0\n", 31) = 31
653   ioctl(3, 0x81004806, 0xbfb0a380)  = 33
653   write(1, "UPS HID device name: \"POWERCOM CO., LTD. USB to Serial\"\n", 56) = 56
653   ioctl(3, HIDIOCINITREPORT, 0)     = 0
653   ioctl(3, HIDIOCGUSAGE, {report_type=3, report_id=ffffffff, field_index=0, usage_index=0, usage_code=850089, value=0}) = -1 EINVAL (Invalid argument)
653   ioctl(3, HIDIOCGUSAGE, {report_type=3, report_id=ffffffff, field_index=0, usage_index=0, usage_code=8400ff, value=0}) = -1 EINVAL (Invalid argument)
653   ioctl(3, HIDIOCSUSAGE, {report_type=3, report_id=0, field_index=0, usage_index=0, usage_code=ffa00003, value=95}) = 0
653   ioctl(3, HIDIOCSREPORT, {report_type=3,report_id=0,num_fields=0}) = 0
653   ioctl(3, HIDIOCSUSAGE, {report_type=3, report_id=0, field_index=0, usage_index=1, usage_code=ffa00003, value=4}) = 0
653   ioctl(3, HIDIOCSREPORT, {report_type=3,report_id=0,num_fields=0}) = 0
653   ioctl(3, HIDIOCSUSAGE, {report_type=3, report_id=0, field_index=0, usage_index=2, usage_code=ffa00003, value=0}) = 0
653   ioctl(3, HIDIOCSREPORT, {report_type=3,report_id=0,num_fields=0}) = 0
653   ioctl(3, HIDIOCSUSAGE, {report_type=3, report_id=0, field_index=0, usage_index=3, usage_code=ffa00003, value=0}) = 0
653   ioctl(3, HIDIOCSREPORT, {report_type=3,report_id=0,num_fields=0}) = 0
653   ioctl(3, HIDIOCSUSAGE, {report_type=3, report_id=0, field_index=0, usage_index=4, usage_code=ffa00003, value=3}) = 0
653   ioctl(3, HIDIOCSREPORT, {report_type=3,report_id=0,num_fields=0}) = 0
653   ioctl(3, HIDIOCSUSAGE, {report_type=2, report_id=0, field_index=0, usage_index=0, usage_code=ffa00002, value=1}) = 0
653   ioctl(3, HIDIOCSUSAGE, {report_type=2, report_id=0, field_index=0, usage_index=1, usage_code=ffa00002, value=1}) = 0
653   ioctl(3, HIDIOCSREPORT, {report_type=2,report_id=0,num_fields=0}) = 0

As I stated above, I don't know anything about all
this HID stuff and how it works, even the basic
principles...

Thanks!

/mjt



More information about the Nut-upsdev mailing list