Discussion:
[libhid-discuss] Misunderstanding input and output paths
Gary Briggs
2010-04-20 06:50:09 UTC
Permalink
I have a DCDC-USB smart PSU from mini-box.com:
http://www.mini-box.com/DCDC-USB

It appears to be an HID device, and the source code available on that
website uses HID code on windows.

I'm trying to write code to interface with this device, and I'm basically
just confused. The output of lsusb -vvv for that device is here:
http://icculus.org/~chunky/stuff/dcdcusb/dcdcusb-lsusb-vvv.txt

I'm reading the sample sourcecode on the libhid page, and it tells me
how to work out the path to access the input and output paths. That
seems predicated on a single input and output path, but the lsusb dump
above contains a bunch of each. Right at the end it mentions a specific
pair of endpoints [0x81, 0x01] but I'm not clear whether that actually
has any bearing on the path as libhid would find it useful.

I've written some code [mainly copied outright from the libhid test
example], here:
http://icculus.org/~chunky/stuff/dcdcusb/main.c

The output from that program is:
http://icculus.org/~chunky/stuff/dcdcusb/set_output_report_output.txt

Overall, I'm just confused as to what's going on or how to progress and
make this work. If anyone has any suggestions as to what I should do
from here, I'd greatly appreciate it.

Thank-you very much,
Gary (-;
Charles Lepple
2010-04-20 11:49:50 UTC
Permalink
Post by Gary Briggs
I'm reading the sample sourcecode on the libhid page, and it tells me
how to work out the path to access the input and output paths. That
seems predicated on a single input and output path, but the lsusb dump
above contains a bunch of each. Right at the end it mentions a
specific
pair of endpoints [0x81, 0x01] but I'm not clear whether that actually
has any bearing on the path as libhid would find it useful.
The sample only refers to a single input and output path, but the
original motivation for the "path" concept was that HID devices such
as UPSes have several items which have the same Usage, but can be
differentiated by the HID Collections that enclose each Usage. Hence,
you would have one path per Usage (counting the Usages implicitly
defined by the Usage Minimum and Usage Maximum ranges), and in your
case, roughly one path per report.

Endpoints are not tightly coupled with paths and reports. Every device
always has an Endpoint Zero (EP0) which isn't specifically described
in the lsusb output (this is how lsusb retrieves all of that
information). HID devices are supposed to allow you to request reports
over EP0, and if the libhid function does not have "interrupt" in the
name, that's how it will do it.

libhid grabs one report at a time, and attempts to parse out the
individual Usage results. You might want to grab a single report, and
parse it in your own code.

Later on, you can optimize things by reading from the Interrupt In
endpoint (less USB protocol overhead than polling EP0), or writing to
the Interrupt Out endpoint.
Post by Gary Briggs
I've written some code [mainly copied outright from the libhid test
http://icculus.org/~chunky/stuff/dcdcusb/main.c
http://icculus.org/~chunky/stuff/dcdcusb/set_output_report_output.txt
The only thing to note is that there is a bug in the printing routine,
and if you see a "0x00000000" path element, it is just the 2nd or
later element of a range. The parser knows what to do internally,
though.
Post by Gary Briggs
Overall, I'm just confused as to what's going on or how to progress and
make this work. If anyone has any suggestions as to what I should do
from here, I'd greatly appreciate it.
What are your goals? Shutting down the power supply? Monitoring
certain parameters? If possible, I would start with something
innocuous like changing the state of an LED, but if you can power the
test machine from another power source, that would be best.

I won't have time to look at the Windows source code until later
tonight at the earliest.
--
Charles Lepple
Gary Briggs
2010-04-23 05:17:32 UTC
Permalink
Post by Charles Lepple
Post by Gary Briggs
I've written some code [mainly copied outright from the libhid test
http://icculus.org/~chunky/stuff/dcdcusb/main.c
http://icculus.org/~chunky/stuff/dcdcusb/set_output_report_output.txt
The only thing to note is that there is a bug in the printing routine,
and if you see a "0x00000000" path element, it is just the 2nd or
later element of a range. The parser knows what to do internally,
though.
I'm still just having a hard time understanding the overall "how to get a
path out, given a lsusb listing".

I started reading this:
http://www.networkupstools.org/doc/2.2.0/hid-subdrivers.html
Is this actually relevant to me? It seems that if someone else has done
most of the work for HID power supplies, I'll have more luck beginning
with that?
Post by Charles Lepple
Post by Gary Briggs
Overall, I'm just confused as to what's going on or how to progress and
make this work. If anyone has any suggestions as to what I should do
from here, I'd greatly appreciate it.
What are your goals? Shutting down the power supply? Monitoring
certain parameters? If possible, I would start with something
innocuous like changing the state of an LED, but if you can power the
test machine from another power source, that would be best.
At least to start, I just want to read the current state of the PSU.
Saliently, at least to start, I just want to be able to figure out
whether the ignition is connected or not, and if not, how long until it
actually shuts the whole system down.

I figure that once I see *something* everything else is in the realm of
figuring out the device-specific hoojimajiggery. I'm still just at the
point where I want to see *something*.

Thank-you so much for your time and explanations, they've really been
helping me.

Gary (-;
Xiaofan Chen
2010-04-23 05:32:06 UTC
Permalink
I will probably start with something simpler and
to understand the various concept of HID device,
Input/Output/Feature report and report IDs.

In the first glance, the device has multiple
report IDs. You can use Control Transfer
to set and get various reports. You can also
use Interrupt transfer for the Input/Output report.

The Windows program can of course help.

For a simpler HID device and how to use
libusb to control it, you can refer to the following
links.

Jan Axelson's generic HID firmware and Host
software and my simple libsub based program to drive
it under Linux/Windows/BSDs.
http://www.lvr.com/hidpage.htm
http://www.microchip.com/forums/tm.aspx?m=340898

I've added report ID support to the firmware and
have the corresponding libusb-1.0 based host software.
(tested under Linux, Windows and FreeBSD 8).
http://code.google.com/p/picusb/downloads/list
http://picusb.googlecode.com/files/Firmware_generic_hid.zip
http://picusb.googlecode.com/files/libusb1_lvrhid8.c

If you can, get Jan Axelson's USB Complete book
and have some good understand about USB and HID.
--
Xiaofan http://mcuee.blogspot.com
Charles Lepple
2010-04-23 12:09:25 UTC
Permalink
Post by Gary Briggs
Post by Charles Lepple
Post by Gary Briggs
I've written some code [mainly copied outright from the libhid test
http://icculus.org/~chunky/stuff/dcdcusb/main.c
http://icculus.org/~chunky/stuff/dcdcusb/
set_output_report_output.txt
The only thing to note is that there is a bug in the printing
routine,
and if you see a "0x00000000" path element, it is just the 2nd or
later element of a range. The parser knows what to do internally,
though.
I'm still just having a hard time understanding the overall "how to get a
path out, given a lsusb listing".
You're not the first :-)

Based on a quick read of the Windows source code, I don't know if the
paths will really matter. Note that the hid_interrupt_read() and
hid_interrupt_write() calls do not worry about paths - they just take
an endpoint number, and require you to figure out the packet size
yourself.

But for completeness, here's how the first element in the dump is
computed.
Post by Gary Briggs
path: 0x00010080.0x00010081; type: 0x80
Report Descriptor: (length is 200)
Item(Global): Usage Page, data= [ 0x01 ] 1
Generic Desktop Controls
Item(Local ): Usage, data= [ 0x80 ] 128
System Control
"Usage Page...= 0x01" is shorthand, and gets translated to "0x0001 <<
16", or 0x00010000. "Usage...=0x80" gets OR'd with that, to produce
"0x00010080". That full 32-bit Usage is the first element of the path
("root directory", if you will).
Post by Gary Briggs
Item(Main ): Collection, data= [ 0x01 ] 1
Application
The notion of a collection is similar to a directory on a filesystem;
hence, the full Usage calculated above will be the first elements of
all the paths until the collection ends.
Post by Gary Briggs
Item(Global): Report ID, data= [ 0x01 ] 1
Report IDs are sort of another dimension to the data - they allow the
information to be broken up into chunks that can be requested
separately (keyed by the Report ID). They are "sticky" until the next
Report ID.
Post by Gary Briggs
Item(Local ): Usage, data= [ 0x81 ] 129
System Power Down
This Usage ID is inside the collection, and it inherits the last Usage
Page which was set (0x0001 => 0x00010000). So its full 32-bit Usage is
(0x0001 << 16) | 0x0081, or 0x00010081, which is the second element of
the path (shown in the first line of the dump).
Post by Gary Briggs
Item(Global): Logical Minimum, data= [ 0x00 ] 0
Item(Global): Logical Maximum, data= [ 0x01 ] 1
Item(Global): Report Size, data= [ 0x01 ] 1
Item(Global): Report Count, data= [ 0x01 ] 1
Item(Main ): Input, data= [ 0x06 ] 6
Data Variable Relative No_Wrap Linear
Preferred_State No_Null_Position
Non_Volatile Bitfield
This portion from the lsusb listing basically says that the path
represents one bit of input data. The "0x80" at the end of the libhid
dump is the internal libhid direction flag (Input, which is Device-to-
Host).
Post by Gary Briggs
Item(Global): Report Count, data= [ 0x07 ] 7
Item(Main ): Input, data= [ 0x01 ] 1
Constant Array Absolute No_Wrap Linear
Preferred_State No_Null_Position
Non_Volatile Bitfield
The single bit of data is padded out with 7 bits of "don't care" (the
Constant/Absolute flags above).

If we look up the Usage in that Usage Page (which lsusb has done for
us), it appears as though the PSU is synthesizing a "System Power
Down" keypress.
Post by Gary Briggs
Item(Main ): End Collection, data=none
So the next Usage Page and Usage pair will specify the first element
in the path.
Post by Gary Briggs
http://www.networkupstools.org/doc/2.2.0/hid-subdrivers.html
Is this actually relevant to me? It seems that if someone else has done
most of the work for HID power supplies, I'll have more luck beginning
with that?
Unfortunately, that particular driver is looking for the standard HID
Power Device Class (PDC) Usage numbers (0x0084 and 0x0085).

Here's a sample libhid dump from the kind of UPS that NUT works with
out-of-the-box:

http://boxster.ghz.cc/projects/libhid/browser/trunk/ref/test_libhid_output/MGE_Pulsar_Evolution_500
Post by Gary Briggs
Post by Charles Lepple
Post by Gary Briggs
Overall, I'm just confused as to what's going on or how to progress and
make this work. If anyone has any suggestions as to what I should do
from here, I'd greatly appreciate it.
What are your goals? Shutting down the power supply? Monitoring
certain parameters? If possible, I would start with something
innocuous like changing the state of an LED, but if you can power the
test machine from another power source, that would be best.
At least to start, I just want to read the current state of the PSU.
Saliently, at least to start, I just want to be able to figure out
whether the ignition is connected or not, and if not, how long until it
actually shuts the whole system down.
You may be in luck if it's rainy this weekend - I'll have more time to
look into the Windows code.
Post by Gary Briggs
I figure that once I see *something* everything else is in the realm of
figuring out the device-specific hoojimajiggery. I'm still just at the
point where I want to see *something*.
Thank-you so much for your time and explanations, they've really been
helping me.
You're welcome. You have evidently spent a lot of time researching
this, so I figure it's the least I can do.

Oh, OK - I admit it - I think this device would be cool to support in
Network UPS Tools :-)
Charles Lepple
2010-04-21 02:58:23 UTC
Permalink
Post by Gary Briggs
It appears to be an HID device, and the source code available on that
website uses HID code on windows.
By the way, have you tried compiling their code on Linux? They have a
bunch of "#ifdef _WIN32" blocks which seem to use the Linux hiddev API.
Post by Gary Briggs
I'm reading the sample sourcecode on the libhid page, and it tells me
how to work out the path to access the input and output paths. That
seems predicated on a single input and output path, but the lsusb dump
above contains a bunch of each. Right at the end it mentions a
specific
pair of endpoints [0x81, 0x01] but I'm not clear whether that actually
has any bearing on the path as libhid would find it useful.
After looking at the Windows app, it seems like what I said before
about EP0 isn't necessarily going to work the same way as the
interrupt endpoints. They are basically using the Win32 WriteFile()
call, which apparently tries to use interrupt endpoints if they are
available.

You could probably try something like this:

char packet_out[] = { DCDCUSB_GET_ALL_VALUES };
char packet_in[255];

ret = hid_interrupt_write(hid, DCDCUSB_WRITEENDPOINT, packet_out,
sizeof(packet_out), TIMEOUT);
if (ret != HID_RET_SUCCESS) {
fprintf(stderr, "hid_interrupt_write failed with return code %d: %s
\n", ret,hid_strerror(ret));
return 1;
}

ret = hid_interrupt_read(hid, DCDCUSB_READENDPOINT, packet_in,
sizeof(packet_in), TIMEOUT);
if (ret != HID_RET_SUCCESS) {
fprintf(stderr, "hid_interrupt_read failed with return code %d: %s
\n", ret,hid_strerror(ret));
return 1;
}

Not sure what is reasonable for TIMEOUT, but it's in milliseconds, so
maybe 1000?
--
Charles Lepple
Gary Briggs
2010-04-23 05:45:24 UTC
Permalink
Post by Charles Lepple
Post by Gary Briggs
It appears to be an HID device, and the source code available on that
website uses HID code on windows.
By the way, have you tried compiling their code on Linux? They have a
bunch of "#ifdef _WIN32" blocks which seem to use the Linux hiddev API.
Yeah, I tried. I'm fairly certain that it has run on linux at some point
in the past, but that time was fairly long gone. I tried reaching them
to see if they had an older version, but I've had no response yet.
Post by Charles Lepple
Post by Gary Briggs
I'm reading the sample sourcecode on the libhid page, and it tells me
how to work out the path to access the input and output paths. That
seems predicated on a single input and output path, but the lsusb dump
above contains a bunch of each. Right at the end it mentions a specific
pair of endpoints [0x81, 0x01] but I'm not clear whether that actually
has any bearing on the path as libhid would find it useful.
After looking at the Windows app, it seems like what I said before
about EP0 isn't necessarily going to work the same way as the
interrupt endpoints. They are basically using the Win32 WriteFile()
call, which apparently tries to use interrupt endpoints if they are
available.
<snipped>
Post by Charles Lepple
Not sure what is reasonable for TIMEOUT, but it's in milliseconds, so
maybe 1000?
Your code included hid_strerror, but that doesn't appear to be available
here. I'm using the default package included in ubuntu 9.04 on PPC,
which according to showpkg is version 0.2.15+20060325-2.2ubuntu1

The code I ended using is this:

char packet_out[] = { DCDCUSB_GET_ALL_VALUES };
ret = hid_interrupt_write(hid, DCDCUSB_WRITEENDPOINT, packet_out, sizeof(packet_out), TIMEOUT);
if (ret != HID_RET_SUCCESS) {
fprintf(stderr, "hid_interrupt_write failed with return code %d\n", ret);
return 1;
}

char packet_in[255];
memset(packet_in, 0, sizeof(packet_in));
ret = hid_interrupt_read(hid, DCDCUSB_READENDPOINT, packet_in, sizeof(packet_in), TIMEOUT);
printf("\nPacketlength %i\n", strlen(packet_in));
if (ret != HID_RET_SUCCESS) {
fprintf(stderr, "hid_interrupt_read failed with return code %d\n", ret);
return 1;
}

What I think is the salient output is this:
device identification of HIDInterface 004/002[0]:
dev_handle: 0x100141b0
device: 0x1001b188
location: 004/002
manufacturer: MINI-BOX.COM
product: DCDC-USB
serial number: 1.00rel.
TRACE: hid_reset_parser(): resetting the HID parser for USB device 004/002[0]...
TRACE: hid_dump_tree(): iterating the parse tree for USB device 004/002[0]...

<snip>

TRACE: hid_reset_parser(): resetting the HID parser for USB device 004/002[0]...
TRACE: hid_interrupt_write(): writing interrupt report to device 004/002[0] ...
NOTICE: hid_interrupt_write(): successfully sent interrupt report to device 004/002[0]
TRACE: hid_interrupt_read(): retrieving interrupt report from device 004/002[0] ...
WARNING: hid_interrupt_read(): failed to get interrupt read from device 004/002[0]: could not claim interface 0: Device or resource busy

Packetlength 11
hid_interrupt_read failed with return code 21


The packet itself contains binary garbage if I try to print it out.

The full output of the program is here:
http://icculus.org/~chunky/stuff/dcdcusb/dcdcusbout.txt

Would I be better off trying to approach this starting with existing UPS
drivers?

Thanks for your help,
Gary (-;
Xiaofan Chen
2010-04-23 06:17:11 UTC
Permalink
Post by Gary Briggs
Your code included hid_strerror, but that doesn't appear to be available
here. I'm using the default package included in ubuntu 9.04 on PPC,
which according to showpkg is version 0.2.15+20060325-2.2ubuntu1
There is a recent report about endian problem for libusb-1.0, I am
not sure if it affects libusb-0.1 or not. But you may want to check
if that works for PPC. Take note libhid is based on libusb-0.1.
http://old.nabble.com/libusb-usbfs-endian-handling-td28141057.html
Post by Gary Briggs
could not claim interface 0: Device or resource busy
You have to detach the kernel HID driver using libhid's
libhid-detach-device program.
--
Xiaofan http://mcuee.blogspot.com
Charles Lepple
2010-04-23 12:15:51 UTC
Permalink
Post by Xiaofan Chen
There is a recent report about endian problem for libusb-1.0, I am
not sure if it affects libusb-0.1 or not. But you may want to check
if that works for PPC. Take note libhid is based on libusb-0.1.
http://old.nabble.com/libusb-usbfs-endian-handling-td28141057.html
When libhid was first being written, my primary desktop machine had a
PowerPC processor, so I doubt we have any endian-related bugs (unless
they were introduced recently, and 0.2.15 is not recent).

The key is that the report descriptor is read properly. It is 200
bytes long, and we would have seen that number byte-swapped if that
had been the problem.

Charles Lepple
2010-04-21 03:02:23 UTC
Permalink
Post by Gary Briggs
It appears to be an HID device, and the source code available on that
website uses HID code on windows.
By the way, for browsing the Windows source code, I found the attached
Doxygen[*] configuration file handy.

[*] http://www.doxygen.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: Doxyfile.gz
Type: application/x-gzip
Size: 16717 bytes
Desc: not available
URL: <http://lists.alioth.debian.org/pipermail/libhid-discuss/attachments/20100420/f1005fb5/attachment-0001.bin>
-------------- next part --------------
Loading...