-
-
Notifications
You must be signed in to change notification settings - Fork 7k
PluggableUSB and PluggableHID howto
MIDIUSB or HID libraries are based on PluggableUSB.
Keyboard and Mouse libraries are based on PluggableHID and are bundled with the IDE
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 😄