[Nut-upsdev] Krauler UP-M500VA investigation

Peter Selinger selinger at mathstat.dal.ca
Thu Nov 16 15:16:00 CET 2006


Ralf Fassel wrote:
> 
> * Peter Selinger
> | > > function of all the variables, as they all use standard names (except
> | > > the top-level 00860004, which is likely a bug - should be 00840004).
> | > 
> | > If it is a bug, it's not caused by software.
> | 
> | Oh no, it's a bug in the UPS firmware, or at least a design flaw, in
> | the sense of "does not comply with the USB Power Device Class
> | specification". Both the Ablerex and the MEC have exactly the same.
> 
> Could this be deliberate?  Report '00860004', but answer only if asked
> for '00840004'?  Would not surprise me if e.g. Windows ignores this
> '00860004' and always asks for '00840004'...

It's an interesting guess, but this is not possible. The reason is
that (as Charles already pointed out) one never asks a HID device for
'00860004.xxx' - one asks, e.g., for "Report number 13". The UPS sends
a sort of directory in the beginning, explaining what reports
correspond to what usages. So in a sense the usages such as '00860004'
are purely informational. 

It is, however, possible that it sends the wrong report id's, or that
the hardware is simply too slow to respond to our requests (as Charles
suggested). 

I once posted a program called get_descriptor.c on the nut-upsdev
mailing list [attached below]. This is a simple tool that can be used
to experiment with retrieving individual reports from a USB device
from the command line. It might be a good starting point for
exploration, as it is relatively simple to modify (e.g. to insert
timing delays).

If we find some hack that works, we can then worry about how newhidups
should be modified.

-- Peter


----------------------------------------------------------------------
/* 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