[fpc-pascal] USB Human Interface Devices

James Richters james at productionautomation.net
Mon Aug 19 01:52:40 CEST 2019


>>Assuming there are no other bugs in the libusbxhid library, for writing data you have to guess the reportType (in, out, feature) and reportNum parameter and set the report data buffer correctly (first byte is the report number) for your device. Have a look at similar devices.

I don't know if there is a bug or not... but I "Think" I have the information I need.  I found this device documented here:
https://github.com/rubienr/machinekit/tree/feature-xhc-whb04b-6/src/hal/user_comps/xhc-whb04b-6

it states:  "Data transmitted is packed as 7 bytes plus a constant leading byte 0x06 which is the report ID. The data exclusive report ID reads as follows:"
this matches the python code I was trying to duplicate...  so I send a $06 then 7 bytes of data  at a time.  It also describes the data structure as well.

The C code at https://github.com/rubienr/machinekit/blob/feature-xhc-whb04b-6/src/hal/user_comps/xhc-whb04b-6/usb.cc
Uses libusb... here is the relevant section:  It's in C but it's commented pretty well..  and describes the exact parameters passed to libusb_control_transfer

void Usb::sendDisplayData()
{
    outputPackageBuffer.asBlocks.init(&outputPackageData);
    if (mIsSimulationMode)
   {
        *verboseTxOut << "out   0x" << outputPackageBuffer.asBlocks << endl <<
                      std::dec << "out   size " << sizeof(outputPackageBuffer.asBlockArray) << "B " << outputPackageData
                      << endl;
    }

    for (size_t idx = 0; idx < (sizeof(outputPackageBuffer.asBlockArray) / sizeof(UsbOutPackageBlockFields)); idx++)
    {
        UsbOutPackageBlock& block = outputPackageBuffer.asBlockArray[idx];
        size_t blockSize = sizeof(UsbOutPackageBlock);
        // see also
        // http://www.beyondlogic.org/usbnutshell/usb6.shtml
        // http://libusb.sourceforge.net/api-1.0/group__desc.html
        // http://libusb.sourceforge.net/api-1.0/group__misc.html
        int    r         = libusb_control_transfer(deviceHandle,
            // send to hid descriptor: bmRequestType == LIBUSB_DT_HID == 0x21 == (interface | endpoint)
                                                   LIBUSB_DT_HID,
            // bRequest == LIBUSB_REQUEST_SET_CONFIGURATION == 0x09 == set configuration
                                                   LIBUSB_REQUEST_SET_CONFIGURATION,
            // wValue: if bRequest == LIBUSB_REQUEST_SET_CONFIGURATION the configuration value
                                                   0x0306,
            // wIndex, device interface number
                                                   0x00,
            // data to transmit
                                                   block.asBuffer.asBytes,
            // wLength, data length
                                                   blockSize,
            // transfer timeout[ms]
                                                   0);

        if (r < 0)
        {
            std::cerr << "transmission failed, try to reconnect ..." << endl;
            setDoReconnect(true);
            return;
        }
    }
}


So I tried with libusbxhid: 
libusbhid_set_report(device_context, HID_REPORT_TYPE_FEATURE, $6 , 8 , WhB04_Packet )
and I get  control transfer to usb device failed!

so inside function libusbhid_set_report, I put a bunch of writelns to show what is being passed to libusb_control_transfer:
  Writeln('device handle =');
  Writeln('bmRequestType = $', inttohex(LIBUSB_CONTROL_REQUEST_TYPE_OUT,4));
  Writeln('bRequest      = $', inttohex(HID_SET_REPORT,4)                 );
  Writeln('wValue        = $', inttohex((reportType << 8) or reportNum,4) );
  Writeln('wIndex        = $', 0                                          );
  Writeln('data          = ',  '$'+Inttohex(report_data[0],2),' $'+Inttohex(report_data[1],2),' $'+Inttohex(report_data[2],2),' $'+Inttohex(report_data[3],2),' $'+Inttohex(report_data[4],2),' $'+Inttohex(report_data[5],2),
                              ' $'+Inttohex(report_data[6],2),' $'+Inttohex(report_data[7],2));
  Writeln('wLength       = $', Inttohex(reportLen,2)                      );
  Writeln('timeout       = $', 0                                          );
  Result:=libusb_control_transfer(hid_device_context.usb_device_handle,LIBUSB_CONTROL_REQUEST_TYPE_OUT{????},HID_SET_REPORT,(reportType << 8) or reportNum, 0{interface_num}, @report_data, reportLen{sizeof(data)},0{no timeout});

Suspend
06  FD  FE  FF  01  02  03  04
device handle =
bmRequestType = $0021
bRequest      = $0009
wValue        = $0306
wIndex        = $0
data          = $06 $FD $FE $FF $01 $02 $03 $04
wLength       = $08
timeout       = $0
control transfer to usb device failed!
-1
It shows I'm getting the same values for everything....  but yet it's still failing.  I have this attempt on my fork at https://github.com/Zaaphod/libusbxhid



I'm not sure it's libusbxhid though.. because I also can't get it to work with pas-libusb  I can run test3controlasync.pas with my device and it does send a request to the device and retrieves some information from it... so I modified that program (test3controlasync_2.pas)  to just pass the same paramaters as above and then it doesn't work anymore... here's the output from test3controlasync_2.pas

Running "i:\programming\pas-libusb_test_dll\src\examples\test3controlasync_2.exe "
Submitting control transfer
Finished Transfer, Data = 38, Status = 0, ActualLength = 18
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor            $10CE
  idProduct           $EB93
  bcdDevice            1.00
  iManufacturer           1
  iProduct                0
  iSerialNumber           0
  bNumConfigurations      1
Done.
CT.bmRequestType = 21
CT.bRequest      = 09
CT.wValue        = 306
CT.wIndex        = 00
CT.wLength       = 07
CT.Timeout       = 00
Submitting control transfer
USB Error: Submit: LIBUSB_ERROR_IO: LIBUSB_ERROR_IO  

So it worked for the first information request (the default one including with the original test2controlasync.pas) , but not for my attempted request..  this leads me to believe that I have something wrong and  it's not a bug...  but I'm lost as to what it might be..  my attempt is on my fork here: 
https://github.com/Zaaphod/pas-libusb/blob/Hack/src/examples/test3controlasync_2.pas

 
>> One thing I'm really not sure how to handle with threads is this....  ok so I have my main fast read thread going in the tightest loop possible... but now I want to write something to the display (assuming I ever figure that out and don't just put a sticker over the display and look at the screen instead :D  )  It seems I would have to stop reading to get a chance to write something..  I did a little test and I can do  a suspend(Thread_ID) and resume(Thread_ID)  but what if it's in the middle of a read.. but only got 6 of the 8 bytes when I do the suspend??  Now I am not really ready to write..... so what method can I use to do a suspend_thread_just_before_the_next_read_happens() ?  so that when the thread is suspended, the read is completed and the buffer updated.. etc.. and the USB system and libusb are all ready for me to send my write commands?  Resuming would be no problem.. because it was suspended at the correct position.. I can just resume when I am done with the write and everything should be fine.

>My guess is that it would be better to leave the time sensitive read 
>thread to do the fast reads and immediate calculations and leave writing 
>to the device screen in a separate thread (even main thread), with a 
>lower update frequency!

Yes, that was what I was attempting to describe.. but I can't write to the usb device if the read thread happens to be in the middle of a read... so how do I pause the read thread at the correct time so it's not partially in the middle of reading a packet, and I can't read again until I'm done writing.  I figure I can just check for some period of time of idle handwheel input before I stop the read thread and do the write then start the read thread again, because only the handwheel produces such a large stream of data.. but still I’m not sure how to make sure I stop it at the correct time.

James


More information about the fpc-pascal mailing list