Hi,

So somehow I found myself here despite trying to avoid kernel hacking by
using libusb.  libusb has this concept of submit a read/write with a timeout.
So I submit a read, and just pick a random timeout, say 1 second.
Lib usb then sits in a poll loop, after a second it cancels the read and
returns.  My program then resubmits the read again and again.

It appears that UHCI does not setup the right DATA0/DATA1 toggle
for this case.  It keeps toggling with each new read.  The DATA0/DATA1
is suppose to toggle with each successfully completed packet.
The result in this case is the toggle gets advanced each time even
though no packet has been delivered, resulting in initial dropped packets.
So when the URB is canceled, there is some code
in the uhci_unlink_generic() call thats calculates the next appropriate
toggle to use.

Heres a one line patch(against 2.6.9) that fixes it for me.

diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c       2004-10-18 16:55:07.000000000 -0500
+++ b/drivers/usb/host/uhci-hcd.c       2004-12-01 21:35:52.000000000 -0600
@@ -1477,7 +1477,7 @@
 {
        struct list_head *head, *tmp;
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-       int prevactive = 1;
+       int prevactive = 0;
 
        uhci_dec_fsbr(uhci, urb);       /* Safe since it checks */


Attached below are a couple of test programs I used to verify
the problem with a Belkin mouse(Hold the mouse up in the air,
press a button every 1-2 seconds, notice the missing data.)

Looks like the OHCI handles toggle in hardware, so not an issue there.
Oh, Libusb(0.1.8) has some other related bugs in it that are only
fixed in CVS version
(un-reliable timeout, not reaping urb after cancel).
Maybe I'll see if libusb can add some concept of nonblock read and let me check
status of urb without using it's timeout..

Hopefully this change doesn't blow up some other case, I think it makes logical
sense.  Comments please!
Karl.

// mous.c
#include <stdio.h>
#include <usb.h>
#include <stdlib.h>
#include <unistd.h>

// id's of my belkin mouse:
int vid = 0x1241;
int prod = 0x1111;

struct usb_bus *bus;
struct usb_device *dev;
int stat, scancnt;
unsigned char binbuf[1024];
usb_dev_handle *udev;
int verbose = 0;

int main(int argc, char *argv[])
{
  char *cbuf;
  int i;
  int show_listing = 0;
  int found_device = 0;
  struct usb_device *fnd_dev;

  for (i=1; i<argc; i++) {
    cbuf = argv[i];
    if ((*cbuf == '-') || (*cbuf == '/')) {
      ++cbuf;
      switch(*cbuf) {
        case 'p':
          ++cbuf;
          if ((*cbuf == 0) && (i < (argc-1))) { i++; cbuf = argv[i]; };
          sscanf(cbuf,"%x", &prod);
          printf("product id to use:%xH", prod);
        break;

        case 'v':
          ++cbuf;
          if ((*cbuf == 0) && (i < (argc-1))) { i++; cbuf = argv[i]; };
          sscanf(cbuf,"%x", &vid);
          printf("vendor id to use:%xH", vid);
        break;

        default :
        case '?':
          ++cbuf;
          printf("mous options\n");
          printf("options:\n");
          printf(" -v #   vendor id to use\n");
          printf(" -v #   product id to use\n");
          exit(1);
        break;
      }
    }
  }

  usb_init();
  usb_find_busses();
  usb_find_devices();

  for (bus = usb_busses; bus; bus = bus->next) {
      for (dev = bus->devices; dev; dev = dev->next) {
        int ret;
        char string[256];

        if ((dev->descriptor.idVendor == vid) &&
            (dev->descriptor.idProduct == prod)) {
          found_device = 1;
          fnd_dev = dev;
          break;
        }
      }
      if (found_device)
        break;
  }

  if (found_device) {
      udev = usb_open(dev);
      printf("udev=%x\n", udev);
      usb_set_debug(7);
      if (udev) {
        stat = usb_set_configuration (udev,
                  dev->config[0].bConfigurationValue);
        printf ("stat:%d from usb_set_config to %x\n",stat,
dev->config[0].bConfigurationValue);
        if (stat != 0) {
          printf ("%s\n", usb_strerror());
          printf("need root?, or some other problem?\n");
          exit(1);
        }

        stat = usb_claim_interface(udev, 0);
        printf ("stat:%d from claim\n",stat);
        if (stat != 0)
          printf ("%s\n", usb_strerror());

        scancnt = 0;
        while (1) {
           memset(binbuf, 0, 16);
           stat = usb_interrupt_read(udev, 1, binbuf, 8, 900);
           //stat = usb_interrupt_read(udev, 1, binbuf, 6, 900);
           printf ("stat:%5d, ", stat);

           for (i=0; i<8; i++) {
             if (i >= stat)
                printf ("__ ");
             else
                printf ("%02x ", binbuf[i]);
           }
           printf (" loop:%d", scancnt);
           ++scancnt;
           if (stat < 0)
             printf (" ERROR: %s", usb_strerror());
           printf ("\n");
        }
        printf ("closing port\n");
        usb_close (udev);
        udev = 0;
      }
  }
  exit (0);
}

/*------------------------------------------------------------------------
  usbfs.c - usb test prog, test usbfs interface.
|------------------------------------------------------------------------*/
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#include <time.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>

int verbose = 0;
/*-------------------------------------------------------------------------
  ts - time stamp.
|-------------------------------------------------------------------------*/
char *ts(void)
{
  static char stamp[80];
  struct timeval tv;
  gettimeofday(&tv, NULL);
  sprintf(stamp,"%d.%05d", tv.tv_sec, tv.tv_usec);
  return stamp;
}

/*-------------------------------------------------------------------------

|-------------------------------------------------------------------------*/
void fill_in_urb(struct usbdevfs_urb *int_urb,
                 unsigned char *buf,
                 int len,
                 int ep,
                 void *context)
{
  memset(int_urb, 0, sizeof(*int_urb));
  int_urb->type = USBDEVFS_URB_TYPE_INTERRUPT;
  int_urb->endpoint = ep;
  int_urb->flags = 0;
  int_urb->buffer = buf;
  int_urb->buffer_length = len;
  int_urb->usercontext = context;
  int_urb->signr = 0;
  int_urb->actual_length = 0;
}

/*-------------------------------------------------------------------------

|-------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
  char *cbuf;
  int i;
  int fd;
  unsigned char binbuf[4096];
  int scancnt = 0;
  int stat;
  char *usb_name = "/proc/bus/usb/001/003";
  struct usbdevfs_urb int_urb[8];
  struct usbdevfs_urb *urb;
  int *context = NULL;
  int interface = 0;
  int configuration = 1; // configuration = 1, typical
#define USB_ENDPOINT_IN 0x80
  int ep = 1 | USB_ENDPOINT_IN;
  int li;
  int waiting;

  if (argc > 1)
    verbose = argc-1;

  printf("opening %s\n", usb_name);
  fd = open(usb_name, O_RDWR);
  if (fd < 0) {
    printf("failed to open %s\n", usb_name);
    exit(1);
  }

  printf("%s setting config to %d\n", ts(), configuration);
  stat = ioctl(fd, USBDEVFS_SETCONFIGURATION, &configuration );
  if (stat < 0) {
    printf("Error %d setting configuration: %s\n", errno, strerror(errno));
    exit(1);
  }

  printf("setting interface to %d\n", interface);
  stat = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &interface );
  if (stat < 0) {
    printf("Error %d claiming interface: %s\n", errno, strerror(errno));
    exit(1);
  }

  for (li=0; li<20; li++) {
    memset(binbuf, 0, 16);
    urb = &int_urb[li & 7];
    fill_in_urb(urb, binbuf, 8, ep, (void *) li);
    if (verbose)
      printf("%s submiting read URB, context:%x urb:%x\n", ts(), li, urb);
    stat = ioctl(fd, USBDEVFS_SUBMITURB, urb );
    if (stat < 0) {
      printf("Error %d submiting URB: %s\n", errno, strerror(errno));
      exit(1);
    }

    waiting = 1;
    while (waiting) {
      if (verbose > 1)
        printf("%s try to reap finished URB\n", ts());
      stat = ioctl(fd, USBDEVFS_REAPURBNDELAY, &context );
      if (stat < 0) {
        if (errno != EAGAIN) {
          printf("Error %d reaping URB: %s\n", errno, strerror(errno));
          exit(1);
        } else {
          if (verbose>1)
            printf("%s Error %d reaping URB: %s\n", ts(), errno,
strerror(errno));
          ++waiting;
          if (waiting > 10) {
            // too long, kill URB request.
            stat = ioctl(fd, USBDEVFS_DISCARDURB, urb);
            if (stat < 0) {
              printf("Error %d discarding URB: %s\n", errno, strerror(errno));
              exit(1);
            }
            if (verbose)
              printf("%s timeout, URB %x discarded\n", ts(), urb);
            // we still need to reap it!
            waiting = 1;
            //waiting = 0;
            //stat = -1;
          }
        }
        if (waiting) {
          if (verbose > 1)
            printf("%s sleeping...\n", ts());
          usleep(100000);
        }
      } else {
        if (context != (void *)urb) {
          if (verbose)
            printf("%s Reaped, but not our URB!.  Context:%x\n", ts(), context);
        } else {
          if (verbose)
            printf("%s OK, reaped.  Context:%x\n", ts(), context);
          waiting = 0;
          stat = urb->actual_length;
        }
      }
    }

    printf ("%s stat:%5d, ", ts(), stat);
    for (i=0; i<8; i++) {
      if (i >= stat)
        printf ("__ ");
      else
        printf ("%02x ", binbuf[i]);
    }
    printf (" loop:%d", scancnt);
    ++scancnt;

    if (stat < 0) {
      printf (" ERROR: %d", stat);  // strerror?
    }
    printf ("\n");
  }

  printf ("closing port\n");
  close(fd);
  exit(0);
}


-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now. 
http://productguide.itmanagersjournal.com/
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to