[Nut-upsdev] Developing the UPS side of the UPS-NUT equation (via usbhid)

Rob Groner rgroner at RTD.com
Fri Mar 7 16:24:49 UTC 2014


Thank you so much for the information.  I'm trying to digest it along with the other docs I have, the example code from Microchip, and a USB HID tutorial I found online.

So, if I understand correctly....  When the device first connects, it sends a huge dump of data (which the usbhid driver shows when you use the debug option) which fully describes the data it is capable of sending.  The usage id's, pages, etc use values from the USBHID spec, but the actual data can be defined as needed by the device (in terms of width and ranges), and then the device assigns an arbitrary (but I'm assuming unique) report ID to the description so that when it needs to send the data, it can do so simply with a Report ID and the described data.  

Am I getting close to the truth?


-----Original Message-----
From: Charles Lepple [mailto:clepple at gmail.com] 
Sent: Thursday, March 06, 2014 11:02 PM
To: Rob Groner
Cc: nut-upsdev at lists.alioth.debian.org
Subject: Re: [Nut-upsdev] Developing the UPS side of the UPS-NUT equation (via usbhid)

On Mar 6, 2014, at 3:55 PM, Rob Groner wrote:

> To make this UPS as easy to use as possible for the end-user who chooses Linux, I figured I would just completely implement the official USB HID UPS spec.  That way no subdriver would be needed, or at least very little.

At the moment, we use USB VID and PID to select which HID-to-NUT tables to consult (since some vendors have "custom" (incorrect) interpretations of standard HID PDC Usages. So at the very least, you would need a skeleton usbhid-ups subdriver which matches your VID:PID combination.

>From there, though, if you follow the standard HID PDC Usage IDs, you should be able to just map the "HID path" (see below) to the corresponding NUT name.

> However, I am having a terrible time making sense of the pages in the NUT guide for writing USB HID drivers, and the USB HID usage table at usb.org.  Just looking at some of the report ID values that I KNOW are working from the UPS example running on the microchip, I can't seem to find their equivalent in either doc.  For instance, I know that the UPS code is sending status info using a Report ID of 0x40 and two bytes of data...but I cannot find anything in the usb.org docs that relates to this.  NUT somehow understands it, though, since it correctly reports the device's status.

The report IDs aren't standardized - the HID Usage IDs are. The Report ID is essentially an opaque, small number to avoid having to send a full list of HID Usage IDs every time a new report is ready.

If you look up Report ID 0x40 in your HID Report Descriptor, you should see a few HID Usage Pages and HID Usage IDs. Usage Pages are just the upper 16 bits of a full Usage ID as seen in the drivers. In the HID Report Descriptor, the Usage Pages and Usages will most likely be represented as hexadecimal. At the end of drivers/libhid.c (hid_usage_lkp[]) is a set of tables that map names to 32-bit hex Usage Page/Usage pairs.

Another slightly strange concept in NUT is the way that HID Collections are represented. Each collection has a Usage Page/Usage associated with it, and for an UPS, these generally start with Page 0x84 (UPS) or Page 0x85 (Battery). They are separated with dots, and the whole thing is termed a "HID path" or similar.

Let's use "UPS.PowerConverter.Output.Voltage" from an MGE Evolution as an example. 

>From lsusb -vvv (run as root; with the Linux kernel HID driver detached if applicable, and the NUT driver stopped):

          Report Descriptor: (length is 1300) ...
            Item(Global): Usage Page, data= [ 0x84 ] 132
                            Power Device Page
            Item(Local ): Usage, data= [ 0x04 ] 4
            Item(Main  ): Collection, data= [ 0x00 ] 0
            Item(Local ): Usage, data= [ 0x16 ] 22
                            Power Converter
            Item(Main  ): Collection, data= [ 0x00 ] 0
            Item(Local ): Usage, data= [ 0x1c ] 28
            Item(Main  ): Collection, data= [ 0x00 ] 0
            Item(Global): Report ID, data= [ 0x1c ] 28 ...
            Item(Local ): Usage, data= [ 0x30 ] 48

Moving backwards from Voltage, we see that the Report ID is 0x1c.

By summing up the size of the features in that Report (the Report size is "sticky" in that it is only specified when it changes between fields), we can find the offset and size of the voltage field. (Sizes are in bits.) I'll let the usbhid-ups driver do the dirty work:

   5.648610	Report[buf]: (10 bytes) => 1c 01 04 57 00 49 00 77 00 3b
   5.648687	Path: UPS.PowerConverter.Output.Voltage, Type: Feature, ReportID: 0x1c, Offset: 48, Size: 16, Value: 119

So an offset of 48 (counting past the report ID 0x1c at the beginning of the buffer) is 48/8 = 6 bytes in, for a value of "77 00" (0x0077 since USB is little-endian), or 119.

Charles Lepple
clepple at gmail

More information about the Nut-upsdev mailing list