Hi Peter, I've forwarded your mail to upsdev as it will be of interest for several people that are working on it. As I'm also on vacation (again) for a week and 1/2 as of this evening, I don't think I'll have the time for it before I get back.... I've not yet audited the patches, but these are queued on the Alioth tracker: https://alioth.debian.org/tracker/index.php?func=detail&aid=302101&group_id=30602&atid=411544 Side notes: - there are few new variables that need to be added to the naming scheme. These are consistant with the current naming, so no problem. But we need to document these a bit: Name / Description / Typical value - battery.charge.warning / ? / ? - ups.test.panel / purpose? / ? => ups.test.panel.result if it's for panel test result - ups.beeper.status / "UPS beeper/buzzer status" / on/off? - battery.mfr.date / "Battery manufacturing date (opaque string)" / 01-02-03 - I'll forward the PARSER patch, if accepted, to the libhid project (the project I've launched for the long run hid UPS support, and more generally for the opensource hid support) as the code base is the same (MGE HID Parser + libusb + wrapping functions) - the APC shutdown method should be matched against the apcupsd method as they have already dealed with this: http://cvs.sourceforge.net/viewcvs.py/apcupsd/apcupsd/src/drivers/usb/usb.c?rev=1.17&view=markup => look for usb_ups_kill_power() @Peter: thanks to look at / complete the above. Don't hesitate to post publically, and to register on Alioth if you want to join the dev team. Thanks for your contribution, Arnaud --- Linux / Unix Expert - MGE UPS SYSTEMS - R&D Dpt OpenSource Developer - http://arnaud.quette.free.fr/ Debian Developer - http://people.debian.org/~aquette/ ... and much more ... ---------------------- Envoy?e par Arnaud QUETTE/FR/MGE-UPS/User/GIN le 25/08/2005 08:56 --------------------------- selinger@mathstat.dal.ca (Peter Selinger) le 24/08/2005 14:51:41 Pour : arnaud.quette@mgeups.com cc : Objet : NUT patches Dear Arnaud, first, thanks for taking over the NUT project. I used to work with Russell when I wrote the belkinunv driver. My wife recently got an APC Back-UPS ES 650 with a USB connector. This gave me an opportunity to fine-tune the newhidups driver a bit. I have made several fixes / changes, which I am sending to you as a tarball of patches (in a separate email). For your convenience, I have separated the changes into a number of different patches that can be applied independently. Here are some details of what each patch does. The patches named nut-cvs-patch-* apply to the most recent CVS development tree. The patch named nut-2.0.2-* applies to the stable 2.0.2 tree, and combines all the changes of the various nut-cvs-patch-* patches. I have included it for convenience (because there is some fuzz and some failed hunks when trying to apply the cvs patches to the stable tree). I hope you can use these! Let me know if you have any question. -- Peter (selinger@users.sourceforge.net) 1) nut-cvs-patch-PARSER-2005-08-24 ---------------------------------------- This patch fixes a critical bug in the HIDParser. According to the USB HID specification, when parsing the report descriptor, local tags (such as "usage") are only supposed to apply to the next main item, not any subsequent items. Concretely, when parsing the report descriptor of my APC UPS, there were some redundant "usage" items that would stay on the UsageTab and would mangle the tree by shifting all usages by one. An example showing the effect of this bug is contained in the file hidparser-example.txt, also contained in the attached tarball. This patch also fixes a minor bug where the first item of the report descriptor was accidentally thrown away. This patch also adds the ability to deal with "long items" in report descriptors (as per USB HID specification). The items are not currently used, but at least they are safely skipped, rather than causing the parser to break. 2) nut-cvs-patch-APC-2005-08-24 ---------------------------------------- This patch fixes/adds driver variables and instant commands for APC. I added support for the following driver variables: battery.charge.warning battery.runtime.low ups.load ups.delay.shutdown ups.test.panel ups.beeper.status ups.mfr.date battery.mfr.date battery.date input.voltage.nominal input.transfer.low input.transfer.high I also added support for the following instant commands: test.panel.start test.panel.stop load.off shutdown.return shutdown.stop beeper.on beeper.off I also extended the info_lkp_t mechanism. I found that for certain data types (in particular, dates) a simple lookup table is insufficient to map the driver data to a string. I have extended the info_lkp_t structure with a hook for a custom function, and extended hu_find_infoval() to use the hook if present. This allows dates (ups.mfr.date, battery.mfr.date, battery.date) from the APC device to be formatted. I also made a variant of the function newhidups.c:find_nut_info(), called find_nut_info_valid(). This function is used when looking up an instant command. Rather than returning the first command in the list, it returns the first one that actually works for the current UPS. This makes it possible to have multiple alternative definitions for a certain instant command that will work on different UPS models. I have also changed the behavior of the "-k" flag to try "load.off" before "shutdown.return", as this kills the power immediately, instead of after a 60 second delay. This change only affects APC models. Finally, I corrected some mistakes in APC Usage Paths that were caused by the above HIDParser bug. 3) nut-cvs-patch-REOPEN-2005-08-24 ---------------------------------------- This patch fixes some problems associated with closing and reopening devices. I assume that the problems themselves are OS specific, but the patch gives solutions that should work correctly for all OSs. When reopening a device, this patch will look for a device that matches the previous manufacturer, model, and serial number. Therefore, if there is more than one UPS attached, one is guaranteed to get the same one. Also, when I unplug and reattach my UPS, my operating system (Linux 2.6.4) attaches a kernel driver to it. The original libusb_open() function would return too early when reopening a device, and in particular, it would not try to detach any kernel drivers. With this patch, reopening is treated in the same way as opening (and works much better for me). Finally, calling usb_release_interface() often caused my driver process to go into uninterruptible sleep and never wake up (it could not even be killed with "kill -KILL"). I had to reboot each time to be able to attach another driver. I commented out this call, which seems to be useless anyway. 4) nut-cvs-patch-NUMPATH-2005-08-24 ---------------------------------------- This patch extends the function hid_lookup_usage() so that path names can also be looked up by numerical values. This is just a convenience - it allows one to put stuff like "UPS.PowerConverter.ff860024" in the apc-hid.h while testing device variables whose purpose is yet unknown. 5) nut-cvs-patch-DEP-2005-08-24 ---------------------------------------- This is just a convenience; I added proper dependencies to drivers/Makefile.in. I noticed that when I edited a *.h file, make would never recompile the appropriate drivers. With this patch, it does. 6) nut-2.0.2-patch-ALL-2005-08-24 ---------------------------------------- While patches 1)-5) are for the CVS development tree, patch 6) is for the stable 2.0.2 tree. It combines all the above changes from 1)-5).
From: arnaud.quette@mgeups.com> > Side notes: > - there are few new variables that need to be added to the > naming scheme. These are consistant with the current naming, > so no problem. But we need to document these a bit: > Name / Description / Typical valueHere are the variables that I have added, that were not already in docs/new-names.txt: * battery.charge.warning / Battery level when UPS switches to "Warning" / 50 This is similar to battery.charge.low. I don't actually know what happens when this warning level is reached; perhaps it is just informational. * battery.mfr.date / Date when the battery was manufactured / 2005/04/02 This is similar to ups.mfr.date. * ups.beeper.status / State of the beeper: enabled, disabled, or muted / enabled The belkinunv driver uses ups.beeper.enable, with values "yes" and "no", but the USB-HID Usage table for power devices actually specifies three states for a generic UPS beeper (see usage 0084005a, AudibleAlarmControl): "Read or Write value: 1: Disabled (Never sound) 2: Enabled (Sound when an alarm is present) 3: Muted (Temporarily silence the alarm) This is the requested state (Write value) or the present state (Read value) of the audible alarm. The Muted state (3) persists until the alarm would normally stop sounding. At the end of this period the value reverts to Enabled (2). Writing the value Muted (3) when the audible alarm is not sounding is accepted but otherwise has no effect." I therefore recommend that this variable be standardized. In fact, it should be writable, but I don't know how to do this. * ups.test.panel / Panel test / 0 My APC UPS has a "panel test" variable, which consists of a single R/W bit. In write mode: setting this to 1 causes the UPS to omit one long beep. Setting this to 0 has no audible or visible effect. In read mode: simply returns the value of the last "write". I surmise that this variable indicates that a test is "in progess", but I am not sure of the test ever stops or does anything. Since NUT has instant commands "test.panel.start" and "test.panel.stop", I figured it might be worth having an associated variable as well. But it probably cannot be given an interesting meaning until we find other APC models with more interesting panels (mine has no LED's, so there is not much to test except the beeper). In particular, a panel test does not seem to have any interesting "result" that is reported.> - I'll forward the PARSER patch, if accepted, to the libhid project > (the project I've launched for the long run hid UPS support, > and more generally for the opensource hid support) as the > code base is the same (MGE HID Parser + libusb + wrapping functions)Good. This bug is quite serious. For example, look at ALfred Ganz's post at http://lists.alioth.debian.org/pipermail/nut-upsdev/2005-August/000015.html - it shows the same mangled Usage tree that I got.> - the APC shutdown method should be matched against > the apcupsd method as they have already dealed with this: > http://cvs.sourceforge.net/viewcvs.py/apcupsd/apcupsd/src/drivers/usb/usb.c?rev=1.17&view=markup > => look for usb_ups_kill_power()My change in this function is just a matter of taste. When called with the "-k" flag, I prefer the UPS to shut down immediately, as this will typically happen at the end of my shutdown script when the computer is ready to power off. The method that was already implemented also works, but has a 60 second delay. The file you reference above also seems to prefer a delay. Thanks, -- Peter> ---------------------- Envoy?e par Arnaud QUETTE/FR/MGE-UPS/User/GIN le 25/08/2005 08:56 --------------------------- > > > selinger@mathstat.dal.ca (Peter Selinger) le 24/08/2005 14:51:41 > > > Pour : arnaud.quette@mgeups.com > cc : > > Objet : NUT patches > > > Dear Arnaud, > > first, thanks for taking over the NUT project. I used to work with > Russell when I wrote the belkinunv driver. > > My wife recently got an APC Back-UPS ES 650 with a USB connector. > This gave me an opportunity to fine-tune the newhidups driver a bit. > I have made several fixes / changes, which I am sending to you as a > tarball of patches (in a separate email). For your convenience, I have > separated the changes into a number of different patches that can be > applied independently. Here are some details of what each patch does. > > The patches named nut-cvs-patch-* apply to the most recent CVS > development tree. > > The patch named nut-2.0.2-* applies to the stable 2.0.2 tree, and > combines all the changes of the various nut-cvs-patch-* patches. I > have included it for convenience (because there is some fuzz and some > failed hunks when trying to apply the cvs patches to the stable tree). > > I hope you can use these! Let me know if you have any question. > -- Peter > (selinger@users.sourceforge.net) > > 1) nut-cvs-patch-PARSER-2005-08-24 > ---------------------------------------- > > This patch fixes a critical bug in the HIDParser. According to the USB > HID specification, when parsing the report descriptor, local tags > (such as "usage") are only supposed to apply to the next main item, > not any subsequent items. Concretely, when parsing the report > descriptor of my APC UPS, there were some redundant "usage" items that > would stay on the UsageTab and would mangle the tree by shifting all > usages by one. An example showing the effect of this bug is contained > in the file hidparser-example.txt, also contained in the attached > tarball. > > This patch also fixes a minor bug where the first item of the report > descriptor was accidentally thrown away. > > This patch also adds the ability to deal with "long items" in report > descriptors (as per USB HID specification). The items are not > currently used, but at least they are safely skipped, rather than > causing the parser to break. > > 2) nut-cvs-patch-APC-2005-08-24 > ---------------------------------------- > > This patch fixes/adds driver variables and instant commands for APC. > I added support for the following driver variables: > > battery.charge.warning > battery.runtime.low > ups.load > ups.delay.shutdown > ups.test.panel > ups.beeper.status > ups.mfr.date > battery.mfr.date > battery.date > input.voltage.nominal > input.transfer.low > input.transfer.high > > I also added support for the following instant commands: > > test.panel.start > test.panel.stop > load.off > shutdown.return > shutdown.stop > beeper.on > beeper.off > > I also extended the info_lkp_t mechanism. I found that for certain > data types (in particular, dates) a simple lookup table is > insufficient to map the driver data to a string. I have extended the > info_lkp_t structure with a hook for a custom function, and extended > hu_find_infoval() to use the hook if present. This allows dates > (ups.mfr.date, battery.mfr.date, battery.date) from the APC device to > be formatted. > > I also made a variant of the function newhidups.c:find_nut_info(), > called find_nut_info_valid(). This function is used when looking up an > instant command. Rather than returning the first command in the list, > it returns the first one that actually works for the current UPS. > This makes it possible to have multiple alternative definitions for a > certain instant command that will work on different UPS models. > > I have also changed the behavior of the "-k" flag to try "load.off" > before "shutdown.return", as this kills the power immediately, instead > of after a 60 second delay. This change only affects APC models. > > Finally, I corrected some mistakes in APC Usage Paths that were caused > by the above HIDParser bug. > > 3) nut-cvs-patch-REOPEN-2005-08-24 > ---------------------------------------- > > This patch fixes some problems associated with closing and reopening > devices. I assume that the problems themselves are OS specific, but > the patch gives solutions that should work correctly for all OSs. > > When reopening a device, this patch will look for a device that > matches the previous manufacturer, model, and serial > number. Therefore, if there is more than one UPS attached, one is > guaranteed to get the same one. > > Also, when I unplug and reattach my UPS, my operating system (Linux > 2.6.4) attaches a kernel driver to it. The original libusb_open() > function would return too early when reopening a device, and in > particular, it would not try to detach any kernel drivers. With this > patch, reopening is treated in the same way as opening (and works much > better for me). > > Finally, calling usb_release_interface() often caused my driver > process to go into uninterruptible sleep and never wake up (it could > not even be killed with "kill -KILL"). I had to reboot each time to be > able to attach another driver. I commented out this call, which seems > to be useless anyway. > > 4) nut-cvs-patch-NUMPATH-2005-08-24 > ---------------------------------------- > > This patch extends the function hid_lookup_usage() so that path names > can also be looked up by numerical values. This is just a convenience > - it allows one to put stuff like "UPS.PowerConverter.ff860024" in the > apc-hid.h while testing device variables whose purpose is yet unknown. > > 5) nut-cvs-patch-DEP-2005-08-24 > ---------------------------------------- > > This is just a convenience; I added proper dependencies to > drivers/Makefile.in. I noticed that when I edited a *.h file, make > would never recompile the appropriate drivers. With this patch, it > does. > > 6) nut-2.0.2-patch-ALL-2005-08-24 > ---------------------------------------- > > While patches 1)-5) are for the CVS development tree, patch 6) is for > the stable 2.0.2 tree. It combines all the above changes from 1)-5).
Charles Lepple wrote:> > would you mind forwarding (to either me, or the list) the .txt file > showing the hidparser bug? I am trying to revamp libhid's hidparser, > and it would be helpful to have the descriptor. > > Also useful would be a hex dump of the descriptor itself (available > with libhid's test program), but if you don't have time to get libhid > up and running, I understand.Charles, I finally got my hands on that hex dump. Here is descriptor 22/0, from an APC Back-UPS ES 650: 05 84 09 04 a1 01 09 24 a1 00 85 01 09 fe 79 01 75 08 95 01 15 00 26 ff 00 b1 22 85 02 09 ff 79 02 b1 22 85 03 05 85 09 89 79 04 b1 22 85 04 09 8f 79 03 b1 22 85 05 09 8b b1 22 85 06 06 86 ff 09 60 81 a2 09 60 b1 a2 85 07 05 85 09 85 75 10 27 ff ff 00 00 b1 a2 85 08 05 84 09 40 67 21 d1 f0 00 55 05 b1 22 85 09 09 30 b1 a2 85 0a 09 fd 75 08 26 ff 00 65 00 55 00 79 03 b1 22 85 0b 05 85 09 2c b1 22 85 0c 09 66 25 64 81 a2 09 66 b1 a2 09 68 75 10 27 ff ff 00 00 66 01 10 81 a2 09 68 b1 a2 85 0d 09 83 75 08 25 64 65 00 b1 22 85 0e 09 67 b1 22 85 0f 09 8c b1 22 85 10 09 8e b1 22 85 11 09 29 15 01 b1 a2 85 12 09 8d 15 00 b1 22 05 84 09 02 a1 02 85 16 05 85 75 01 25 01 09 44 81 a2 09 44 b1 a2 09 45 81 a2 09 45 b1 a2 09 d0 81 a2 09 d0 b1 a2 09 d1 81 a2 09 d1 b1 a2 09 42 81 a2 09 42 b1 a2 05 84 09 69 81 a2 09 69 b1 a2 05 85 09 43 81 a2 09 43 b1 a2 09 4b 81 a2 09 4b b1 a2 05 84 09 65 81 a2 09 65 b1 a2 95 17 81 01 b1 01 c0 85 17 05 85 09 2a 95 01 75 10 27 ff ff 00 00 66 01 10 b1 a2 85 18 05 84 09 5a 75 08 15 01 25 03 65 00 b1 a2 c0 09 12 a1 00 85 1c 06 86 ff 09 16 75 18 15 00 27 ff ff ff 00 b2 a2 01 85 20 05 85 09 85 75 10 27 ff ff 00 00 b1 a2 85 22 09 66 75 08 25 64 b1 a2 85 23 09 68 75 10 27 ff ff 00 00 66 01 10 09 68 b1 a2 85 24 09 2a b1 a2 85 25 05 84 09 40 67 21 d1 f0 00 55 05 b1 22 85 26 09 30 b1 a2 85 27 06 86 ff 09 24 75 08 16 e8 00 26 fe 00 65 00 55 00 b1 a2 85 28 09 18 75 20 17 01 00 00 80 27 ff ff ff 7f b2 a2 01 c0 05 84 09 1a a1 00 85 30 05 84 09 40 75 08 15 00 26 ff 00 67 21 d1 f0 00 55 07 b1 22 85 31 09 30 b1 a2 85 32 09 53 15 57 25 61 b1 a2 85 33 09 54 15 7f 26 8b 00 b1 a2 85 34 06 86 ff 09 24 16 aa 00 26 fe 00 65 00 55 00 b1 a2 85 35 09 61 15 00 25 02 b1 a2 85 36 09 52 25 0d b1 a2 c0 09 05 a1 00 85 40 09 7c 25 01 b1 a2 85 41 09 7d 75 10 16 ff ff 26 ff 7f 66 01 10 b1 a2 c0 05 84 09 16 a1 00 85 50 09 35 75 08 15 00 25 64 65 00 b1 a2 85 51 06 86 ff 09 24 16 6d 00 26 b8 00 b1 a2 c0 06 86 ff 09 01 a1 00 85 60 09 23 75 10 15 00 27 ff ff 00 00 b1 a2 85 61 09 26 75 08 15 84 25 8c b1 a2 85 62 09 25 75 20 17 01 00 00 80 27 ff ff ff 7f b2 a2 01 c0 85 7f 05 84 09 fe 79 05 75 08 95 01 15 00 26 ff 00 b1 22 85 7e 06 86 ff 09 42 79 06 b1 22 85 7d 05 84 09 ff 79 02 b1 22 85 7c 09 fd 79 03 b1 22 85 7b 05 85 09 85 75 10 27 ff ff 00 00 b1 a2 05 84 09 02 a1 02 85 7a 05 85 09 44 75 01 25 01 b1 a2 09 45 b1 a2 09 d0 b1 a2 09 d1 b1 a2 09 42 b1 a2 05 84 09 69 b1 a2 05 85 09 43 b1 a2 09 4b b1 a2 05 84 09 65 b1 a2 95 17 b1 01 c0 85 79 06 86 ff 09 72 75 08 95 01 b1 a2 85 78 05 84 09 5a 15 01 25 03 b1 a2 85 75 06 86 ff 09 29 26 ff 00 b1 a2 85 74 09 2a 75 20 17 01 00 00 80 27 ff ff ff 7f b1 a2 c0 -- Peter
Charles Lepple wrote:> > On 8/29/05, Peter Selinger <selinger@mathstat.dal.ca> wrote: > > Charles, I finally got my hands on that hex dump. Here is descriptor > > 22/0, from an APC Back-UPS ES 650: > > Thanks! (That passed through my rudimentary HID descriptor parser just > fine, but then again, it's more of a tokenizer - it doesn't have all > of the functionality of the MGE HIDParser code yet.) > > A couple of questions, if you don't mind... > > What tool did you use to extract that?I did that with a little custom program called "get_descriptor.c". The source is attached below.> Also, what does the "descriptor 22/0" designation refer to?As you probably know, a USB device communicates its capabilities via a bunch of "descriptors". There are 5 standard descriptor types defined in the USB specification, Section 9.6 [1]: 0x01 = device descriptor 0x02 = configuration descriptor 0x03 = string descriptor 0x04 = interface descriptor 0x05 = endpoint descriptor. Further, the HID class specification defines some additional descriptors in Section 6.2 [2]: 0x21 = HID descriptor 0x22 = report descriptor 0x23 = physical descriptor There can be more than one descriptor of a given type, identified by an "index" and possibly a "language ID" (see [1] Sec. 9.4.3). The report descriptor of a HID device is always descriptor number 0x22, index 0. This is what I meant by "descriptor 22/0". [1] the USB specification, http://www.usb.org/developers/docs/usb_20_02212005.zip [2] the USB HID specification, http://www.usb.org/developers/devclass_docs/HID1_11.pdf> Do you have the file 'hidparser-example.txt' that was in the original > email you sent to Arnaud?I believe I already sent that to you (and to the mailing list) on August 26. The file is also contained in the tarball that Arnaud posted to the bug tracker at: https://alioth.debian.org/tracker/index.php?func=detail&aid=302101&group_id=30602&atid=411544 ("Download" at the bottom of the page). ---------------------------------------------------------------------- /* get_descriptor.c: a simple tool to get a descriptor from a USB device. Uses the libusb API, see http://libusb.sourceforge.net/doc/ */ /* NOTE: don't run this program on your USB mouse or keyboard: it will detach the kernel driver and leave your computer pretty useless. */ /* Copyright (C) 2005 Peter Selinger. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ /* Usage: get_descriptor <bus> <device> <configuration> <interface> <altsetting> <endpoint> <descriptor> <index> for example get_descriptor 005 003 0 0 0 128 0x22 0 is what I used to get the device descriptor of my USB. */ #include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> /* the next invlude is not mentioned in the libusb documentation, but evidently needed */ #include <usb.h> /* ---------------------------------------------------------------------- */ /* general-purpose auxiliary functions */ /* write a hexdump of buf[0]..buf[len-1] to fout */ void hexdump(FILE *fout, char *buf, int len) { int i; for (i=0; i<len; i++) { if ((i % 24) == 0) { fprintf(fout, "\n"); } fprintf(fout, " %02x", (unsigned char)buf[i]); } fprintf(fout, "\n"); } /* write an ASCII dump of buf[0]..buf[len-1] to fout */ void asciidump(FILE *fout, char *buf, int len) { int i; for (i=0; i<len; i++) { if ((i % 72) == 0) { fprintf(fout, "\n "); } fprintf(fout, "%c", buf[i] >= 32 && buf[i] < 127 ? buf[i] : '.'); } fprintf(fout, "\n"); } /* convert string to integer. Return -1 on invalid string. */ int mystrtol(char *s) { int r; char *p; r = strtol(s, &p, 0); if (*s == 0 || *p != 0) { return -1; } return r; } /* ---------------------------------------------------------------------- */ /* auxiliary USB functions */ /* claim interface, detaching any kernel driver if necessary */ int usb_claim_interface_with_detach(usb_dev_handle *dev, int interface) { int r; r = usb_claim_interface(dev, interface); #if LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP if (r == -EBUSY) { r = usb_detach_kernel_driver_np(dev, interface); if (r == 0) { r = usb_claim_interface(dev, interface); } } #endif return r; } /* ---------------------------------------------------------------------- */ /* user interface */ void usage(FILE *fout, char *myname) { fprintf(fout, "Usage: %s <bus> <device> <configuration> <interface> <altsetting> <endpoint> <descriptor> <index>\n", myname); } int main(int ac, char *av[]) { struct usb_bus *bus; struct usb_device *dev; struct usb_config_descriptor *con; struct usb_interface *inter; struct usb_interface_descriptor *alt; struct usb_endpoint_descriptor *end; int i, r; int err = 0; char buf[2048]; usb_dev_handle *udev; FILE *fout = stdout; char *myname, *arg_bus, *arg_dev; int arg_con, arg_int, arg_alt, arg_end, arg_typ, arg_ind; /* required initializations */ usb_init(); usb_find_busses(); usb_find_devices(); myname = av[0]; av++; ac--; /* command line options */ if (ac != 8) { fprintf(stderr, "Wrong number of arguments.\n"); usage(stderr, myname); exit(1); } arg_bus = av[0]; arg_dev = av[1]; arg_con = mystrtol(av[2]); arg_int = mystrtol(av[3]); arg_alt = mystrtol(av[4]); arg_end = mystrtol(av[5]); arg_typ = mystrtol(av[6]); arg_ind = mystrtol(av[7]); /* find the requested bus */ for (bus = usb_get_busses(); bus; bus = bus->next) { if (strcmp(bus->dirname, arg_bus) == 0) { break; } } if (!bus) { fprintf(stderr, "There is no bus %s.\n", arg_bus); fprintf(stderr, "Possible values:"); for (bus = usb_get_busses(); bus; bus = bus->next) { fprintf(stderr, " %s", bus->dirname); } fprintf(stderr, "\n"); exit(2); } /* find the requested device */ for (dev = bus->devices; dev; dev = dev->next) { if (strcmp(dev->filename, arg_dev) == 0) { break; } } if (!dev) { fprintf(stderr, "Bus %s has no device %s.\n", arg_bus, arg_dev); fprintf(stderr, "Possible values:"); for (dev = bus->devices; dev; dev = dev->next) { fprintf(stderr, " %s", dev->filename); } fprintf(stderr, "\n"); exit(2); } /* find the requested configuration */ if (arg_con < 0 || arg_con >= dev->descriptor.bNumConfigurations) { fprintf(stderr, "Bus %s device %s has no configuration %d.\n", arg_bus, arg_dev, arg_con); fprintf(stderr, "Possible values: 0--%d\n", dev->descriptor.bNumConfigurations-1); exit(2); } con = &dev->config[arg_con]; /* find the requested interface */ if (arg_int < 0 || arg_int >= con->bNumInterfaces) { fprintf(stderr, "Bus %s device %s configuration %d has no interface %d.\n", arg_bus, arg_dev, arg_con, arg_int); fprintf(stderr, "Possible values: 0--%d\n", con->bNumInterfaces-1); exit(2); } inter = &con->interface[arg_int]; /* find the requested alternative setting */ if (arg_alt < 0 || arg_alt >= inter->num_altsetting) { fprintf(stderr, "Bus %s device %s configuration %d interface %d has no altsetting %d.\n", arg_bus, arg_dev, arg_con, arg_int, arg_alt); fprintf(stderr, "Possible values: 0--%d\n", inter->num_altsetting-1); exit(2); } alt = &inter->altsetting[arg_alt]; /* find the requested endpoint */ if (arg_end != 128) { for (i = 0; i < alt->bNumEndpoints; i++) { end = &alt->endpoint[i]; if (end->bEndpointAddress == arg_end) { break; } } if (i >= alt->bNumEndpoints) { fprintf(stderr, "Bus %s device %s configuration %d interface %d altsetting %d has no endpoint %d.\n", arg_bus, arg_dev, arg_con, arg_int, arg_alt, arg_end); fprintf(stderr, "Possible values: 128"); for (i = 0; i < alt->bNumEndpoints; i++) { end = &alt->endpoint[i]; fprintf(stderr, " %d", end->bEndpointAddress); } fprintf(stderr, "\n"); exit(2); } } /* OK, we got all the information. Now try to fetch the descriptor */ /* open the device */ udev = usb_open(dev); if (!udev) { fprintf(fout, "Can't open bus %s device %s: %s\n", arg_bus, arg_dev, usb_strerror()); exit(3); } /* set the configuration */ r = usb_set_configuration(udev, con->bConfigurationValue); if (r<0) { fprintf(fout, "Warning: %s\n", usb_strerror()); err = 3; } /* claim the interface */ r = usb_claim_interface_with_detach(udev, alt->bInterfaceNumber); if (r<0) { fprintf(fout, "Warning: %s\n", usb_strerror()); err = 3; } /* set altsetting */ r = usb_set_altinterface(udev, alt->bAlternateSetting); if (r<0) { fprintf(fout, "Warning: %s\n", usb_strerror()); err = 3; } /* get the descriptor for this endpoint, type, index */ r = usb_get_descriptor_by_endpoint(udev, arg_end, arg_typ, arg_ind, buf, sizeof(buf)); if (r < 0) { fprintf(fout, "Can't get endpoint %d descriptor 0x%02x index %d: %s\n", arg_end, arg_typ, arg_ind, usb_strerror()); err = 3; goto done; } fprintf(stderr, "Bus %s device %s configuration %d interface %d altsetting %d endpoint %d descriptor 0x%02x index %d:\n", arg_bus, arg_dev, arg_con, arg_int, arg_alt, arg_end, arg_typ, arg_ind); hexdump(fout, buf, r); asciidump(fout, buf, r); done: usb_close(udev); if (err && getuid() != 0) { fprintf(fout, "Try running this program as root, or as the owner of /proc/bus/usb/%s/%s.\n", arg_bus, arg_dev); } return err; }