-
Notifications
You must be signed in to change notification settings - Fork 7.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TinyUSB OUT endpoint uses incorrect endpoint size (IDFGH-8673) #10118
Comments
I guess it should be worth noting I'm using a custom HID descriptor here to get the IN and OUT endpoints... |
I did more testing. I found that writing to the OUT endpoint with a single byte works fine. Trying to send multiple bytes in one packet causes a timeout. |
From this description its hard to guess what may be wrong, but why are you trying to catch invalid packets on report_id 0?
Another question is, are you using |
For whatever reason, the tinyUSB api requires you to use HID_REPORT_TYPE_INVALID when receiving data on OUT endpoints. If you read the code comments in the API you'll see what I'm talking about. |
Sorry, but its hard to read unformatted code here. |
See the link to the code in question. When a transfer is completed from Host to Device, it calls this function and you can see the report type is set to HID_REPORT_TYPE_INVALID. This is how @hathach has decided to implement this. https://github.com/hathach/tinyusb/blob/52a9098b31d993d743ee31efe155b90c7ba5b21e/src/class/hid/hid_device.c#L412 |
Its just like you said, buffer length should be |
As I already said, I'm including the report byte in the size of my payload. Espressif has forked their own branch of TinyUSB. Do they not have any ownership of the bug? |
Sorry, but maybe i dont understand something.
And my understanding is that you are sending 5 bytes from PC + 1 byte is reportID, which is in total 6 bytes.
which is size of 5. Am i understanding correctly? |
@mitchellcairns Hi! Thanks for the pointing out the problem. I'll try to do as much as can to help. May I clarify the device descriptors that you use. I had tried to parse it from the description and that's what I got.
Does it seems the same, if I may ask? UPD. If yes, I probably need to look at the 61-length HID report descriptor. If it is possible, I would like to see it also. Thanks and all the best. |
@mitchellcairns Hi again. I have tried to reproduce the bug and I get the following statement:
Please, leave a comment to agree/disagree with the statement. Anyway, I will try to look the cause of this behavior. |
@mitchellcairns Hi again. Small suggestions:
To make it works with 37, 5 or any other sizes we need a little bit more time and make some changes in the code. |
Im sorry, but i cant agree with this statement. Even recently i am playing with custom "alphanumeric/bitmaped display" descriptor, and its working with varied size. It also may be problem with client app on host. I am using web HID right now and no issue. |
@chegewara No problem, thanks for the information. Let me try to repeat again what I said before, maybe it will help. What does it mean. When we configured EP size for 5 bytes we have the following situation:
Why? Because when output report size is equal to the EP size (e.g. 5), the USB data transfer divided by 5 bytes to collect whole packet during USB transfer but comparison in the end fulfil with the 64 defined max size. That is not correct as we cannot distinguish two scenarios:
Here is the python code that can help reproduce this behavior.
In my opinion, after running such code we should see the following output:
|
I understand that. So i changed my app for this test and here is my descriptor:
as you can size IN and OUT packet size is 4,
I am receiving 5 bytes, where first byte is reportID and 4 bytes its payload. I have slightly opposite results. When i am sending less bytes than
also sending more data than report size is not triggering callback. PS sorry for my previous answers, looks like i clearly didnt understand and didnt test it properly |
@chegewara Thank you for more information. The main idea in your example that you send "test" from web HID app, but in the low level you send [report_id] + [buffer] which is [0x00, 0x74, 0x65, 0x73, 0x74]. This tells us only one thing: when you have sent "tes", actually you have sent 4 bytes (which is equal EP max number in your descriptor). Stack save this data and wait for the next packet. Which is also "tes" (3 bytes from you and 4 bytes in a transport level). And when finally you've send the last one - it is triggered the callback. This will end when the data overflows with 64 bytes. That means you need to send "tes" for 16 times and one more to make an overflow. Then everything stop working. 😕 |
ReportID is not part of payload and is not counted in EP max packet size, at least its how i understand it. |
ReportID is exactly the part of the payload and it is the very first byte of it. UPD: when the ReportID field is present. It can be not present, specification allows that. Anyway, there are some strange moment which need to be verified. |
With HID, the report ID is part of the payload and is included in the packet size count. EG a packet size of 5 could be
I work with many HID devices and this is the case in every situation where Report ID is part of the HID report descriptor. |
Hi everyone! Specification (Device Class Definition for Human Interface Devices (HID) Firmware Specification—5/27/01 Version 1.11) has a list of report constraints (p.54, §8.4) that tells us the main things:
Which means that all the output reports which are more than wMaxPacketSize will be divided into several ones and should be finished with a short report if necessary. During usage of the python script which is presented in description, this script represents a low-level HID driver and should handle the situation when data packet == wMaxPacketSize which also means that it should add the ending with a short packet. It is the responsibility of the HID driver on the host side according to the DCD for HID specification. For example, to send short packet without payload in python script is possible with the following lines:
That will trigger report callback as well. From everything above, tinyUSB stack works correctly and does not require any changes in the code. Thanks for the detailed descriptions and some extra information which had been brought up during discussion and were very helpful. |
Hi everyone, We are closing this issue as there were no more questions based on the issue description. Feel free to reopen. |
@chegewara I am late to the party. As roman-jam already pointed out, though I want to add a bit of explanation with control transfer. HID input/output report can be get/set via BOTH control endpoint and interrupt endpoint. In fact, interrupt endpoint is optional if it is not used often enough e.g in case of keyboard with output for LED indicator.
hope this helps |
I've edited this issue to reflect the actual issue I've found and what the bug might be.
Answers checklist.
IDF version.
v5.0-beta1-824-ga8ef7570ca
Operating System used.
Windows
How did you build your project?
VS Code IDE
If you are using Windows, please specify command line type.
PowerShell
Development Kit.
ESP32-S3-WROOM-1 | Custom PCB & Official Devkit
Power Supply used.
USB
What is the expected behavior?
Each time the USB Device is sent an OUT interrupt report, the tud_hid_set_report_cb should be triggered.
What is the actual behavior?
The callback will be triggered once, then never again. (Confirmed other OUT reports are being sent to the device via Wireshark).
Steps to reproduce.
static const uint8_t gc_hid_configuration_descriptor[] = { // Configuration number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, 1, 0, 41, TUSB_DESC_CONFIG_ATT_SELF_POWERED, 500), // Interface 9, TUSB_DESC_INTERFACE, 0x00, 0x00, 0x02, TUSB_CLASS_HID, 0x00, 0x00, 0x00, // HID Descriptor 9, HID_DESC_TYPE_HID, U16_TO_U8S_LE(0x0110), 0, 1, HID_DESC_TYPE_REPORT, U16_TO_U8S_LE(61), // Endpoint Descriptor 7, TUSB_DESC_ENDPOINT, 0x81, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(37), 1, // Endpoint Descriptor 7, TUSB_DESC_ENDPOINT, 0x02, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(5), 8, };
This descriptor has an OUT endpoint with a size of 5 bytes.
Insert functionality in tud_hid_set_report_cb which will be triggered under the conditions in TinyUSB for data received on the OUT endpoint
if (report_type == HID_REPORT_TYPE_INVALID && report_id == 0) { // Code here to demonstrate the issue. You can change or toggle LED color, as I have been doing, but I'll use a log statement to demonstrate. ESP_LOGI("OUT GOT", "Recieved OUT report: %X", (unsigned int) buffer[0]); }
I set up the Espressif driver with a Libusb driver through Zadig (I installed WinUSB) for further testing.
I used a python script to send an appropriate chunk of data.
`import os
import usb
from usb.backend import libusb1
import time
be = libusb1.get_backend()
brand = 0x303a #Expressif
prod = 0x4004 #ESP32 S3
dev = usb.core.find(idVendor=brand, idProduct=prod, backend=be)
print(dev)
internum = 0
setting = 0
epnum = 0x1
startPayload = [0x11, 0x00, 0x00, 0x00, 0x00]
tmp = dev.write(0x02, startPayload, timeout=1000)`
More Information.
The result is that the write times out.
The fix is to change the OUT size to 6 instead of 5. I believe this is a bug. Even with the report byte being part of the data (which is expected),
The text was updated successfully, but these errors were encountered: