HID
HID copied to clipboard
No OUT endpoint in RawHID on SAMD21
It seems RawHID does not advertise an OUT endpoint. At least on SAMD. I tried it with the RawHID example on a SAMD21 board. I was able to receive data but it crashes as soon as I tried writing to it. My plattform is Ubuntu 18.04 Running lsusb gave this which seems to indicated that there is simply no endpoint to send the data to.
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 0 No Subclass
bInterfaceProtocol 0 None
iInterface 0
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.01
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 28
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x84 EP 4 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Device Status: 0x0000
(Bus Powered)
@facchinm maybe you have any insight? It seems you did all the porting work.
From @NicoHood 's code RawHID only exposes an IN endpoint (see https://github.com/NicoHood/HID/blob/master/src/SingleReport/RawHID.cpp#L52) also on AVR. Some minor modifications to that file should exposes the OUT EP, but I don't know if it will work since I never tested it :wink:
Ah yes, I was just trying to understand where this is constructed. Thanks for the input! I will have a further look but I guess @NicoHood might need to given an insight on how this is supposed to work.
It was never working relyably, and its been years since I wrote all this USB stuff. I am sorry, I cannot help here.
I see, looking at the commits it seems this was all done in a small sprint back in 2015 :)
I would be pretty interested in getting a reliable RawHID library going for Arduino. Do you maybe know someone (or yourself but it sounds you are not that interested) who might be interested to fix or re-write the RawHID part to a reliable state? The company I work for could allocate some funds to sponsor that development.
I have bidirectional usb hid working on an arduino due, by modifying HID.h / HID.cpp. I can post it here if you'd like to take a look, but it's pretty simple to get working.
Sure! Every little bit would be great. If no one else has interest in making a cross plattform raw hid I would take it on but I have a steep learning curve ahead so a point to start would certainly be great :)
Here you are, there is a platformio project for the due that will echo usb packets, and a java application that will write to and then read from that device.
Sorry I don't have much time to clean it up or comment it, but you can see the changes I applied to HID.h / HID.cpp and maybe get it working properly.
https://github.com/PhilipAnderson/due-raw-usb-hid-test
I should note that I don't really know what I'm doing here, but what I have done is working and is useful for me at least.
Thank you! Very helpful.
That's fascinating that it isn't working for you guys. I do note that lsusb does NOT list an output endpoint, yet despite that, it appears to work anyway.
The example program, "RawHID" works as-is, including the RawHID.write(...) lines. I am able to successfully receive the data sent by RawHID.write on the host. In fact, it even works when I run 2 instances of RawHID, i.e.,
RawHID_ hidB; ... hidB.begin(...); ... hidB.write(...);
It even keeps all the reads/writes to both RawHID and hidB properly separated.
- This is on a SAMD21.
So you are able to receive data sent to the host or you are able to receive data on the MCU sent by the host?
MCU to host worked fine, host to mcu just failed. lsusb does report outputs on other devices for me, so I'm not sure that is correct.
Just as I said.. BOTH.
@PTS93
What crashed for you? Was it the host? Or the MCU? If it was the MCU, I wonder if "megabuf" might be too long for something. I haven't actually tried sending any more than a 4-byte array, which is all that my project needs. If it was the host side, it might be helpful to start off with something fairly simple;
Edit: without really understanding this stuff entirely, tracing back the TX and RX size from the report description here; https://github.com/NicoHood/HID/blob/master/src/SingleReport/RawHID.cpp#L40 leads to 64 bytes, ultimately here; https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/USB/USBAPI.h#L28
Compile with gcc, and when you run it, it expects a parameter /dev/hidrawX
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <linux/types.h>
#include <linux/input.h>
#include <linux/hidraw.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int hid_fd = -1;
int openhid(char *device){
int fd;
int i, res, desc_size = 0;
char buf[256];
struct hidraw_report_descriptor rpt_desc;
struct hidraw_devinfo info;
/* Open the Device with non-blocking reads. In real life,
don't use a hard coded path; use libudev instead. */
fd = open(device, O_RDWR|O_NONBLOCK);
if (fd < 0) {
printf("Unable to open device\n");
return -1;
}
memset(&rpt_desc, 0x0, sizeof(rpt_desc));
memset(&info, 0x0, sizeof(info));
memset(buf, 0x0, sizeof(buf));
/* Get Report Descriptor Size */
res = ioctl(fd, HIDIOCGRDESCSIZE, &desc_size);
if (res < 0)
printf("Failed to get HIDIOCGRDESCSIZE\n");
else
printf("Report Descriptor Size: %d\n", desc_size);
/* Get Raw Info */
res = ioctl(fd, HIDIOCGRAWINFO, &info);
if (res < 0) {
printf("Failed to get HIDIOCGRAWINFO");
} else {
printf("Raw Info:\n");
printf("\tbustype: %d\n", info.bustype);
printf("\tvendor: 0x%04hx\n", info.vendor);
printf("\tproduct: 0x%04hx\n", info.product);
}
if ((int16_t)info.vendor != (int16_t)0x239a){
printf("INFO.VENDOR: 0x%04hx\n", info.vendor);
close(fd);
return -1;
}
if ((int16_t)info.product != (int16_t)0x801e){
printf("INFO.PRODUCT: 0x%04hx\n", (int16_t)info.product);
close(fd);
return -1;
}
return fd;
}
void writehid(char* value, int len){
unsigned char buf[16];
int res,i;
buf[0] = 0x0; // Report ID (0)
buf[1] = 0x20; // Command (0x20)
for (i=0; i<len; i++)
buf[i+2] = value[i];
res = write(hid_fd, buf, 2+len);
if (res < 0) {
printf("Write error: %d\n", errno);
} else {
printf("write() wrote %d bytes\n", res);
}
// Give the device some time to respond.
sleep(1);
// Get a report from the device * /
res = read(hid_fd, buf, 16);
if (res < 0) {
printf("Read error\n");
} else {
printf("read() read %d bytes: ", res);
for (i = 0; i < res; i++)
printf("%hhx ", buf[i]);
printf("\n");
}
}
int main(int argc, char ** argv){
int i;
hid_fd = openhid(argv[1]);
printf("hid_fd: %d\n", hid_fd);
if (hid_fd >= 0) writehid("test", 4);
else printf("No filedes, not writing.\n");
}
And note that I have it set up as a request/response communications pattern. So in your sketch, you can do something like putting a RawHID.write(...) at this point; https://github.com/NicoHood/HID/blob/master/examples/RawHID/RawHID/RawHID.ino#L67
Otherwise you will want to move the read in the host side program from the bottom of the writehid function into a loop at the bottom of the main, or change it to blocking reads.
Looking at the code again... We do not need an out endpoint, as you can handle this with the control endpoint as well. Thatswhy I did not use it. https://github.com/NicoHood/HID/blob/master/src/SingleReport/RawHID.cpp#L52 https://github.com/NicoHood/HID/blob/master/src/SingleReport/RawHID.cpp#L134-L142
Ooh, weird ok, cool :) So technically I guess it should work? @lbdroid I only tested it with node-hid a node.js library for working with HID devices. It works fine with Teensy so I assumed it shouldn't make a difference with this one. I will try it again with the C examples.
One thing I just realized that you want to keep track of... apparently I hardcoded a vid/pid in there. You won't want to forget to change that to match your hardware, OR, just delete the checks.
And start with a 1-byte send.
#116 is related. Just linking it since I found that answer first.
As a point of comparison, here is the result from the Teensy RawHID implementation:
# lsusb -vvvv
Bus 001 Device 057: ID 16c0:0486 Van Ooijen Technische Informatica Teensyduino RawHID
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 1.10
bDeviceClass 0 (Defined at Interface level)
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x16c0 Van Ooijen Technische Informatica
idProduct 0x0486 Teensyduino RawHID
bcdDevice 2.76
iManufacturer 1 Teensyduino
iProduct 2 Teensyduino RawHID
iSerial 3 3284590
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 73
bNumInterfaces 2
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xc0
Self Powered
MaxPower 100mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 0 No Subclass
bInterfaceProtocol 0 None
iInterface 0
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.11
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 28
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x04 EP 4 OUT
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
I used this Java/Groovy program to test:
https://gist.github.com/madhephaestus/2f03c9588113d58e22d23a30e1eaaecc
I tested this using an official Arduino Zero, and connected over the native USB port
The java program can open the Teensy RawHID, send bytes, and received bytes back. Using the Example, i modified it to send bytes after receiving. It was able to open and receive bytes, but could not send data back from the Zero to the java program. On the teensy the java program receives data.
jfyi I'm using tinyusb for this now, its a very expansive USB abstraction layer that exposes raw hid besides many other things https://github.com/adafruit/Adafruit_TinyUSB_Arduino/tree/master/examples/HID/hid_generic_inout
@PTS93 do you know how to get that running on the Arduino Zero?
I think the Adafruit SAMD core still includes the Arduino Zero. If not all you have to do is copy the entry from the Arduino SAMD core in boards.txt over and copy the arduino_zero folder found in the variant folder of the core.
@madhephaestus have you looked into using zephyr instead of arduino? It's a proper rtos and infinitely more capable, and hidraw works flawlessly.