nyholku / purejavahidapi

Other
120 stars 69 forks source link

How to set output reports exceeding max report size #44

Closed Frank12B closed 7 years ago

Frank12B commented 7 years ago

Hi, I am in the situation that I need to split multiple output report requests (look at the example below). I know that in general when issuing an control transfer that exceeds max report length the setup packet, in particular the wLength field, tells the device that there will "follow" another data packet to be "combined" with the forerunner.

So when i use HidDevice.setOutputReport(byte reportID, byte[] data, int length) I write the first 8 bytes of the request. But how do I tell the device that there will follow another data packet? I noticed the parameter long bOFF in the Memory.write function. Do I have to raise it equal to the length of the following data packet, use m_OutputReportMemory.write to write the additional packet again? I do not really understand what Kernel32Library.WriteFile does.

In the example below the device returns me the first 8 bytes written and thats all. I would be glad if you could give me a hint ;)

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;

import purejavahidapi.HidDevice;
import purejavahidapi.HidDeviceInfo;
import purejavahidapi.InputReportListener;
import purejavahidapi.PureJavaHidApi;

public class HidTest {

    private static int VENDOR_ID = (short) 0x0665;
    private static int PRODUCT_ID = (short) 0x5161;

    @SuppressWarnings("unused")
    private static byte[] original = { 81, 80, 73, 71, 83, 50, 104, 45, 13 }; // 9 bytes -> 1 byte too large

    private static byte[] split1 = { 81, 80, 73, 71, 83, 50, 104, 45 }; // first 8 bytes

    private static byte[] split2 = { 13, 0, 0, 0, 0, 0, 0, 0 }; //last byte combined with 0s

    private static void test() {

        HidDeviceInfo devInfo = null;

        List<HidDeviceInfo> devList = PureJavaHidApi.enumerateDevices();

        for (HidDeviceInfo info : devList) {
            if (info.getVendorId() == VENDOR_ID && info.getProductId() == PRODUCT_ID) {
                devInfo = info;
                System.out.println(devInfo.getUsagePage());
                break;
            }
        }

        try {

            HidDevice dev = PureJavaHidApi.openDevice(devInfo);
            dev.setInputReportListener(new InputReportListener() {

                @Override
                public void onInputReport(HidDevice arg0, byte id, byte[] data, int len) {

                    System.out.println(new String(data, StandardCharsets.US_ASCII));
                    System.out.printf("onInputReport: id %d len %d data ", id, len);
                    for (int i = 0; i < len; i++)
                        System.out.print(data[i]);

                    System.out.println();
                };
            });

            System.out.println(dev.setOutputReport((byte) 0x00, split1, 8));
            System.out.println(dev.setOutputReport((byte) 0x00, split2, 8));

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        test();
    }

}
nyholku commented 7 years ago

Not sure I follow you here.

AFAIU a report is a report and cannot be (and there is no need) split at user space level where this library operates. You can send/receive reports of any length. Control transfers are lower level concepts and do not mix well with HID API requests. The device defines what kind and size of reports it expects or delivers and the OS takes care of sending/retrieving the reports as per USB spec and in the user space we only need (and can only) deal with full reports.

Frank12B commented 7 years ago

Ok i am sry and try to make it clear now. My device is a low speed device that only accepts data packets of only 8 bytes plus report id. Everything works when I use commands that do not exceed this limit. So I tried several things: a) Sending the report as a whole --> device returned only first 8 bytes and no bulk transaction from the device followed (which is to be expected) b) Sending the report split (inspired by this article: https://www.board4all.biz/threads/send-string-to-usb-hid.629897/ ) --> same as before

If I understand you correctly, I should simply send the whole data packet (the "original" byte array in the example). But I was not successful in doing that. As I already said, it seems to me that only the first 8 bytes was sent. Or maybe I am just foolish, I cant see atm what is going wrong. I checked the commands, crc16 calculation and so on, I dont see the mistake.

I hope I could make it more understandable what my problem is. I am maybe at the beginning of understanding this topic, so pls be patient if I misused some terms.

I attached the communication protocol for my device, its an inverter for solar electricity, maybe it helps.

ForumEA-L-Communication Protocol-NEW.pdf

nyholku commented 7 years ago

From a cursory look this appears to be a serial communication protocol. I would suspect that the device looks like a serial port to the application so what makes you think you should/can use HID?

If indeed it looks like a serial port (COM on Windows) to the applications then you can you PureJavaComm for that. But perhaps the first step would be to use a terminal emulator and just manually send something to the device and see that you get a response.

Frank12B commented 7 years ago

It definitely is a HID device.. I see it being represented as HID device in my device manager and I am communicating with others who were using codeminders hidapi. And as i already wrote in my comment before, all reports not exceeding the maximum size of 8 can be sent successfully and the device responds as expected. Additionally there exists a Java program called WatchPower. I used a sniffer to watch the communication between WatchPower and device so I saw that this program was sending the report split up with a device response in between that 8 bytes were received. After firstSplitPart/response/secondSplitPart the device began to send its data. This was the reason why I decided to ask you about that. When I am at home, I can provide you a log to prove this.

nyholku commented 7 years ago

No need for log, I believe when I'm told :)

However, not being an expert in UBS or HID, I would think that splitting should be done at the OS / driver level automatically, perhaps take this issue up at libusb-devel list, lots of knowledgable people there.

nyholku commented 7 years ago

"I used a sniffer to watch the communication between WatchPower and device so I saw that this program was sending the report split up with a device response in between that 8 bytes were"

If you observed the low level communication, this is no indication how things work at the higher level. But if you have tried submitting full length commands and they do not work then I'm at loss why that would not work. If you can sniff things you could perhaps also sniff how the long transfers using PureJavaHidApi differ from those that work ...

But like I said, presenting this problem and your splitting scenario on the libusb-devel list might provide a lot useful information even if this is definitely not a libusb problem.

Also as a debug tool you could write a short C-program using signal11 HIDAPI to see if you can make it work with that. Just a simple piece of code to send messages shorter and longer than eight bytes.

Frank12B commented 7 years ago

Oh dear I solved it!!! I dont know the reason why but I simply never executed the program, I always used the debugger to see what is going on. The example i gave you works, but the time between sending the two reports is decisive! Sending the whole report doesnt work. I hope I didnt waste your time, maybe this piece of information is of interest for you. I will try to change your Code that I wont have to use setoutputreport twice. Thank you for your patience and for writing this api!!!

nyholku commented 7 years ago

Great that you solved it!

However I do think that PureHavaHidApi works as intended and that modifying the code is 'wrong' in the sense that it is not possible to split reports. My thinking is that what you actually are doing and need to do is to split the contents of the report to multiple reports. A report is conceptually an atomic entity from the USB standard point of view and you cannot split it. What you can do and what I guess is your device is doing is that it concatenates the contents of several reports and interpreters them as a single command. You are of course welcome to modify the code as you wish but I would suggest that the "correct" approach is to write simple wrapper "above" PureJavaHidApi layer that splits your commands to multiple reports.