bjanders / fpanels

Go library for interacting with Logitech/Saitek flight panels
MIT License
16 stars 4 forks source link

Unable to set display contents in Windows: libusb: invalid param [code -2] #1

Closed marvk closed 3 years ago

marvk commented 3 years ago

Hey there,

I'm trying to interface with the Logitech Radio Panel in Java, and while trying to set the display contents, I was getting an error.

Since I'm using your lib as a reference anyways, I tried using it to set the display contents and ran into the same error.

I've added an error printout to this line:

https://github.com/bjanders/fpanels/blob/d7b94033586d73b3d820ba89a00605099bebcd65/radiopanel.go#L218

var err error
_, err = panel.device.Control(0x21, 0x09, 0x03, 0x00, panel.displayState[:])
if err != nil {
    log.Fatal(err)
}

and I'm getting the following error:

2021/01/23 23:17:25 libusb: invalid param [code -2]

Was this working for you and do you have any idea for a fix?

My device M/N is J-U0010.

edit: I should add that the go API is returning inputs just fine. It's just not able to display anything.

marvk commented 3 years ago

And by the way, trying to set the display in Java using usb4java-javax:

final byte[] d = new byte[20];
Arrays.fill(d, (byte) ('9' - '0'));

// 0x21
final byte bmRequestType = (UsbConst.ENDPOINT_DIRECTION_OUT | UsbConst.REQUESTTYPE_TYPE_CLASS | UsbConst.REQUESTTYPE_RECIPIENT_INTERFACE);
// 0x09
final byte bRequest = UsbConst.REQUEST_SET_CONFIGURATION;

final UsbControlIrp irp = usbDevice.createUsbControlIrp(
        bmRequestType,
        bRequest,
        (short) 0x03,
        (short) 0x00
);
irp.setData(d);
usbDevice.syncSubmit(irp);

I am getting the following exception which sounds pretty much like what I'm getting in your golang library.

Exception in thread "main" javax.usb.UsbPlatformException: USB error 2: Unable to submit control message: Invalid parameter
    at org.usb4java.javax.ExceptionUtils.createPlatformException(ExceptionUtils.java:39)
    at org.usb4java.javax.AbstractIrpQueue.processControlIrp(AbstractIrpQueue.java:229)
    at org.usb4java.javax.ControlIrpQueue.processIrp(ControlIrpQueue.java:41)
    at org.usb4java.javax.ControlIrpQueue.processIrp(ControlIrpQueue.java:18)
    at org.usb4java.javax.AbstractIrpQueue.process(AbstractIrpQueue.java:104)
    at org.usb4java.javax.AbstractIrpQueue$1.run(AbstractIrpQueue.java:73)
    at java.base/java.lang.Thread.run(Thread.java:832)
bjanders commented 3 years ago

Hi. Everything in my code worked for me the last time I used it, which is quite some time ago. For example, the radiotest program which displays all kinds of strings on the displays, worked without issues. It doesn't work for you? If not, does it give any errors?

Are you running on Linux, Windows or Mac? I've just tested it on Linux on a couple of distros.

Make sure you have right permissions. If on Linux, are you running as root?

Does your device have the same USB vendor and product ID?

marvk commented 3 years ago

For example, the radiotest program which displays all kinds of strings on the displays, worked without issues. It doesn't work for you? If not, does it give any errors?

radiotest.go is what I am running and besides displaying anything, it is working fine. That is, it outputs selector, rotary encoder and button inputs to the console.

There are no errors out of the box, because you are not logging any errors in your RadioPanel.refreshDisplay method, as mentioned in my initial post. If I add error logging there, as I said, I get libusb: invalid param [code -2].

Are you running on Linux, Windows or Mac? I've just tested it on Linux on a couple of distros.

This is on Windows. Running as administrator unfortunately changed nothing.

Does your device have the same USB vendor and product ID?

Yes: Bus 002 Device 003: ID 06a3:0d05

bjanders commented 3 years ago

It could be some difference between Windows and Linux then. I've never tested on Windows. On Linux the panel displayed the output fine the last time I tested.

bjanders commented 3 years ago

I added error checking, and on Linux don't receive any error in the location you mention, and it returns the expected number of bytes written (20). I'll commit error checking to the code after I figure out how I want to do it. I don't want to use logging inside a library, and I'm not sure I want to exit because the error could be transient. Maybe I'll have to create a channel to return errors from the thread that is refreshing the display.

marvk commented 3 years ago

Yeah I figured it might be a OS issue. Do you have an opportunity to test on Windows?

bjanders commented 3 years ago

I can test on Windows, but I'm unfortunately not very motivated to do it. Sorry. I'm currently not using this library myself anymore at all, not even on Linux. But I still might have a look at it on Windows at some point.

bjanders commented 3 years ago

Edit: What I say below is wrong. 0x20 is REQUEST_TYPE_CLASS, which is defined in the gousb library as gousb.ControlClass.

My original comment with the wrong information follows:

It could be that I'm using the go libusb library wrong, because I send Control(0x21,...) which is ENDPONT_OUT (0x00) | CLASS_STANDARD (0x20) | RECEPIENT_INTERCACE (0x01). However, when I just now was replacing these with constants instead of the magic number 0x21, I noticed from the go libusb documentation that constant 0x20 (Standard) is not defined:

// "Standard" is explicitly omitted, as functionality of standard requests
// is exposed through higher level operations of gousb.

So, I should maybe replace this Control() with some "higher level operation", whatever that is.

It's been some time I fiddled with the USB protocol, so it might take some time for me to figure this out.

marvk commented 3 years ago

Well, I was looking through some other libraries and I found DCSFlightpanels, which is written in C# and uses HIDSkeletonBase.HIDWriteDevice?.WriteFeatureData(array);, which seems a bit higher level, see here.

I unfortunately know very little about the USB protocol and am just trying to figure things out as I go along right now, looking at examples such as your library and DCSFlightpanels.

By the way, I think it isn't an issue with the first byte (bmRequestType). I have tried sending every possible byte and it was the same error every time.

bjanders commented 3 years ago

I was able to reproduce the issue on Windows. Don't know what is causing it. I'll happily accept fixing patches ;-)

Sometimes the display lights up with all zeros.

bjanders commented 3 years ago

Does DCSFlightpanels work for you @marvk?

marvk commented 3 years ago

I'd love to be able to tell you, but I don't usually play DCS and I think that software needs some payware to function since the free planes don't have configurations included. So yeah, I can't really tell you at present time unfortunately. But I tried with a HID-Library in Java and also couldn't get that to work either. It's really strange. The Panel itself works fine with the MSFS2020 drivers that Logitech released, but I would like to make some modifications. Quite unfortunate.

bjanders commented 3 years ago

The other panels, the switch and multifunction, have the same issue. Output to the panels gives the error, but switches work.

I play DCS, so maybe I can test DCSFlightpanels some time.

marvk commented 3 years ago

I would appreciate it. I'd love to get to the bottom of this, my goal is to basically emulate the standard FS2020 drivers, but with the radio display ditching the first 1 and displaying three decimal places so you can display 8.33kHz banded frequencies correctly.

bjanders commented 3 years ago

It's not possible to test any output with DCSFlightpanels without using DCS? Just wondering, if you don't want to wait until I possibly test.

marvk commented 3 years ago

I don't think it is. I even downloaded DCS, but I couldn't get it to work with it. I think I might be missing the right planes.

bjanders commented 3 years ago

I tested with Logitech's Flight Panels Test Software and did a USB dump (which I originally also used to reverse engineer it in the first place) . The output to me looks the same as to what I try to do:

image

The only difference being that Logitech's software sends 22 bytes, and I send 20. I tried with 22 bytes, and it didn't help. I'm starting to suspect that the error is in libusb. When sending with my software nothing is seen in the USB capture, so it is probably being blocked by Windows.

bjanders commented 3 years ago

I compiled libusb with debug logging enabled and made a Go implementation that is as simple as possible (about 5 lines of code, nothing from this fpanels library) to just output to the display, and got the following output:

[ 0.014260] [00001154] libusb: debug [libusb_open] open 2.17
[ 0.076927] [00001154] libusb: debug [hid_open] set maximum input buffer size to 512
[ 0.076945] [00001154] libusb: debug [hid_open] 0 HID input report value(s) found
[ 0.076946] [00001154] libusb: debug [hid_open] 0 HID output report value(s) found
[ 0.076946] [00001154] libusb: debug [hid_open] 1 HID feature report value(s) found
[ 0.076947] [00001154] libusb: debug [hid_open]   Report ID: 0x00
[ 0.081734] [00001154] libusb: debug [libusb_alloc_transfer] transfer 00000177b241c588
[ 0.081737] [00001154] libusb: debug [libusb_submit_transfer] transfer 00000177b241c588
[ 0.081740] [00001154] libusb: debug [libusb_claim_interface] interface 0
[ 0.081740] [00001154] libusb: debug [hid_claim_interface] claimed interface 0
[ 0.081743] [00001154] libusb: debug [windows_assign_endpoints] (re)assigned endpoint 81 to interface 0
[ 0.081744] [00001154] libusb: debug [auto_claim] auto-claimed interface 0 for control request
[ 0.081744] [00001154] libusb: debug [hid_submit_control_transfer] will use interface 0
[ 0.081744] [00001154] libusb: warning [_hid_set_report] unknown HID report type 0
[ 0.081745] [00001154] libusb: debug [libusb_release_i2021/02/22 22:54:32 libusb: invalid param [code -2]

Looking at the libusb code, the issue seems to be "unknown HID report type 0", when it should be HID_REPORT_TYPE_FEATURE, which is a parameter to _hid_set_report(). Now I need to backtrace from where this parameter originates and this will be fixed. ;-)

bjanders commented 3 years ago

I got past that problem, but now I got new ones.

I changed

panel.device.Control(0x21, 0x09, 0x03, 0x00, panel.displayState[:])

to

panel.device.Control(0x21, 0x09, 0x0300, 0x00, panel.displayState[:])

By looking at the libusb code, the high byte in 0x0300 is HID_REPORT_TYPE_FEATURE, and the low byte is the report ID.

Looking at the USB dump above, this is wValue, which actually is 0x0300 (and not 0x0003, like I have).

bjanders commented 3 years ago

This is fixed now with the latest commit. :-) Thanks for the report!

In your Java code just change 0x03 to 0x0300 and make sure you use a buffer length of 22 bytes (not 20) for the display contents.

There is still some issue with the multi panel on Windows, that I'm still looking in to, but the radio panel works now.

marvk commented 3 years ago

Yup, confirmed working! Thanks for letting me know how it's done! :-)

https://user-images.githubusercontent.com/6569856/108795955-d6389800-7587-11eb-845a-482da655d15a.mp4