-
Notifications
You must be signed in to change notification settings - Fork 0
PluggableUSB and PluggableHID howto
##An example is worth a thousand words
[MIDIUSB] (https://github.com/arduino-libraries/MIDIUSB) or [HID] (https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/libraries/HID) libraries are based on PluggableUSB.
[Keyboard] (https://github.com/arduino/Arduino/tree/master/libraries/Keyboard) and [Mouse] (https://github.com/arduino/Arduino/tree/master/libraries/Mouse) libraries are based on PluggableHID and are bundled with the IDE
##Writing a library as a PluggableUSB Module
If you wish to write a library that exposes low-level USB functionality
like MIDI
or Mass Storage
you can take advantage of the new PluggableUSB
core.
Attaching to this framework is very simple: first of all your library needs to include PluggableUSB header:
#include "PluggableUSB.h"
Then, you need to implement a PluggableUSBModule, so the constructor will look like
/* we need 2 endpoints and 2 interfaces */
MIDI_::MIDI_(void) : PluggableUSBModule(2, 2, epType)
{
epType[0] = EP_TYPE_BULK_OUT_MIDI; // MIDI_ENDPOINT_OUT
epType[1] = EP_TYPE_BULK_IN_MIDI; // MIDI_ENDPOINT_IN
PluggableUSB().plug(this);
}
The PluggableUSBModule
must implement setup
, getInterface
and getDescriptor
functions and declare how many endpoints and interfaces it needs to allocate
setup
function signature is bool setup(USBSetup& usb_setup)
;
it is expected to return true if the request was directed to the module and executed correctly, false otherwise.
If no setup phase is required, simply return false
< examples from HID.cpp - simplified >
bool HID_::setup(USBSetup& setup)
{
if (pluggedInterface != setup.wIndex) {
return false;
}
uint8_t request = setup.bRequest;
uint8_t requestType = setup.bmRequestType;
etc etc...
getDescriptor
function signature is int getDescriptor(USBSetup& setup)
;
it is expected to return the number of bytes sent if the request was directed to the module, 0 if the request has not been served or -1 if errors has been encountered.
If no device descriptor is required, simply return 0
< examples from HID.cpp - simplified >
int HID_::getDescriptor(USBSetup& setup)
{
// Check if this is a HID Class Descriptor request
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; }
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; }
// In a HID Class Descriptor wIndex cointains the interface number
if (setup.wIndex != pluggedInterface) { return 0; }
int total = 0;
HIDSubDescriptor* node;
for (node = rootNode; node; node = node->next) {
int res = USB_SendControl(TRANSFER_PGM, node->data, node->length);
if (res == -1)
return -1;
total += res;
}
return total;
}
getInterface
function signature is int getInterface(uint8_t* interfaceCount)
;
it is expected to return the number of bytes sent and increment the interfaceNum
variable with the number of interfaces used.
< examples from HID.cpp - simplified >
int HID_::getInterface(uint8_t* interfaceCount)
{
*interfaceCount += 1; // uses 1
HIDDescriptor hidInterface = {
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
D_HIDREPORT(descriptorSize),
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01)
};
return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
}
Everything is configured, so calling PluggableUSB().plug(this)
will do all the magic.
Your library will be plugged when the constructor is called, so you need to pre-instantiate a singleton for your class to make sure to be already plugged when main()
is executed.
Then you can perform USB writes calling, for example,
USB_Send(pluggedEndpoint, data, len);
If you are interested in writing a library for a specific HID peripheral (Mouse, Keyboard, Touchscreen, Gamepad etc) you can take advantage of the PluggableHID core. No need to explore the darkest corners of USB specifications!
In your library
#include "HID.h"
in your header file.
If the core you are targeting is pluggable-ready, _USING_HID
will be defined.
#ifndef MOUSE_h
#define MOUSE_h
#include "HID.h"
#if !defined(_USING_HID)
#warning "Using legacy HID core (non pluggable)"
#else
etc etc .....
In the cpp file, add a const report descriptor of your choice
static const u8 _hidReportDescriptor[] PROGMEM = {
// Mouse
0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 54
0x09, 0x02, // USAGE (Mouse)
.
. etc etc
.
}
In the constructor call add the following snippet
Mouse_::Mouse_(void) : _buttons(0)
{
static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor));
HID().AppendDescriptor(&node);
}
And you are done 😄