[Nut-upsdev] APC Smart-UPS 1000 RM USB problems with newhidups

Peter Selinger selinger at mathstat.dal.ca
Fri Feb 3 11:40:14 UTC 2006


Yes, I have seen this problem before, and I am still not sure what
causes it. A similar problem was reported by Joal Peshkin on the
mailing list on Nov 6-9, 2005, for a different device (a
TrippLite). In his case, NUT thought that his report descriptor was
shorter than it actually was. Here, too, lsusb gave the correct value,
but we were able to verify that the UPS announced an incorrect value
via its HID Descriptor.

I am attaching below a program that one can use (as root) to read
descriptors from a USB device and dump them to stdout. It would be
interesting to see the output of:

get_descriptor 001 003 1 0 0 128 0x21 0
get_descriptor 001 003 1 0 0 128 0x22 0

(replace 001 003 by your UPS's bus and device number).

-- Peter

George Ross wrote:
> 
> 
> > I'm trying to use a rackmount Smart-UPS from APC with the USB connector, and 
> > I'm running into some issues.  They seem to stem from the fact that the 
> > Report Descriptor is much larger than any other UPS I've seen (61939 bytes). 
> 
> You'll probably find that it's not really that big, and that lsusb gives a 
> more realistic number (1040 is what it says here).
> 
> >   Here is the quick fix that I put in libusb.c after getting the HID 
> > descriptor, and before getting the Report descriptor, which seems to work 
> > okay:
> >                         if (desc->wDescriptorLength > 4096) {
> >                                 desc->wDescriptorLength = 4096;
> >                         }
> > I'm sure there is a better way to handle this.  I'm just wondering if this 
> > is okay to do, and if anyone had a better way to fix it.  Thanks!
> 
> That's pretty much what I did too.  Cf. my posting to the nut-upsdev list
> <200601061304.k06D4uWd011796 at eden.inf.ed.ac.uk> about a month ago.
> 
> BTW, it might be useful to know what kernel you're running on your machine.
> Ours are basically Linux 2.6.12-1.1381_FC3 variants with libusb-0.1.10a.
> -- 
> Dr George D M Ross, School of Informatics, University of Edinburgh
>     Kings Buildings, Mayfield Road, Edinburgh, Scotland, EH9 3JZ
> Mail: gdmr at inf.ed.ac.uk   Voice: +44 131 650 5147   Fax: +44 131 667 7209
>  PGP: 1024D/AD758CC5  B91E D430 1E0D 5883 EF6A  426C B676 5C2B AD75 8CC5
-------------- next part --------------
/* 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 1 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 = NULL;
  struct usb_interface *inter = NULL;
  struct usb_interface_descriptor *alt = NULL;
  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 */
  for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
    con = &dev->config[i];
    if (con->bConfigurationValue == arg_con) {
      break;
    }
  }
  if (i >= dev->descriptor.bNumConfigurations) {
    fprintf(stderr, "Bus %s device %s has no configuration %d.\n", arg_bus, arg_dev, arg_con);
    fprintf(stderr, "Possible values:");
    for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
      con = &dev->config[i];
      fprintf(stderr, " %d", con->bConfigurationValue);
    }
    fprintf(stderr, "\n");
    exit(2);
  }

  /* find the requested interface */
  for (i=0; i<con->bNumInterfaces; i++) {
    inter = &con->interface[i];
    alt = &inter->altsetting[0];
    if (alt->bInterfaceNumber == arg_int) {
      break;
    }
  }
  if (i >= 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:");
    for (i=0; i<con->bNumInterfaces; i++) {
      inter = &con->interface[i];
      alt = &inter->altsetting[0];
      fprintf(stderr, " %d", alt->bInterfaceNumber);
    }
    fprintf(stderr, "\n");
    exit(2);
  }
  
  /* find the requested alternative setting */
  for (i=0; i<inter->num_altsetting; i++) {
    alt = &inter->altsetting[i];
    if (alt->bAlternateSetting == arg_alt) {
      break;
    }
  }
  if (i >= 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:");
    for (i=0; i<inter->num_altsetting; i++) {
      alt = &inter->altsetting[i];
      fprintf(stderr, " %d", alt->bAlternateSetting);
    }
    fprintf(stderr, "\n");
    exit(2);
  }

  /* 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, arg_con);
  if (r<0) {
    fprintf(fout, "Warning: %s\n", usb_strerror());
    err = 3;
  }
  
  /* claim the interface */
  r = usb_claim_interface_with_detach(udev, arg_int);
  if (r<0) {
    fprintf(fout, "Warning: %s\n", usb_strerror());
    err = 3;
  }

  /* set altsetting */
  r = usb_set_altinterface(udev, arg_alt);
  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;
}


More information about the Nut-upsdev mailing list