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

Charles Lepple clepple at gmail.com
Tue Jan 22 04:25:47 UTC 2008


Hi Michael,

On Jan 21, 2008 7:59 AM, Michael Tokarev <mjt at tls.msk.ru> wrote:
> 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).

It sounds like you may want to talk with Alexey Sidirov to coordinate
support for this device.

Can you post the output of 'lsusb -vvv' (run as root) for this device?
(You will get a little more information from lsusb if you run
'usbhid-ups' once with the "-x explore" option, although usbhid-ups
will probably not tell us much since they seem to not follow the USB
Power Device Class (PDC) HID protocol.)

Since it is the same protocol as the serial powercom driver, you can
use the same technique as in the megatec and megatec_usb code: link
the core powercom code with the serial functions for the regular
powercom driver, and with USB HID code in powercom_usb.c (for
instance).

There are two ways to write the USB interface code. One is to use
libusb functions, which are portable to other OSes like FreeBSD,
NetBSD, OS X, and potentially Solaris. The other way (not recommended,
but may be easier) is to just use the ioctl() calls that you found
from strace.

The first method is used by megatec_usb and usbhid-ups. The second
method is used by energizerups.

> 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

You will have to do a little reverse engineering here to figure out
which fields are being passed in, and what they mean. I think that
report_type, report_id and usage_code are passed in, and the others
are returned by this ioctl() call.

> 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

This is most likely an interrupt read over whatever the input pipe is.

> 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

This is just getting some basic information about the device.

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

These last two calls seem to be looking for HID usage codes that are
not supported by this particular device.

> 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

Not sure exactly what's going on here, but it looks like they are
writing to the device (In HIDIOC + S + USAGE/REPORT, the 'S' is for
"set", and 'G' stands for "get").

-- 
- Charles Lepple



More information about the Nut-upsdev mailing list