[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