As I mentioned in the other thread:
I was curious about how hard it would be to support some different USB Objects like Mouse and Keyboards using the Arduino_USBHostMbed5 library. There is some support in the library USBHostGIGA. I am assuming you cannot use both of these libraries within a single sketch.
support USBLS (low speed) devices within Arduino_USBHostMbed5. I have a version that is at least somewhat working. It is a fork/branch based off of @BobTheDog fork/branch that is associated with a current Pull request.
Once I clean up the changes and his PR or other derivation of it is pulled in, I will issue PR for the LS devices.
HID Support
Sorry not sure if anyone else will be interested in this stuff, and this first pass information is probably disjoint and the like, but...
There is a simple HID Call back class:
class USBHostHIDParserCB {
public:
virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax) {};
virtual void hid_input_data(uint32_t usage, int32_t value);
virtual void hid_input_end() {};
};
And simple HID parser class:
class USBHostHIDParser {
public:
bool init(USBHost *host, USBDeviceConnected *dev, uint8_t index, uint16_t len);
void parse(const uint8_t *data, uint16_t len);
inline void attach(USBHostHIDParserCB *hidCB) {
hidCB_ = hidCB;
}
...
private:
...
};
My mouse and keyboard objects contain an instance of this class and are derived from the call back class.
USBHostHIDParser hidParser;
class USBHostMouseEx : public IUSBEnumerator, public USBHostHIDParserCB {
public:
When a device is connected code initializes the hidParser object:
bool USBHostMouseEx::connect() {
if (dev_connected) {
return true;
}
host = USBHost::getHostInst();
for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) {
if ((dev = host->getDevice(i)) != NULL) {
int ret = host->enumerate(dev, this);
if (ret) {
break;
}
if (mouse_device_found) {
{
/* As this is done in a specific thread
* this lock is taken to avoid to process the device
* disconnect in usb process during the device registering */
USBHost::Lock Lock(host);
int_in = dev->getEndpoint(mouse_intf, INTERRUPT_ENDPOINT, IN);
if (!int_in) {
break;
}
USB_INFO("New Mouse device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, mouse_intf);
dev->setName("Mouse", mouse_intf);
host->registerDriver(dev, mouse_intf, this, &USBHostMouseEx::init);
int_in->attach(this, &USBHostMouseEx::rxHandler);
size_in_ = int_in->getSize();
hidParser.init(host, dev, mouse_intf, hid_descriptor_size_);
hidParser.attach(this);
}
host->interruptRead(dev, int_in, buf_in_, size_in_, false);
dev_connected = true;
return true;
}
}
}
init();
return false;
}
Most of this is pretty much like boiler plate stuff, except the two lines:
hidParser.init(host, dev, mouse_intf, hid_descriptor_size_);
hidParser.attach(this);
The init call, is passed in the interface number, for the HID. Note you potentially might have multiple HID interfaces. For Mouse I pass in the information from the first Interface and for Keyboard I pass it in for the second.
You also pass in the size of the HID descriptor (Or some larger value).
The USBHost code in the main library, when it detects a Config item that contains the size of the HID Descriptor, it stores it away. I know normally devices, first declare their interface descriptor, followed by the End points.
So in the function asking if we want to use some end point, I ask for this and store it away:
/virtual/ bool USBHostMouseEx::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type,
ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used
{
printf("intf_nb: %d\n", intf_nb);
printf(" ??? HID Report size: %u\n", host->getLengthReportDescr());
if (intf_nb == mouse_intf) {
if (type == INTERRUPT_ENDPOINT && dir == IN) {
mouse_device_found = true;
hid_descriptor_size_ = host->getLengthReportDescr();
return true;
}
}
return false;
}
The call: hidParser.attach(this); will then give a pointer to our main object to the parser object.
The Mouse object implements all three of the main call back functions:
/*virtual*/ void USBHostMouseEx::hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax) {
//printf("Mouse HID Begin(%lx %lx %d %d)\n", topusage, type, lgmin, lgmax);
hid_input_begin_ = true;
}
/*virtual*/ void USBHostMouseEx::hid_input_end() {
//printf("Mouse HID end()\n");
if (hid_input_begin_) {
mouseEvent = true;
hid_input_begin_ = false;
}
}
/*virtual*/ void USBHostMouseEx::hid_input_data(uint32_t usage, int32_t value) {
//printf("Mouse: usage=%lX, value=%ld\n", usage, value);
uint32_t usage_page = usage >> 16;
usage &= 0xFFFF;
if (usage_page == 9 && usage >= 1 && usage <= 8) {
if (value == 0) {
buttons &= ~(1 << (usage - 1));
} else {
buttons |= (1 << (usage - 1));
}
} else if (usage_page == 1) {
switch (usage) {
case 0x30:
mouseX = value;
break;
case 0x31:
mouseY = value;
break;
case 0x32: // Apple uses this for horizontal scroll
wheelH = value;
break;
case 0x38:
wheel = value;
break;
}
} else if (usage_page == 12) {
if (usage == 0x238) { // Microsoft uses this for horizontal scroll
wheelH = value;
}
}
}
When the device sends us a new data record, my RX Handler code passes it off to the parser like:
void USBHostMouseEx::rxHandler() {
int len = int_in->getLengthTransferred();
if (len) {
//Serial.println("$$$ Extras HID RX $$$");
//MemoryHexDump(Serial, buf_in_, len, true, nullptr, -1, 0);
hidParser.parse(buf_in_, len);
}
host->interruptRead(dev, int_in, buf_in_, size_in_, false);
}
The Parser code will walk through the data.
The most important part is for each field it sees it will call our method:
/virtual/ void USBHostMouseEx::hid_input_data(uint32_t usage, int32_t value) {
The usage values are the upper 2 bytes is the Usage Page and the bottom two is the usage number within that page:
These numbers are defined in the USB Hid Usage Table document (HUT)
Which you can download from:
HID Usage Tables 1.4 | USB-IF
In the case of a Mouse, most of the details are from
Usage 1 - General Desktop page: including X, Y...
Usage 9 - Button Page: the buttons on the mouse
Probably enough for first draft:
The code is up at:
KurtE/GIGA_USBHostMBed5_devices: Some USB Host device extension to Arduino_USBHostMBed5 library (github.com)
And again this is just an experiment!