[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