Author Topic: linux dies after sending a control transfer.  (Read 22385 times)

ulao

  • Frequent Contributor
  • ****
  • Posts: 172
linux dies after sending a control transfer.
« on: September 26, 2020, 10:27:33 pm »
I complied a small little app using libusb, it runs great on windows and also works on linux. However once I send a control transfer on linux, the USB D- line is stays low. Although,  the transfer did make it to the device ( I verified that). I can send another controller transfer so the USB seems to still be alive but all USB activity otherwise is halted.
Also attached the logic capture from saleae that has the USB data in it. I'm guessing its a kernel bug.
The Linux Kernel is Linux 4.19.0-socfpga-r1 armv7l
« Last Edit: September 26, 2020, 10:29:45 pm by ulao »

ulao

  • Frequent Contributor
  • ****
  • Posts: 172
Re: linux dies after sending a control transfer.
« Reply #1 on: September 27, 2020, 11:28:12 am »
Here is the exact data.
Not sure what that X is trying to tell me? but it just keeps going on for 6 ms, than dies.
« Last Edit: September 27, 2020, 11:30:27 am by ulao »

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: linux dies after sending a control transfer.
« Reply #2 on: September 27, 2020, 08:29:01 pm »
Hard to say much without knowing what the device is (commercial product or something under development), what the control transfer is, and whether that control transfer succeeds under Windows. Surely libusb is capable of requesting control transfers.

ulao

  • Frequent Contributor
  • ****
  • Posts: 172
Re: linux dies after sending a control transfer.
« Reply #3 on: September 27, 2020, 09:34:57 pm »
It's a device I developed. It has worked fine for years on windows and even under some versions of linux from the best of my knowledge.  So far I can send gets but not sets. It seems the sets do make it to the device but stall the USB completely. I think the answer is in my output capture but it's a bit outside my level of  knowledge.  I also attached a cvs of the logged data but I think its useless in this form. My best guess is some type of kernel bug because I know libusb has worked with other OS's in the past.  It sort of acts like the host didnt receive ack but I know I'm sending that.

The device I'm developing does use V-USB but people have used this drive many times over. It could be my implementation but its been active since 2007. If there was a bug in my code I'd think I'd know by now.

for kicks I ran my application on ubuntu but no commands work I get this for all
.Error sending message: error sending control message: Bad file descriptor
Comes from usbOpenDevice(&dev, VENDOR, NULL, ID, NULL, 1);
this uses usbcalls.c from an open source libusb project.
But I'm pretty sure this is a compilation issue.


« Last Edit: September 28, 2020, 07:20:57 am by ulao »

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: linux dies after sending a control transfer.
« Reply #4 on: September 28, 2020, 04:53:33 pm »
For Set Report, remember the device has to ACK the data and return a 0-length data packet in the status phase.

ulao

  • Frequent Contributor
  • ****
  • Posts: 172
Re: linux dies after sending a control transfer.
« Reply #5 on: September 28, 2020, 07:12:36 pm »
Does this mean my device is sending NAK? And does the data0 packet show I'm sending 516? It should only be 8 bytes.
'
« Last Edit: September 28, 2020, 07:15:13 pm by ulao »

ulao

  • Frequent Contributor
  • ****
  • Posts: 172
Re: linux dies after sending a control transfer.
« Reply #6 on: September 28, 2020, 07:24:38 pm »
ok, I see what part of my problem was. I was not supplying the right size and just had it send the 512 max size of he array.  but my device still stalls.  I have it on the scope nice now but it looks good to me, am I missing something in this output?

I'm wondering if this is just a linux thing?

Just to recap, my device starts polling for data (HID gamepad) when the OS ask for it. Once I send a Control Transfer, the data arrives and  the polling stops. I would expect the polling to continue since the OS is asking for it but instead the device requires a reset. 
« Last Edit: September 29, 2020, 07:36:53 am by ulao »

ulao

  • Frequent Contributor
  • ****
  • Posts: 172
Re: linux dies after sending a control transfer.
« Reply #7 on: September 29, 2020, 06:10:08 pm »
So I found my issue but I do not understand why?

This is normally free when the interrupt is ready.

   while (!usbInterruptIsReady())
#define usbInterruptIsReady()   (usbTxLen1 & 0x10)
/* This macro indicates whether the last interrupt message has already been
 * sent. If you set a new interrupt message before the old was sent, the
 * message already buffered will be lost.
 */


Say about ever 8ms (like most OS's and according to USB spec), but once I send a Control Transfers (on linux not windows) this is always true. This explains why only Control Transfers work and my normal interrupts for gamepad data stop.  But why would this not go back to false, does the drive think the CT is still going, is it missing the zero packet?

according to my driver.

#if USB_CFG_IMPLEMENT_FN_WRITE
        if(usbMsgFlags & USB_FLG_USE_USER_RW){
            uchar rval = usbFunctionWrite(data, len);
            if(rval == 0xff){   /* an error occurred */
                usbTxLen = USBPID_STALL;
            }else if(rval != 0){    /* This was the final package */
                usbMsgLen = 0;  /* answer with a zero-sized data packet */ <------------------
            }
        }
#endif

it should have done that as I return a 1 after I receive my data. Not sure what that looks like but I posted my output of the packets above.


Could this maybe be a linux limitation? Maybe it has an issue with claiming the device? I made sure to do a usbCloseDevice when I was done but Linux just is not able to use the device for interrupt transfers  till its reset.

Also I do not understand what library my codes uses? I track it back as for as usb.h

 * Copyright (c) 2000-2003 Johannes Erdfelt <johannes@erdfelt.com>
 * Copyright (c) 2015      Nathan Hjelm <hjelmn@cs.unm.edu>
 all other call are written by Christian Starkjohann and I know who that is but they are just wrappers.
« Last Edit: September 30, 2020, 09:51:27 pm by ulao »

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: linux dies after sending a control transfer.
« Reply #8 on: October 03, 2020, 10:06:15 am »
Yes, the host guarantees that bandwidth will be available for interrupt transfers, and Linux is no exception. But the host will cease communications if a control transfer fails in an unexpected way.

From USB Complete, 5th Ed:

A USB 2.0 device has these responsibilities for transfers on a control endpoint:

• Send ACK in response to every Setup packet received without error.

• For supported control write requests, send ACK in response to received data in the
Data stage (if present) and return a ZLP in the Status stage.

• For supported control read requests, send data in response to IN token packets in
the Data stage and ACK the received ZLP in the Status stage.

• For unsupported requests, return STALL in the Data or Status stage.

For all but the Setup stage, one or more NAKs preceding ACK, ZLP, data, or STALL are
acceptable up to the timing limit for the stage.

ulao

  • Frequent Contributor
  • ****
  • Posts: 172
Re: linux dies after sending a control transfer.
« Reply #9 on: October 03, 2020, 10:28:31 am »
That does help then as it explains why Linux is halting further communication for my interrupt transfer. However does not explain why Windows cares not.  I do not see any reason to suspect an error in my output above unless maybe that last packet was not Ack'd?

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: linux dies after sending a control transfer.
« Reply #10 on: October 09, 2020, 10:53:17 am »
Timing can be different on different hosts. Years ago I recall problems with devices that were designed to work with Windows but not fully compliant with the specs, and they would fail on other OSes. An OS can also choose to ignore non-compliant behavior, but a missing ACK would be a major issue that any OS would flag.

ulao

  • Frequent Contributor
  • ****
  • Posts: 172
Re: linux dies after sending a control transfer.
« Reply #11 on: October 10, 2020, 07:37:47 pm »
Ok turns out to be the call to libusb.The second I get a good retrun from usbOpenDevice Linux cuts off all USB traffic but for the application that called this.  This is what that function does.

Code: [Select]
int usbOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int product, char *productName, int _usesReportIDs)
{
struct usb_bus      *bus;
struct usb_device   *dev;
usb_dev_handle      *handle = NULL;
int                 errorCode = USB_ERROR_NOTFOUND;
static int          didUsbInit = 0;

    if(!didUsbInit){
        usb_init();
        didUsbInit = 1;
    }
    usb_find_busses();
    usb_find_devices();
    for(bus=usb_get_busses(); bus; bus=bus->next){
        for(dev=bus->devices; dev; dev=dev->next){
            if(dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product){
                char    string[256];
                int     len;
                handle = usb_open(dev); /* we need to open the device in order to query strings */
                if(!handle){
                    errorCode = USB_ERROR_ACCESS;
                    fprintf(stderr, "Warning: cannot open USB device: %s\n", usb_strerror());
                    continue;
                }
                if(vendorName == NULL && productName == NULL){  /* name does not matter */
                    break;
                }
                /* now check whether the names match: */
                len = usbGetStringAscii(handle, dev->descriptor.iManufacturer, 0x0409, string, sizeof(string));
                if(len < 0){
                    errorCode = USB_ERROR_IO;
                    fprintf(stderr, "Warning: cannot query manufacturer for device: %s\n", usb_strerror());
                }else{
                    errorCode = USB_ERROR_NOTFOUND;
                    /* fprintf(stderr, "seen device from vendor ->%s<-\n", string); */
                    if(strcmp(string, vendorName) == 0){
                        len = usbGetStringAscii(handle, dev->descriptor.iProduct, 0x0409, string, sizeof(string));
                        if(len < 0){
                            errorCode = USB_ERROR_IO;
                            fprintf(stderr, "Warning: cannot query product for device: %s\n", usb_strerror());
                        }else{
                            errorCode = USB_ERROR_NOTFOUND;
                            /* fprintf(stderr, "seen product ->%s<-\n", string); */
                            if(strcmp(string, productName) == 0)
                                break;
                        }
                    }
                }
                usb_close(handle);
                handle = NULL;
            }
        }
        if(handle)
            break;
    }
    if(handle != NULL){
        int rval, retries = 3;
        //if(usb_set_configuration(handle, 1)){
            //fprintf(stderr, "Warning: could not set configuration: %s\n", usb_strerror());
        //}
        /* now try to claim the interface and detach the kernel HID driver on
         * linux and other operating systems which support the call.
         */
        while((rval = usb_claim_interface(handle, 0)) != 0 && retries-- > 0){
#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
            if(usb_detach_kernel_driver_np(handle, 0) < 0){
                fprintf(stderr, "Warning: could not detach kernel HID driver: %s\n", usb_strerror());
            }
#endif
        }
#ifndef __APPLE__
        if(rval != 0)
            fprintf(stderr, "Warning: could not claim interface\n");
#endif
/* Continue anyway, even if we could not claim the interface. Control transfers
 * should still work.
 */
        errorCode = 0;
        *device = handle;
        usesReportIDs = _usesReportIDs;
    }
    return errorCode;
}
Calling usbCloseDevice does not release the device either. I must reset it.
I did notice this bit here
Continue anyway, even if we could not claim the interface. Control transfers should still work.
but commenting any of that make the CT's report error device is busy. Makes me yet again wonder if linux is just not as capable as windows.

EDIT: hmm, I'm not alone.
https://libusb-devel.narkive.com/gOkpkDrQ/re-attaching-device-to-kernel-driver-after-usb-detach-kernel-driver-np

and

https://stackoverflow.com/questions/53196265/libusb-send-control-transfer-without-interrupting-stream
« Last Edit: October 12, 2020, 10:35:06 am by ulao »

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: linux dies after sending a control transfer.
« Reply #12 on: October 15, 2020, 10:28:29 am »
You are using libusb to communicate and at the same time sending HID-class control transfers?

ulao

  • Frequent Contributor
  • ****
  • Posts: 172
Re: linux dies after sending a control transfer.
« Reply #13 on: October 15, 2020, 07:54:37 pm »
No, I'm not necessarily using libusb to communicate. I'm not sure what to OS does to do that? If you connect a HID USB gamepad to a linux  box, you will see the event system mount it to js0. You then can use 'Jstest js0' to see the buttons and what the states are. I'm not really sure what jstest uses. Possibly SDL and if so I'm not sure what SDL uses.

SDL's page says these are the dependencies.
sudo apt-get install git cmake build-essential sqlite3 libsqlite3-dev libssl1.0-dev libssl1.0.0 libusb-1.0-0-dev libudev-dev libgtest-dev libbluetooth3 libbluetooth-dev bluez-tools libpulse-dev python3-pip python3-setuptools

So maybe it is using libusb. If so maybe linux is just not capable of sending controller transfers to a controller, as it is useless unless you are polling from it. I mean, that is its only intention...

The same thing is done in windows.  but when the USB drive is attached, it start polling (no need to run anything). In the case of windows I use  control transfers with HIDAPI, I'm not sure what the OS uses to poll the controller I'm guessing HIDUSB or WINUSB depending.
« Last Edit: October 15, 2020, 09:20:16 pm by ulao »

Jan Axelson

  • Administrator
  • Frequent Contributor
  • *****
  • Posts: 3033
    • Lakeview Research
Re: linux dies after sending a control transfer.
« Reply #14 on: October 16, 2020, 11:39:05 am »
OK, just wanted to be sure you understood that the host assigns one driver to the HID interface, typically the HID driver, and you can't use two drivers/APIs to access the same device at the same time.