driver/keyboardioHID: astyle

Signed-off-by: Gergely Nagy <algernon@keyboard.io>
f/driver/keyboardiohid-orig
Gergely Nagy 4 years ago committed by Jesse Vincent
parent 2c4a274cb3
commit 35b1fb239c
No known key found for this signature in database
GPG Key ID: 122F5DF7108E4046

@ -29,199 +29,199 @@ THE SOFTWARE.
// See Appendix B of USB HID spec // See Appendix B of USB HID spec
static const uint8_t _hidReportDescriptorKeyboard[] PROGMEM = { static const uint8_t _hidReportDescriptorKeyboard[] PROGMEM = {
// Keyboard // Keyboard
D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP,
D_USAGE, D_USAGE_KEYBOARD, D_USAGE, D_USAGE_KEYBOARD,
D_COLLECTION, D_APPLICATION, D_COLLECTION, D_APPLICATION,
// Modifiers // Modifiers
D_USAGE_PAGE, D_PAGE_KEYBOARD, D_USAGE_PAGE, D_PAGE_KEYBOARD,
D_USAGE_MINIMUM, 0xe0, D_USAGE_MINIMUM, 0xe0,
D_USAGE_MAXIMUM, 0xe7, D_USAGE_MAXIMUM, 0xe7,
D_LOGICAL_MINIMUM, 0x0, D_LOGICAL_MINIMUM, 0x0,
D_LOGICAL_MAXIMUM, 0x1, D_LOGICAL_MAXIMUM, 0x1,
D_REPORT_SIZE, 0x1, D_REPORT_SIZE, 0x1,
D_REPORT_COUNT, 0x8, D_REPORT_COUNT, 0x8,
D_INPUT, (D_DATA|D_VARIABLE|D_ABSOLUTE), D_INPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE),
// Reserved byte // Reserved byte
D_REPORT_COUNT, 0x1, D_REPORT_COUNT, 0x1,
D_REPORT_SIZE, 0x8, D_REPORT_SIZE, 0x8,
D_INPUT, (D_CONSTANT), D_INPUT, (D_CONSTANT),
// LEDs // LEDs
D_REPORT_COUNT, 0x5, D_REPORT_COUNT, 0x5,
D_REPORT_SIZE, 0x1, D_REPORT_SIZE, 0x1,
D_USAGE_PAGE, D_PAGE_LEDS, D_USAGE_PAGE, D_PAGE_LEDS,
D_USAGE_MINIMUM, 0x1, D_USAGE_MINIMUM, 0x1,
D_USAGE_MAXIMUM, 0x5, D_USAGE_MAXIMUM, 0x5,
D_OUTPUT, (D_DATA|D_VARIABLE|D_ABSOLUTE), D_OUTPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE),
// Pad LEDs up to a byte // Pad LEDs up to a byte
D_REPORT_COUNT, 0x1, D_REPORT_COUNT, 0x1,
D_REPORT_SIZE, 0x3, D_REPORT_SIZE, 0x3,
D_OUTPUT, (D_CONSTANT), D_OUTPUT, (D_CONSTANT),
// Non-modifiers // Non-modifiers
D_REPORT_COUNT, 0x6, D_REPORT_COUNT, 0x6,
D_REPORT_SIZE, 0x8, D_REPORT_SIZE, 0x8,
D_LOGICAL_MINIMUM, 0x0, D_LOGICAL_MINIMUM, 0x0,
D_LOGICAL_MAXIMUM, 0xff, D_LOGICAL_MAXIMUM, 0xff,
D_USAGE_PAGE, D_PAGE_KEYBOARD, D_USAGE_PAGE, D_PAGE_KEYBOARD,
D_USAGE_MINIMUM, 0x0, D_USAGE_MINIMUM, 0x0,
D_USAGE_MAXIMUM, 0xff, D_USAGE_MAXIMUM, 0xff,
D_INPUT, (D_DATA|D_ARRAY|D_ABSOLUTE), D_INPUT, (D_DATA | D_ARRAY | D_ABSOLUTE),
D_END_COLLECTION D_END_COLLECTION
}; };
BootKeyboard_::BootKeyboard_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1), leds(0) { BootKeyboard_::BootKeyboard_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1), leds(0) {
epType[0] = EP_TYPE_INTERRUPT_IN; epType[0] = EP_TYPE_INTERRUPT_IN;
} }
int BootKeyboard_::getInterface(uint8_t* interfaceCount) { int BootKeyboard_::getInterface(uint8_t* interfaceCount) {
*interfaceCount += 1; // uses 1 *interfaceCount += 1; // uses 1
HIDDescriptor hidInterface = { HIDDescriptor hidInterface = {
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_BOOT_INTERFACE, HID_PROTOCOL_KEYBOARD), D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_BOOT_INTERFACE, HID_PROTOCOL_KEYBOARD),
D_HIDREPORT(sizeof(_hidReportDescriptorKeyboard)), D_HIDREPORT(sizeof(_hidReportDescriptorKeyboard)),
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01)
}; };
return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
} }
int BootKeyboard_::getDescriptor(USBSetup& setup) { int BootKeyboard_::getDescriptor(USBSetup& setup) {
// Check if this is a HID Class Descriptor request // Check if this is a HID Class Descriptor request
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) {
return 0; return 0;
} }
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) {
return 0; return 0;
} }
// In a HID Class Descriptor wIndex cointains the interface number // In a HID Class Descriptor wIndex cointains the interface number
if (setup.wIndex != pluggedInterface) { if (setup.wIndex != pluggedInterface) {
return 0; return 0;
} }
// Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol
// due to the USB specs, but Windows and Linux just assumes its in report mode. // due to the USB specs, but Windows and Linux just assumes its in report mode.
protocol = default_protocol; protocol = default_protocol;
return USB_SendControl(TRANSFER_PGM, _hidReportDescriptorKeyboard, sizeof(_hidReportDescriptorKeyboard)); return USB_SendControl(TRANSFER_PGM, _hidReportDescriptorKeyboard, sizeof(_hidReportDescriptorKeyboard));
} }
void BootKeyboard_::begin(void) { void BootKeyboard_::begin(void) {
PluggableUSB().plug(this); PluggableUSB().plug(this);
// Force API to send a clean report. // Force API to send a clean report.
// This is important for and HID bridge where the receiver stays on, // This is important for and HID bridge where the receiver stays on,
// while the sender is resetted. // while the sender is resetted.
releaseAll(); releaseAll();
sendReport(); sendReport();
} }
void BootKeyboard_::end(void) { void BootKeyboard_::end(void) {
releaseAll(); releaseAll();
sendReport(); sendReport();
} }
bool BootKeyboard_::setup(USBSetup& setup) { bool BootKeyboard_::setup(USBSetup& setup) {
if (pluggedInterface != setup.wIndex) { if (pluggedInterface != setup.wIndex) {
return false; return false;
} }
uint8_t request = setup.bRequest; uint8_t request = setup.bRequest;
uint8_t requestType = setup.bmRequestType; uint8_t requestType = setup.bmRequestType;
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) { if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) {
if (request == HID_GET_REPORT) { if (request == HID_GET_REPORT) {
// TODO(anyone): HID_GetReport(); // TODO(anyone): HID_GetReport();
return true; return true;
} }
if (request == HID_GET_PROTOCOL) { if (request == HID_GET_PROTOCOL) {
// TODO(anyone) improve // TODO(anyone) improve
#ifdef __AVR__ #ifdef __AVR__
UEDATX = protocol; UEDATX = protocol;
#endif #endif
#ifdef ARDUINO_ARCH_SAM #ifdef ARDUINO_ARCH_SAM
USBDevice.armSend(0, &protocol, 1); USBDevice.armSend(0, &protocol, 1);
#endif #endif
return true; return true;
} }
if (request == HID_GET_IDLE) { if (request == HID_GET_IDLE) {
// TODO(anyone) improve // TODO(anyone) improve
#ifdef __AVR__ #ifdef __AVR__
UEDATX = idle; UEDATX = idle;
#endif #endif
#ifdef ARDUINO_ARCH_SAM #ifdef ARDUINO_ARCH_SAM
USBDevice.armSend(0, &idle, 1); USBDevice.armSend(0, &idle, 1);
#endif #endif
return true; return true;
}
} }
}
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) { if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) {
if (request == HID_SET_PROTOCOL) { if (request == HID_SET_PROTOCOL) {
protocol = setup.wValueL; protocol = setup.wValueL;
return true; return true;
} }
if (request == HID_SET_IDLE) { if (request == HID_SET_IDLE) {
// We currently ignore SET_IDLE, because we don't really do anything with it, and implementing // We currently ignore SET_IDLE, because we don't really do anything with it, and implementing
// it causes issues on OSX, such as key chatter. Other operating systems do not suffer if we // it causes issues on OSX, such as key chatter. Other operating systems do not suffer if we
// force this to zero, either. // force this to zero, either.
#if 0 #if 0
idle = setup.wValueL; idle = setup.wValueL;
#else #else
idle = 0; idle = 0;
#endif #endif
return true; return true;
}
if (request == HID_SET_REPORT) {
// Check if data has the correct length afterwards
int length = setup.wLength;
if (setup.wValueH == HID_REPORT_TYPE_OUTPUT) {
if (length == sizeof(leds)) {
USB_RecvControl(&leds, length);
return true;
} }
if (request == HID_SET_REPORT) { } else { // Input (set HID report)
// Check if data has the correct length afterwards if (setup.wValueH == HID_REPORT_TYPE_INPUT) {
int length = setup.wLength; if (length == sizeof(_keyReport)) {
USB_RecvControl(&_keyReport, length);
if (setup.wValueH == HID_REPORT_TYPE_OUTPUT) { return true;
if (length == sizeof(leds)) { }
USB_RecvControl(&leds, length);
return true;
}
} else { // Input (set HID report)
if (setup.wValueH == HID_REPORT_TYPE_INPUT) {
if (length == sizeof(_keyReport)) {
USB_RecvControl(&_keyReport, length);
return true;
}
}
}
} }
}
} }
}
return false; return false;
} }
uint8_t BootKeyboard_::getLeds(void) { uint8_t BootKeyboard_::getLeds(void) {
return leds; return leds;
} }
uint8_t BootKeyboard_::getProtocol(void) { uint8_t BootKeyboard_::getProtocol(void) {
return protocol; return protocol;
} }
void BootKeyboard_::setProtocol(uint8_t protocol) { void BootKeyboard_::setProtocol(uint8_t protocol) {
this->protocol = protocol; this->protocol = protocol;
} }
int BootKeyboard_::sendReport(void) { int BootKeyboard_::sendReport(void) {
if (memcmp(&_lastKeyReport, &_keyReport, sizeof(_keyReport))) { if (memcmp(&_lastKeyReport, &_keyReport, sizeof(_keyReport))) {
// if the two reports are different, send a report // if the two reports are different, send a report
int returnCode = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, &_keyReport, sizeof(_keyReport)); int returnCode = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, &_keyReport, sizeof(_keyReport));
HIDReportObserver::observeReport(HID_REPORTID_KEYBOARD, &_keyReport, sizeof(_keyReport), returnCode); HIDReportObserver::observeReport(HID_REPORTID_KEYBOARD, &_keyReport, sizeof(_keyReport), returnCode);
memcpy(&_lastKeyReport, &_keyReport, sizeof(_keyReport)); memcpy(&_lastKeyReport, &_keyReport, sizeof(_keyReport));
return returnCode; return returnCode;
} }
return -1; return -1;
} }
// press() adds the specified key (printing, non-printing, or modifier) // press() adds the specified key (printing, non-printing, or modifier)
@ -231,35 +231,35 @@ int BootKeyboard_::sendReport(void) {
size_t BootKeyboard_::press(uint8_t k) { size_t BootKeyboard_::press(uint8_t k) {
uint8_t done = 0; uint8_t done = 0;
if ((k >= HID_KEYBOARD_FIRST_MODIFIER) && (k <= HID_KEYBOARD_LAST_MODIFIER)) { if ((k >= HID_KEYBOARD_FIRST_MODIFIER) && (k <= HID_KEYBOARD_LAST_MODIFIER)) {
// it's a modifier key // it's a modifier key
_keyReport.modifiers |= (0x01 << (k - HID_KEYBOARD_FIRST_MODIFIER)); _keyReport.modifiers |= (0x01 << (k - HID_KEYBOARD_FIRST_MODIFIER));
} else { } else {
// it's some other key: // it's some other key:
// Add k to the key report only if it's not already present // Add k to the key report only if it's not already present
// and if there is an empty slot. // and if there is an empty slot.
for (uint8_t i = 0; i < sizeof(_keyReport.keycodes); i++) { for (uint8_t i = 0; i < sizeof(_keyReport.keycodes); i++) {
if (_keyReport.keycodes[i] != k) { // is k already in list? if (_keyReport.keycodes[i] != k) { // is k already in list?
if (0 == _keyReport.keycodes[i]) { // have we found an empty slot? if (0 == _keyReport.keycodes[i]) { // have we found an empty slot?
_keyReport.keycodes[i] = k; _keyReport.keycodes[i] = k;
done = 1; done = 1;
break; break;
}
} else {
done = 1;
break;
}
}
// use separate variable to check if slot was found
// for style reasons - we do not know how the compiler
// handles the for() index when it leaves the loop
if (0 == done) {
return 0;
} }
} else {
done = 1;
break;
}
}
// use separate variable to check if slot was found
// for style reasons - we do not know how the compiler
// handles the for() index when it leaves the loop
if (0 == done) {
return 0;
} }
return 1; }
return 1;
} }
@ -268,43 +268,43 @@ size_t BootKeyboard_::press(uint8_t k) {
// it shouldn't be repeated any more. // it shouldn't be repeated any more.
size_t BootKeyboard_::release(uint8_t k) { size_t BootKeyboard_::release(uint8_t k) {
if ((k >= HID_KEYBOARD_FIRST_MODIFIER) && (k <= HID_KEYBOARD_LAST_MODIFIER)) { if ((k >= HID_KEYBOARD_FIRST_MODIFIER) && (k <= HID_KEYBOARD_LAST_MODIFIER)) {
// it's a modifier key // it's a modifier key
_keyReport.modifiers = _keyReport.modifiers & (~(0x01 << (k - HID_KEYBOARD_FIRST_MODIFIER))); _keyReport.modifiers = _keyReport.modifiers & (~(0x01 << (k - HID_KEYBOARD_FIRST_MODIFIER)));
} else { } else {
// it's some other key: // it's some other key:
// Test the key report to see if k is present. Clear it if it exists. // Test the key report to see if k is present. Clear it if it exists.
// Check all positions in case the key is present more than once (which it shouldn't be) // Check all positions in case the key is present more than once (which it shouldn't be)
for (uint8_t i = 0; i < sizeof(_keyReport.keycodes); i++) { for (uint8_t i = 0; i < sizeof(_keyReport.keycodes); i++) {
if (_keyReport.keycodes[i] == k) { if (_keyReport.keycodes[i] == k) {
_keyReport.keycodes[i] = 0; _keyReport.keycodes[i] = 0;
} }
} }
// rearrange the keys list so that the free (= 0x00) are at the // rearrange the keys list so that the free (= 0x00) are at the
// end of the keys list - some implementations stop for keys at the // end of the keys list - some implementations stop for keys at the
// first occurence of an 0x00 in the keys list // first occurence of an 0x00 in the keys list
// so (0x00)(0x01)(0x00)(0x03)(0x02)(0x00) becomes // so (0x00)(0x01)(0x00)(0x03)(0x02)(0x00) becomes
// (0x03)(0x02)(0x01)(0x00)(0x00)(0x00) // (0x03)(0x02)(0x01)(0x00)(0x00)(0x00)
uint8_t current = 0, nextpos = 0; uint8_t current = 0, nextpos = 0;
while (current < sizeof(_keyReport.keycodes)) { while (current < sizeof(_keyReport.keycodes)) {
if (_keyReport.keycodes[current]) { if (_keyReport.keycodes[current]) {
uint8_t tmp = _keyReport.keycodes[nextpos]; uint8_t tmp = _keyReport.keycodes[nextpos];
_keyReport.keycodes[nextpos] = _keyReport.keycodes[current]; _keyReport.keycodes[nextpos] = _keyReport.keycodes[current];
_keyReport.keycodes[current] = tmp; _keyReport.keycodes[current] = tmp;
++nextpos; ++nextpos;
} }
++current; ++current;
}
} }
}
return 1; return 1;
} }
void BootKeyboard_::releaseAll(void) { void BootKeyboard_::releaseAll(void) {
memset(&_keyReport.bytes, 0x00, sizeof(_keyReport.bytes)); memset(&_keyReport.bytes, 0x00, sizeof(_keyReport.bytes));
} }
@ -312,24 +312,24 @@ void BootKeyboard_::releaseAll(void) {
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean BootKeyboard_::isKeyPressed(uint8_t k) { boolean BootKeyboard_::isKeyPressed(uint8_t k) {
for (uint8_t i = 0; i < sizeof(_keyReport.keycodes); i++) { for (uint8_t i = 0; i < sizeof(_keyReport.keycodes); i++) {
if (_keyReport.keycodes[i] == k) { if (_keyReport.keycodes[i] == k) {
return true; return true;
}
} }
return false; }
return false;
} }
/* Returns true if the non-modifer key passed in was sent during the previous key report /* Returns true if the non-modifer key passed in was sent during the previous key report
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean BootKeyboard_::wasKeyPressed(uint8_t k) { boolean BootKeyboard_::wasKeyPressed(uint8_t k) {
for (uint8_t i = 0; i < sizeof(_keyReport.keycodes); i++) { for (uint8_t i = 0; i < sizeof(_keyReport.keycodes); i++) {
if (_lastKeyReport.keycodes[i] == k) { if (_lastKeyReport.keycodes[i] == k) {
return true; return true;
}
} }
return false; }
return false;
} }
@ -338,36 +338,36 @@ boolean BootKeyboard_::wasKeyPressed(uint8_t k) {
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean BootKeyboard_::isModifierActive(uint8_t k) { boolean BootKeyboard_::isModifierActive(uint8_t k) {
if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) {
k = k - HID_KEYBOARD_FIRST_MODIFIER; k = k - HID_KEYBOARD_FIRST_MODIFIER;
return !!(_keyReport.modifiers & (1 << k)); return !!(_keyReport.modifiers & (1 << k));
} }
return false; return false;
} }
/* Returns true if the modifer key passed in was being sent during the previous key report /* Returns true if the modifer key passed in was being sent during the previous key report
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean BootKeyboard_::wasModifierActive(uint8_t k) { boolean BootKeyboard_::wasModifierActive(uint8_t k) {
if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) {
k = k - HID_KEYBOARD_FIRST_MODIFIER; k = k - HID_KEYBOARD_FIRST_MODIFIER;
return !!(_lastKeyReport.modifiers & (1 << k)); return !!(_lastKeyReport.modifiers & (1 << k));
} }
return false; return false;
} }
/* Returns true if any modifier key will be sent during this key report /* Returns true if any modifier key will be sent during this key report
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean BootKeyboard_::isAnyModifierActive() { boolean BootKeyboard_::isAnyModifierActive() {
return _keyReport.modifiers > 0; return _keyReport.modifiers > 0;
} }
/* Returns true if any modifier key was being sent during the previous key report /* Returns true if any modifier key was being sent during the previous key report
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean BootKeyboard_::wasAnyModifierActive() { boolean BootKeyboard_::wasAnyModifierActive() {
return _lastKeyReport.modifiers > 0; return _lastKeyReport.modifiers > 0;
} }
BootKeyboard_ BootKeyboard; BootKeyboard_ BootKeyboard;

@ -33,52 +33,52 @@ THE SOFTWARE.
#include "../HIDAliases.h" #include "../HIDAliases.h"
typedef union { typedef union {
// Low level key report: up to 6 keys and shift, ctrl etc at once // Low level key report: up to 6 keys and shift, ctrl etc at once
struct { struct {
uint8_t modifiers; uint8_t modifiers;
uint8_t reserved; uint8_t reserved;
uint8_t keycodes[6]; uint8_t keycodes[6];
}; };
uint8_t bytes[8]; uint8_t bytes[8];
} HID_BootKeyboardReport_Data_t; } HID_BootKeyboardReport_Data_t;
class BootKeyboard_ : public PluggableUSBModule { class BootKeyboard_ : public PluggableUSBModule {
public: public:
BootKeyboard_(void); BootKeyboard_(void);
size_t press(uint8_t); size_t press(uint8_t);
void begin(void); void begin(void);
void end(void); void end(void);
size_t release(uint8_t); size_t release(uint8_t);
void releaseAll(void); void releaseAll(void);
int sendReport(void); int sendReport(void);
boolean isModifierActive(uint8_t k); boolean isModifierActive(uint8_t k);
boolean wasModifierActive(uint8_t k); boolean wasModifierActive(uint8_t k);
boolean isAnyModifierActive(); boolean isAnyModifierActive();
boolean wasAnyModifierActive(); boolean wasAnyModifierActive();
boolean isKeyPressed(uint8_t k); boolean isKeyPressed(uint8_t k);
boolean wasKeyPressed(uint8_t k); boolean wasKeyPressed(uint8_t k);
uint8_t getLeds(void); uint8_t getLeds(void);
uint8_t getProtocol(void); uint8_t getProtocol(void);
void setProtocol(uint8_t protocol); void setProtocol(uint8_t protocol);
uint8_t default_protocol = HID_REPORT_PROTOCOL; uint8_t default_protocol = HID_REPORT_PROTOCOL;
protected: protected:
HID_BootKeyboardReport_Data_t _keyReport, _lastKeyReport; HID_BootKeyboardReport_Data_t _keyReport, _lastKeyReport;
// Implementation of the PUSBListNode // Implementation of the PUSBListNode
int getInterface(uint8_t* interfaceCount); int getInterface(uint8_t* interfaceCount);
int getDescriptor(USBSetup& setup); int getDescriptor(USBSetup& setup);
bool setup(USBSetup& setup); bool setup(USBSetup& setup);
EPTYPE_DESCRIPTOR_SIZE epType[1]; EPTYPE_DESCRIPTOR_SIZE epType[1];
uint8_t protocol; uint8_t protocol;
uint8_t idle; uint8_t idle;
uint8_t leds; uint8_t leds;
}; };
extern BootKeyboard_ BootKeyboard; extern BootKeyboard_ BootKeyboard;

@ -65,38 +65,38 @@ THE SOFTWARE.
D_INPUT, (D_DATA|D_VARIABLE|D_RELATIVE), D_INPUT, (D_DATA|D_VARIABLE|D_RELATIVE),
typedef union { typedef union {
// Absolute mouse report: 8 buttons, 2 absolute axis, wheel // Absolute mouse report: 8 buttons, 2 absolute axis, wheel
struct { struct {
uint8_t buttons; uint8_t buttons;
uint16_t xAxis;
uint16_t yAxis;
int8_t wheel;
};
} HID_MouseAbsoluteReport_Data_t;
class AbsoluteMouseAPI {
public:
inline AbsoluteMouseAPI(void);
inline void begin(void);
inline void end(void);
inline void click(uint8_t b = MOUSE_LEFT);
inline void moveTo(uint16_t x, uint16_t y, signed char wheel = 0);
inline void move(int x, int y, signed char wheel = 0);
inline void press(uint8_t b = MOUSE_LEFT);
inline void release(uint8_t b = MOUSE_LEFT);
inline bool isPressed(uint8_t b = MOUSE_LEFT);
// Sending is public in the base class for advanced users.
virtual void sendReport(void* data, int length) {}
protected:
uint16_t xAxis; uint16_t xAxis;
uint16_t yAxis; uint16_t yAxis;
uint8_t _buttons; int8_t wheel;
};
} HID_MouseAbsoluteReport_Data_t;
inline void buttons(uint8_t b); class AbsoluteMouseAPI {
inline int16_t qadd16(int16_t base, int16_t increment); public:
inline AbsoluteMouseAPI(void);
inline void begin(void);
inline void end(void);
inline void click(uint8_t b = MOUSE_LEFT);
inline void moveTo(uint16_t x, uint16_t y, signed char wheel = 0);
inline void move(int x, int y, signed char wheel = 0);
inline void press(uint8_t b = MOUSE_LEFT);
inline void release(uint8_t b = MOUSE_LEFT);
inline bool isPressed(uint8_t b = MOUSE_LEFT);
// Sending is public in the base class for advanced users.
virtual void sendReport(void* data, int length) {}
protected:
uint16_t xAxis;
uint16_t yAxis;
uint8_t _buttons;
inline void buttons(uint8_t b);
inline int16_t qadd16(int16_t base, int16_t increment);
}; };
#include "AbsoluteMouseAPI.hpp" #include "AbsoluteMouseAPI.hpp"

@ -24,80 +24,80 @@
#if defined(USBCON) #if defined(USBCON)
HID_& HID() { HID_& HID() {
static HID_ obj; static HID_ obj;
return obj; return obj;
} }
int HID_::getInterface(uint8_t* interfaceCount) { int HID_::getInterface(uint8_t* interfaceCount) {
*interfaceCount += 1; // uses 1 *interfaceCount += 1; // uses 1
HIDDescriptor hidInterface = { HIDDescriptor hidInterface = {
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE), D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
D_HIDREPORT(descriptorSize), D_HIDREPORT(descriptorSize),
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01)
}; };
return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
} }
int HID_::getDescriptor(USBSetup& setup) { int HID_::getDescriptor(USBSetup& setup) {
// Check if this is a HID Class Descriptor request // Check if this is a HID Class Descriptor request
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) {
return 0; return 0;
} }
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) {
return 0; return 0;
} }
// In a HID Class Descriptor wIndex cointains the interface number
if (setup.wIndex != pluggedInterface) {
return 0;
}
int total = 0;
HIDSubDescriptor* node;
USB_PackMessages(true);
for (node = rootNode; node; node = node->next) {
int res = USB_SendControl(TRANSFER_PGM, node->data, node->length);
if (res == -1)
return -1;
total += res;
}
// Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol
// due to the USB specs, but Windows and Linux just assumes its in report mode.
protocol = HID_REPORT_PROTOCOL;
USB_PackMessages(false); // In a HID Class Descriptor wIndex cointains the interface number
return total; if (setup.wIndex != pluggedInterface) {
return 0;
}
int total = 0;
HIDSubDescriptor* node;
USB_PackMessages(true);
for (node = rootNode; node; node = node->next) {
int res = USB_SendControl(TRANSFER_PGM, node->data, node->length);
if (res == -1)
return -1;
total += res;
}
// Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol
// due to the USB specs, but Windows and Linux just assumes its in report mode.
protocol = HID_REPORT_PROTOCOL;
USB_PackMessages(false);
return total;
} }
__attribute__((weak)) __attribute__((weak))
uint8_t HID_::getShortName(char *name) { uint8_t HID_::getShortName(char *name) {
name[0] = 'k'; name[0] = 'k';
name[1] = 'b'; name[1] = 'b';
name[2] = 'i'; name[2] = 'i';
name[3] = 'o'; name[3] = 'o';
name[4] = '0'; name[4] = '0';
name[5] = '1'; name[5] = '1';
return 6; return 6;
} }
void HID_::AppendDescriptor(HIDSubDescriptor *node) { void HID_::AppendDescriptor(HIDSubDescriptor *node) {
if (!rootNode) { if (!rootNode) {
rootNode = node; rootNode = node;
} else { } else {
HIDSubDescriptor *current = rootNode; HIDSubDescriptor *current = rootNode;
while (current->next) { while (current->next) {
current = current->next; current = current->next;
}
current->next = node;
} }
descriptorSize += node->length; current->next = node;
}
descriptorSize += node->length;
} }
int HID_::SendReport(uint8_t id, const void* data, int len) { int HID_::SendReport(uint8_t id, const void* data, int len) {
auto result = SendReport_(id, data, len); auto result = SendReport_(id, data, len);
HIDReportObserver::observeReport(id, data, len, result); HIDReportObserver::observeReport(id, data, len, result);
return result; return result;
} }
int HID_::SendReport_(uint8_t id, const void* data, int len) { int HID_::SendReport_(uint8_t id, const void* data, int len) {
@ -109,78 +109,78 @@ int HID_::SendReport_(uint8_t id, const void* data, int len) {
* costs RAM, which is something scarce on AVR. So on that platform, we opt to * costs RAM, which is something scarce on AVR. So on that platform, we opt to
* send the id and the report separately instead. */ * send the id and the report separately instead. */
#ifdef ARDUINO_ARCH_SAMD #ifdef ARDUINO_ARCH_SAMD
uint8_t p[64]; uint8_t p[64];
p[0] = id; p[0] = id;
memcpy(&p[1], data, len); memcpy(&p[1], data, len);
return USB_Send(pluggedEndpoint, p, len+1); return USB_Send(pluggedEndpoint, p, len + 1);
#else #else
auto ret = USB_Send(pluggedEndpoint, &id, 1); auto ret = USB_Send(pluggedEndpoint, &id, 1);
if (ret < 0) return ret; if (ret < 0) return ret;
auto ret2 = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, len); auto ret2 = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, len);
if (ret2 < 0) return ret2; if (ret2 < 0) return ret2;
return ret + ret2; return ret + ret2;
#endif #endif
} }
bool HID_::setup(USBSetup& setup) { bool HID_::setup(USBSetup& setup) {
if (pluggedInterface != setup.wIndex) { if (pluggedInterface != setup.wIndex) {
return false; return false;
} }
uint8_t request = setup.bRequest; uint8_t request = setup.bRequest;
uint8_t requestType = setup.bmRequestType; uint8_t requestType = setup.bmRequestType;
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) {
if (request == HID_GET_REPORT) {
// TODO(anyone): HID_GetReport();
return true;
}
if (request == HID_GET_PROTOCOL) {
// TODO(anyone): Send8(protocol);
return true;
}
if (request == HID_GET_IDLE) {
// TODO(anyone): Send8(idle);
}
}
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) { if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) {
if (request == HID_SET_PROTOCOL) { if (request == HID_GET_REPORT) {
// The USB Host tells us if we are in boot or report mode. // TODO(anyone): HID_GetReport();
// This only works with a real boot compatible device. return true;
protocol = setup.wValueL; }
return true; if (request == HID_GET_PROTOCOL) {
} // TODO(anyone): Send8(protocol);
if (request == HID_SET_IDLE) { return true;
idle = setup.wValueL; }
return true; if (request == HID_GET_IDLE) {
} // TODO(anyone): Send8(idle);
if (request == HID_SET_REPORT) {
uint16_t length = setup.wLength;
if (length == sizeof(setReportData)) {
USB_RecvControl(&setReportData, length);
} else if (length == sizeof(setReportData.leds)) {
USB_RecvControl(&setReportData.leds, length);
setReportData.reportId = 0;
}
}
} }
}
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) {
if (request == HID_SET_PROTOCOL) {
// The USB Host tells us if we are in boot or report mode.
// This only works with a real boot compatible device.
protocol = setup.wValueL;
return true;
}
if (request == HID_SET_IDLE) {
idle = setup.wValueL;
return true;
}
if (request == HID_SET_REPORT) {
uint16_t length = setup.wLength;
if (length == sizeof(setReportData)) {
USB_RecvControl(&setReportData, length);
} else if (length == sizeof(setReportData.leds)) {
USB_RecvControl(&setReportData.leds, length);
setReportData.reportId = 0;
}
}
}
return false; return false;
} }
HID_::HID_(void) : PluggableUSBModule(1, 1, epType), HID_::HID_(void) : PluggableUSBModule(1, 1, epType),
rootNode(NULL), descriptorSize(0), rootNode(NULL), descriptorSize(0),
protocol(HID_REPORT_PROTOCOL), idle(1) { protocol(HID_REPORT_PROTOCOL), idle(1) {
setReportData.reportId = 0; setReportData.reportId = 0;
setReportData.leds = 0; setReportData.leds = 0;
epType[0] = EP_TYPE_INTERRUPT_IN; epType[0] = EP_TYPE_INTERRUPT_IN;
PluggableUSB().plug(this); PluggableUSB().plug(this);
} }
int HID_::begin(void) { int HID_::begin(void) {
return 0; return 0;
} }
#endif /* if defined(USBCON) */ #endif /* if defined(USBCON) */

@ -59,63 +59,63 @@
#define HID_REPORT_TYPE_FEATURE 3 #define HID_REPORT_TYPE_FEATURE 3
typedef struct { typedef struct {
uint8_t len; // 9 uint8_t len; // 9
uint8_t dtype; // 0x21 uint8_t dtype; // 0x21
uint8_t addr; uint8_t addr;
uint8_t versionL; // 0x101 uint8_t versionL; // 0x101
uint8_t versionH; // 0x101 uint8_t versionH; // 0x101
uint8_t country; uint8_t country;
uint8_t desctype; // 0x22 report uint8_t desctype; // 0x22 report
uint8_t descLenL; uint8_t descLenL;
uint8_t descLenH; uint8_t descLenH;
} HIDDescDescriptor; } HIDDescDescriptor;
typedef struct { typedef struct {
InterfaceDescriptor hid; InterfaceDescriptor hid;
HIDDescDescriptor desc; HIDDescDescriptor desc;
EndpointDescriptor in; EndpointDescriptor in;
} HIDDescriptor; } HIDDescriptor;
class HIDSubDescriptor { class HIDSubDescriptor {
public: public:
HIDSubDescriptor *next = NULL; HIDSubDescriptor *next = NULL;
HIDSubDescriptor(const void *d, const uint16_t l) : data(d), length(l) { } HIDSubDescriptor(const void *d, const uint16_t l) : data(d), length(l) { }
const void* data; const void* data;
const uint16_t length; const uint16_t length;
}; };
class HID_ : public PluggableUSBModule { class HID_ : public PluggableUSBModule {
public: public:
HID_(void); HID_(void);
int begin(void); int begin(void);
int SendReport(uint8_t id, const void* data, int len); int SendReport(uint8_t id, const void* data, int len);
void AppendDescriptor(HIDSubDescriptor* node); void AppendDescriptor(HIDSubDescriptor* node);
uint8_t getLEDs(void) { uint8_t getLEDs(void) {
return setReportData.leds; return setReportData.leds;
} }
protected: protected:
// Implementation of the PluggableUSBModule // Implementation of the PluggableUSBModule
int getInterface(uint8_t* interfaceCount); int getInterface(uint8_t* interfaceCount);
int getDescriptor(USBSetup& setup); int getDescriptor(USBSetup& setup);
bool setup(USBSetup& setup); bool setup(USBSetup& setup);
uint8_t getShortName(char* name); uint8_t getShortName(char* name);
int SendReport_(uint8_t id, const void* data, int len); int SendReport_(uint8_t id, const void* data, int len);
private: private:
EPTYPE_DESCRIPTOR_SIZE epType[1]; EPTYPE_DESCRIPTOR_SIZE epType[1];
HIDSubDescriptor* rootNode; HIDSubDescriptor* rootNode;
uint16_t descriptorSize; uint16_t descriptorSize;
uint8_t protocol; uint8_t protocol;
uint8_t idle; uint8_t idle;
struct { struct {
uint8_t reportId; uint8_t reportId;
uint8_t leds; uint8_t leds;
} setReportData; } setReportData;
}; };
// Replacement for global singleton. // Replacement for global singleton.

@ -26,29 +26,30 @@ THE SOFTWARE.
#include <stdint.h> #include <stdint.h>
class HIDReportObserver class HIDReportObserver {
{ public:
public:
typedef void(*SendReportHook)(uint8_t id, const void* data,
typedef void(*SendReportHook)(uint8_t id, const void* data, int len, int result);
int len, int result);
static void observeReport(uint8_t id, const void* data,
static void observeReport(uint8_t id, const void* data, int len, int result) {
int len, int result) { if (send_report_hook_) {
if(send_report_hook_) { (*send_report_hook_)(id, data, len, result);
(*send_report_hook_)(id, data, len, result);
}
} }
}
static SendReportHook currentHook() { return send_report_hook_; }
static SendReportHook currentHook() {
static SendReportHook resetHook(SendReportHook new_hook) { return send_report_hook_;
auto previous_hook = send_report_hook_; }
send_report_hook_ = new_hook;
return previous_hook; static SendReportHook resetHook(SendReportHook new_hook) {
} auto previous_hook = send_report_hook_;
send_report_hook_ = new_hook;
private: return previous_hook;
}
static SendReportHook send_report_hook_;
private:
static SendReportHook send_report_hook_;
}; };

@ -38,7 +38,7 @@ THE SOFTWARE.
// Not every HID usage listed in this file is currently supported by Arduino // Not every HID usage listed in this file is currently supported by Arduino
// In particular, any System Control or Consumer Control entry that doesn't // In particular, any System Control or Consumer Control entry that doesn't
// have a comment indicating that it's "HID type OSC" may require additional // have a comment indicating that it's "HID type OSC" may require additional
// code in the Arduino core to work, although // some keycodes with usage // code in the Arduino core to work, although // some keycodes with usage
// type RTC (Re-Trigger Control) and OOC (On/Off Control) are also functional. // type RTC (Re-Trigger Control) and OOC (On/Off Control) are also functional.
// //
// Non-working usages are listed here in the interest of not having to manually // Non-working usages are listed here in the interest of not having to manually

@ -27,12 +27,12 @@ THE SOFTWARE.
// Keyboard Leds // Keyboard Leds
enum KeyboardLeds : uint8_t { enum KeyboardLeds : uint8_t {
LED_NUM_LOCK = (1 << 0), LED_NUM_LOCK = (1 << 0),
LED_CAPS_LOCK = (1 << 1), LED_CAPS_LOCK = (1 << 1),
LED_SCROLL_LOCK = (1 << 2), LED_SCROLL_LOCK = (1 << 2),
LED_COMPOSE = (1 << 3), LED_COMPOSE = (1 << 3),
LED_KANA = (1 << 4), LED_KANA = (1 << 4),
LED_POWER = (1 << 5), LED_POWER = (1 << 5),
LED_SHIFT = (1 << 6), LED_SHIFT = (1 << 6),
LED_DO_NOT_DISTURB = (1 << 7), LED_DO_NOT_DISTURB = (1 << 7),
}; };

@ -27,27 +27,27 @@ THE SOFTWARE.
#include "../DescriptorPrimitives.h" #include "../DescriptorPrimitives.h"
static const uint8_t _hidMultiReportDescriptorAbsoluteMouse[] PROGMEM = { static const uint8_t _hidMultiReportDescriptorAbsoluteMouse[] PROGMEM = {
/* Mouse absolute */ /* Mouse absolute */
D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) 54 */ D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) 54 */
D_USAGE, D_USAGE_MOUSE, /* USAGE (Mouse) */ D_USAGE, D_USAGE_MOUSE, /* USAGE (Mouse) */
D_COLLECTION, D_APPLICATION, /* COLLECTION (Application) */ D_COLLECTION, D_APPLICATION, /* COLLECTION (Application) */
D_REPORT_ID, HID_REPORTID_MOUSE_ABSOLUTE, /* REPORT_ID */ D_REPORT_ID, HID_REPORTID_MOUSE_ABSOLUTE, /* REPORT_ID */
DESCRIPTOR_ABS_MOUSE_BUTTONS DESCRIPTOR_ABS_MOUSE_BUTTONS
DESCRIPTOR_ABS_MOUSE_XY DESCRIPTOR_ABS_MOUSE_XY
DESCRIPTOR_ABS_MOUSE_WHEEL DESCRIPTOR_ABS_MOUSE_WHEEL
D_END_COLLECTION /* End */ D_END_COLLECTION /* End */
}; };
AbsoluteMouse_::AbsoluteMouse_(void) { AbsoluteMouse_::AbsoluteMouse_(void) {
static HIDSubDescriptor node(_hidMultiReportDescriptorAbsoluteMouse, sizeof(_hidMultiReportDescriptorAbsoluteMouse)); static HIDSubDescriptor node(_hidMultiReportDescriptorAbsoluteMouse, sizeof(_hidMultiReportDescriptorAbsoluteMouse));
HID().AppendDescriptor(&node); HID().AppendDescriptor(&node);
} }
void AbsoluteMouse_::sendReport(void* data, int length) { void AbsoluteMouse_::sendReport(void* data, int length) {
HID().SendReport(HID_REPORTID_MOUSE_ABSOLUTE, data, length); HID().SendReport(HID_REPORTID_MOUSE_ABSOLUTE, data, length);
} }
AbsoluteMouse_ AbsoluteMouse; AbsoluteMouse_ AbsoluteMouse;

@ -32,12 +32,12 @@ THE SOFTWARE.
#include "../DeviceAPIs/AbsoluteMouseAPI.h" #include "../DeviceAPIs/AbsoluteMouseAPI.h"
class AbsoluteMouse_ : public AbsoluteMouseAPI { class AbsoluteMouse_ : public AbsoluteMouseAPI {
public: public:
AbsoluteMouse_(void); AbsoluteMouse_(void);
protected: protected:
// Sending is public in the base class for advanced users. // Sending is public in the base class for advanced users.
virtual void sendReport(void* data, int length); virtual void sendReport(void* data, int length);
}; };
extern AbsoluteMouse_ AbsoluteMouse; extern AbsoluteMouse_ AbsoluteMouse;

@ -27,81 +27,81 @@ THE SOFTWARE.
#include "../DescriptorPrimitives.h" #include "../DescriptorPrimitives.h"
static const uint8_t _hidMultiReportDescriptorConsumer[] PROGMEM = { static const uint8_t _hidMultiReportDescriptorConsumer[] PROGMEM = {
/* Consumer Control (Sound/Media keys) */ /* Consumer Control (Sound/Media keys) */
D_USAGE_PAGE, 0x0C, /* usage page (consumer device) */ D_USAGE_PAGE, 0x0C, /* usage page (consumer device) */
D_USAGE, 0x01, /* usage -- consumer control */ D_USAGE, 0x01, /* usage -- consumer control */
D_COLLECTION, D_APPLICATION, /* collection (application) */ D_COLLECTION, D_APPLICATION, /* collection (application) */
D_REPORT_ID, HID_REPORTID_CONSUMERCONTROL, /* report id */ D_REPORT_ID, HID_REPORTID_CONSUMERCONTROL, /* report id */
/* 4 Media Keys */ /* 4 Media Keys */
D_LOGICAL_MINIMUM, 0x00, /* logical minimum */ D_LOGICAL_MINIMUM, 0x00, /* logical minimum */
D_MULTIBYTE(D_LOGICAL_MAXIMUM), 0xFF, 0x03, /* logical maximum (3ff) */ D_MULTIBYTE(D_LOGICAL_MAXIMUM), 0xFF, 0x03, /* logical maximum (3ff) */
D_USAGE_MINIMUM, 0x00, /* usage minimum (0) */ D_USAGE_MINIMUM, 0x00, /* usage minimum (0) */
D_MULTIBYTE(D_USAGE_MAXIMUM), 0xFF, 0x03, /* usage maximum (3ff) */ D_MULTIBYTE(D_USAGE_MAXIMUM), 0xFF, 0x03, /* usage maximum (3ff) */
D_REPORT_COUNT, 0x04, /* report count (4) */ D_REPORT_COUNT, 0x04, /* report count (4) */
D_REPORT_SIZE, 0x10, /* report size (16) */ D_REPORT_SIZE, 0x10, /* report size (16) */
D_INPUT, 0x00, /* input */ D_INPUT, 0x00, /* input */
D_END_COLLECTION /* end collection */ D_END_COLLECTION /* end collection */
}; };
ConsumerControl_::ConsumerControl_(void) { ConsumerControl_::ConsumerControl_(void) {
static HIDSubDescriptor node(_hidMultiReportDescriptorConsumer, sizeof(_hidMultiReportDescriptorConsumer)); static HIDSubDescriptor node(_hidMultiReportDescriptorConsumer, sizeof(_hidMultiReportDescriptorConsumer));
HID().AppendDescriptor(&node); HID().AppendDescriptor(&node);
} }
void ConsumerControl_::begin(void) { void ConsumerControl_::begin(void) {
// release all buttons // release all buttons
end(); end();
} }
void ConsumerControl_::end(void) { void ConsumerControl_::end(void) {
memset(&_report, 0, sizeof(_report)); memset(&_report, 0, sizeof(_report));
sendReport(); sendReport();
} }
void ConsumerControl_::write(uint16_t m) { void ConsumerControl_::write(uint16_t m) {
press(m); press(m);
release(m); release(m);
} }
void ConsumerControl_::press(uint16_t m) { void ConsumerControl_::press(uint16_t m) {
// search for a free spot // search for a free spot
for (uint8_t i = 0; i < sizeof(HID_ConsumerControlReport_Data_t) / 2; i++) { for (uint8_t i = 0; i < sizeof(HID_ConsumerControlReport_Data_t) / 2; i++) {
if (_report.keys[i] == 0x00) { if (_report.keys[i] == 0x00) {
_report.keys[i] = m; _report.keys[i] = m;
break; break;
}
} }
}
} }
void ConsumerControl_::release(uint16_t m) { void ConsumerControl_::release(uint16_t m) {
// search and release the keypress // search and release the keypress
for (uint8_t i = 0; i < sizeof(HID_ConsumerControlReport_Data_t) / 2; i++) { for (uint8_t i = 0; i < sizeof(HID_ConsumerControlReport_Data_t) / 2; i++) {
if (_report.keys[i] == m) { if (_report.keys[i] == m) {
_report.keys[i] = 0x00; _report.keys[i] = 0x00;
// no break to delete multiple keys // no break to delete multiple keys
}
} }
}
} }
void ConsumerControl_::releaseAll(void) { void ConsumerControl_::releaseAll(void) {
memset(&_report, 0, sizeof(_report)); memset(&_report, 0, sizeof(_report));
} }
void ConsumerControl_::sendReportUnchecked(void) { void ConsumerControl_::sendReportUnchecked(void) {
HID().SendReport(HID_REPORTID_CONSUMERCONTROL, &_report, sizeof(_report)); HID().SendReport(HID_REPORTID_CONSUMERCONTROL, &_report, sizeof(_report));
} }
void ConsumerControl_::sendReport(void) { void ConsumerControl_::sendReport(void) {
// If the last report is different than the current report, then we need to send a report. // If the last report is different than the current report, then we need to send a report.
// We guard sendReport like this so that calling code doesn't end up spamming the host with empty reports // We guard sendReport like this so that calling code doesn't end up spamming the host with empty reports
// if sendReport is called in a tight loop. // if sendReport is called in a tight loop.
// if the previous report is the same, return early without a new report. // if the previous report is the same, return early without a new report.
if (memcmp(&_lastReport, &_report, sizeof(_report)) == 0) if (memcmp(&_lastReport, &_report, sizeof(_report)) == 0)
return; return;
sendReportUnchecked(); sendReportUnchecked();
memcpy(&_lastReport, &_report, sizeof(_report)); memcpy(&_lastReport, &_report, sizeof(_report));
} }
ConsumerControl_ ConsumerControl; ConsumerControl_ ConsumerControl;

@ -31,35 +31,35 @@ THE SOFTWARE.
#include "../HID-Settings.h" #include "../HID-Settings.h"
typedef union { typedef union {
// Every usable Consumer key possible, up to 4 keys presses possible // Every usable Consumer key possible, up to 4 keys presses possible
uint16_t keys[4]; uint16_t keys[4];
struct { struct {
uint16_t key1; uint16_t key1;
uint16_t key2; uint16_t key2;
uint16_t key3; uint16_t key3;
uint16_t key4; uint16_t key4;
}; };
} HID_ConsumerControlReport_Data_t; } HID_ConsumerControlReport_Data_t;
class ConsumerControl_ { class ConsumerControl_ {
public: public:
ConsumerControl_(void); ConsumerControl_(void);
void begin(void); void begin(void);
void end(void); void end(void);
void write(uint16_t m); void write(uint16_t m);
void press(uint16_t m); void press(uint16_t m);
void release(uint16_t m); void release(uint16_t m);
void releaseAll(void); void releaseAll(void);
// Sending is public in the base class for advanced users. // Sending is public in the base class for advanced users.
void sendReport(void); void sendReport(void);
protected: protected:
HID_ConsumerControlReport_Data_t _report; HID_ConsumerControlReport_Data_t _report;
HID_ConsumerControlReport_Data_t _lastReport; HID_ConsumerControlReport_Data_t _lastReport;
private: private:
void sendReportUnchecked(void); void sendReportUnchecked(void);
}; };
extern ConsumerControl_ ConsumerControl; extern ConsumerControl_ ConsumerControl;

@ -27,136 +27,136 @@ THE SOFTWARE.
#include "../DescriptorPrimitives.h" #include "../DescriptorPrimitives.h"
static const uint8_t _hidMultiReportDescriptorGamepad[] PROGMEM = { static const uint8_t _hidMultiReportDescriptorGamepad[] PROGMEM = {
/* Gamepad with 32 buttons and 6 axis*/ /* Gamepad with 32 buttons and 6 axis*/
D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) */ D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) */
D_USAGE, D_USAGE_JOYSTICK, /* USAGE (Joystick) */ D_USAGE, D_USAGE_JOYSTICK, /* USAGE (Joystick) */
D_COLLECTION, D_APPLICATION, /* COLLECTION (Application) */ D_COLLECTION, D_APPLICATION, /* COLLECTION (Application) */
D_REPORT_ID, HID_REPORTID_GAMEPAD, /* REPORT_ID */ D_REPORT_ID, HID_REPORTID_GAMEPAD, /* REPORT_ID */
/* 32 Buttons */ /* 32 Buttons */
D_USAGE_PAGE, D_PAGE_BUTTON, /* USAGE_PAGE (Button) */ D_USAGE_PAGE, D_PAGE_BUTTON, /* USAGE_PAGE (Button) */
D_USAGE_MINIMUM, 0x01, /* USAGE_MINIMUM (Button 1) */ D_USAGE_MINIMUM, 0x01, /* USAGE_MINIMUM (Button 1) */
D_USAGE_MAXIMUM, 0x20, /* USAGE_MAXIMUM (Button 32) */ D_USAGE_MAXIMUM, 0x20, /* USAGE_MAXIMUM (Button 32) */
D_LOGICAL_MINIMUM, 0x00, /* _LOGICAL_MINIMUM (0) */ D_LOGICAL_MINIMUM, 0x00, /* _LOGICAL_MINIMUM (0) */
D_LOGICAL_MAXIMUM, 0x01, /* _LOGICAL_MAXIMUM (1) */ D_LOGICAL_MAXIMUM, 0x01, /* _LOGICAL_MAXIMUM (1) */
D_REPORT_SIZE, 0x01, /* REPORT_SIZE (1) */ D_REPORT_SIZE, 0x01, /* REPORT_SIZE (1) */
D_REPORT_COUNT, 0x20, /* REPORT_COUNT (32) */ D_REPORT_COUNT, 0x20, /* REPORT_COUNT (32) */
D_INPUT, (D_DATA|D_VARIABLE|D_ABSOLUTE), /* INPUT (Data,Var,Abs) */ D_INPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE), /* INPUT (Data,Var,Abs) */
/* 4 16bit Axis */ /* 4 16bit Axis */
D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) */ D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) */
D_COLLECTION, D_PHYSICAL, /* COLLECTION (Physical) */ D_COLLECTION, D_PHYSICAL, /* COLLECTION (Physical) */
D_USAGE, 0x30, /* USAGE (X) */ D_USAGE, 0x30, /* USAGE (X) */
D_USAGE, 0x31, /* USAGE (Y) */ D_USAGE, 0x31, /* USAGE (Y) */
D_USAGE, 0x33, /* USAGE (Rx) */ D_USAGE, 0x33, /* USAGE (Rx) */
D_USAGE, 0x34, /* USAGE (Ry) */ D_USAGE, 0x34, /* USAGE (Ry) */
D_MULTIBYTE(D_LOGICAL_MINIMUM), 0x00, 0x80, /* _LOGICAL_MINIMUM (-32768) */ D_MULTIBYTE(D_LOGICAL_MINIMUM), 0x00, 0x80, /* _LOGICAL_MINIMUM (-32768) */
D_MULTIBYTE(D_LOGICAL_MAXIMUM), 0xFF, 0x7F, /* _LOGICAL_MAXIMUM (32767) */ D_MULTIBYTE(D_LOGICAL_MAXIMUM), 0xFF, 0x7F, /* _LOGICAL_MAXIMUM (32767) */
D_REPORT_SIZE, 0x10, /* REPORT_SIZE (16) */ D_REPORT_SIZE, 0x10, /* REPORT_SIZE (16) */
D_REPORT_COUNT, 0x04, /* REPORT_COUNT (4) */ D_REPORT_COUNT, 0x04, /* REPORT_COUNT (4) */
D_INPUT, (D_DATA|D_VARIABLE|D_ABSOLUTE), /* INPUT (Data,Var,Abs) */ D_INPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE), /* INPUT (Data,Var,Abs) */
/* 2 8bit Axis */ /* 2 8bit Axis */
D_USAGE, 0x32, /* USAGE (Z) */ D_USAGE, 0x32, /* USAGE (Z) */
D_USAGE, 0x35, /* USAGE (Rz) */ D_USAGE, 0x35, /* USAGE (Rz) */
D_LOGICAL_MINIMUM, 0x80, /* _LOGICAL_MINIMUM (-128) */ D_LOGICAL_MINIMUM, 0x80, /* _LOGICAL_MINIMUM (-128) */
D_LOGICAL_MAXIMUM, 0x7F, /* _LOGICAL_MAXIMUM (127) */ D_LOGICAL_MAXIMUM, 0x7F, /* _LOGICAL_MAXIMUM (127) */
D_REPORT_SIZE, 0x08, /* REPORT_SIZE (8) */ D_REPORT_SIZE, 0x08, /* REPORT_SIZE (8) */
D_REPORT_COUNT, 0x02, /* REPORT_COUNT (2) */ D_REPORT_COUNT, 0x02, /* REPORT_COUNT (2) */
D_INPUT, (D_DATA|D_VARIABLE|D_ABSOLUTE), /* INPUT (Data,Var,Abs) */ D_INPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE), /* INPUT (Data,Var,Abs) */
D_END_COLLECTION, /* END_COLLECTION */ D_END_COLLECTION, /* END_COLLECTION */
/* 2 Hat Switches */ /* 2 Hat Switches */
D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) */ D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) */
D_USAGE, 0x39, /* USAGE (Hat switch) */ D_USAGE, 0x39, /* USAGE (Hat switch) */
D_USAGE, 0x39, /* USAGE (Hat switch) */ D_USAGE, 0x39, /* USAGE (Hat switch) */
D_LOGICAL_MINIMUM, 0x01, /* _LOGICAL_MINIMUM (1) */ D_LOGICAL_MINIMUM, 0x01, /* _LOGICAL_MINIMUM (1) */
D_LOGICAL_MAXIMUM, 0x08, /* _LOGICAL_MAXIMUM (8) */ D_LOGICAL_MAXIMUM, 0x08, /* _LOGICAL_MAXIMUM (8) */
D_REPORT_COUNT, 0x02, /* REPORT_COUNT (2) */ D_REPORT_COUNT, 0x02, /* REPORT_COUNT (2) */
D_REPORT_SIZE, 0x04, /* REPORT_SIZE (4) */ D_REPORT_SIZE, 0x04, /* REPORT_SIZE (4) */
D_INPUT, (D_DATA|D_VARIABLE|D_ABSOLUTE), /* INPUT (Data,Var,Abs) */ D_INPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE), /* INPUT (Data,Var,Abs) */
D_END_COLLECTION /* END_COLLECTION */ D_END_COLLECTION /* END_COLLECTION */
}; };
Gamepad_::Gamepad_(void) { Gamepad_::Gamepad_(void) {
static HIDSubDescriptor node(_hidMultiReportDescriptorGamepad, sizeof(_hidMultiReportDescriptorGamepad)); static HIDSubDescriptor node(_hidMultiReportDescriptorGamepad, sizeof(_hidMultiReportDescriptorGamepad));
HID().AppendDescriptor(&node); HID().AppendDescriptor(&node);
} }
void Gamepad_::begin(void) { void Gamepad_::begin(void) {
// release all buttons // release all buttons
end(); end();
} }
void Gamepad_::end(void) { void Gamepad_::end(void) {
memset(&_report, 0x00, sizeof(_report)); memset(&_report, 0x00, sizeof(_report));
sendReport(&_report, sizeof(_report)); sendReport(&_report, sizeof(_report));
} }
void Gamepad_::write(void) { void Gamepad_::write(void) {
sendReport(&_report, sizeof(_report)); sendReport(&_report, sizeof(_report));
} }
void Gamepad_::press(uint8_t b) { void Gamepad_::press(uint8_t b) {
_report.buttons |= (uint32_t)1 << (b - 1); _report.buttons |= (uint32_t)1 << (b - 1);
} }
void Gamepad_::release(uint8_t b) { void Gamepad_::release(uint8_t b) {
_report.buttons &= ~((uint32_t)1 << (b - 1)); _report.buttons &= ~((uint32_t)1 << (b - 1));
} }
void Gamepad_::releaseAll(void) { void Gamepad_::releaseAll(void) {
memset(&_report, 0x00, sizeof(_report)); memset(&_report, 0x00, sizeof(_report));
} }
void Gamepad_::buttons(uint32_t b) { void Gamepad_::buttons(uint32_t b) {
_report.buttons = b; _report.buttons = b;
} }
void Gamepad_::xAxis(int16_t a) { void Gamepad_::xAxis(int16_t a) {
_report.xAxis = a; _report.xAxis = a;
} }
void Gamepad_::yAxis(int16_t a) { void Gamepad_::yAxis(int16_t a) {
_report.yAxis = a; _report.yAxis = a;
} }
void Gamepad_::zAxis(int8_t a) { void Gamepad_::zAxis(int8_t a) {
_report.zAxis = a; _report.zAxis = a;
} }
void Gamepad_::rxAxis(int16_t a) { void Gamepad_::rxAxis(int16_t a) {
_report.rxAxis = a; _report.rxAxis = a;
} }
void Gamepad_::ryAxis(int16_t a) { void Gamepad_::ryAxis(int16_t a) {
_report.ryAxis = a; _report.ryAxis = a;
} }
void Gamepad_::rzAxis(int8_t a) { void Gamepad_::rzAxis(int8_t a) {
_report.rzAxis = a; _report.rzAxis = a;
} }
void Gamepad_::dPad1(int8_t d) { void Gamepad_::dPad1(int8_t d) {
_report.dPad1 = d; _report.dPad1 = d;
} }
void Gamepad_::dPad2(int8_t d) { void Gamepad_::dPad2(int8_t d) {
_report.dPad2 = d; _report.dPad2 = d;
} }
void Gamepad_::sendReport(void* data, int length) { void Gamepad_::sendReport(void* data, int length) {
HID().SendReport(HID_REPORTID_GAMEPAD, data, length); HID().SendReport(HID_REPORTID_GAMEPAD, data, length);
} }
Gamepad_ Gamepad; Gamepad_ Gamepad;

@ -43,83 +43,83 @@ THE SOFTWARE.
typedef union { typedef union {
// 32 Buttons, 6 Axis, 2 D-Pads // 32 Buttons, 6 Axis, 2 D-Pads
uint32_t buttons; uint32_t buttons;
struct { struct {
uint8_t button1 : 1; uint8_t button1 : 1;
uint8_t button2 : 1; uint8_t button2 : 1;
uint8_t button3 : 1; uint8_t button3 : 1;
uint8_t button4 : 1; uint8_t button4 : 1;
uint8_t button5 : 1; uint8_t button5 : 1;
uint8_t button6 : 1; uint8_t button6 : 1;
uint8_t button7 : 1; uint8_t button7 : 1;
uint8_t button8 : 1; uint8_t button8 : 1;
uint8_t button9 : 1; uint8_t button9 : 1;
uint8_t button10 : 1; uint8_t button10 : 1;
uint8_t button11 : 1; uint8_t button11 : 1;
uint8_t button12 : 1; uint8_t button12 : 1;
uint8_t button13 : 1; uint8_t button13 : 1;
uint8_t button14 : 1; uint8_t button14 : 1;
uint8_t button15 : 1; uint8_t button15 : 1;
uint8_t button16 : 1; uint8_t button16 : 1;
uint8_t button17 : 1; uint8_t button17 : 1;
uint8_t button18 : 1; uint8_t button18 : 1;
uint8_t button19 : 1; uint8_t button19 : 1;
uint8_t button20 : 1; uint8_t button20 : 1;
uint8_t button21 : 1; uint8_t button21 : 1;
uint8_t button22 : 1; uint8_t button22 : 1;
uint8_t button23 : 1; uint8_t button23 : 1;
uint8_t button24 : 1; uint8_t button24 : 1;
uint8_t button25 : 1; uint8_t button25 : 1;
uint8_t button26 : 1; uint8_t button26 : 1;
uint8_t button27 : 1; uint8_t button27 : 1;
uint8_t button28 : 1; uint8_t button28 : 1;
uint8_t button29 : 1; uint8_t button29 : 1;
uint8_t button30 : 1; uint8_t button30 : 1;
uint8_t button31 : 1; uint8_t button31 : 1;
uint8_t button32 : 1; uint8_t button32 : 1;
int16_t xAxis; int16_t xAxis;
int16_t yAxis; int16_t yAxis;
int16_t rxAxis; int16_t rxAxis;
int16_t ryAxis; int16_t ryAxis;
int8_t zAxis; int8_t zAxis;
int8_t rzAxis; int8_t rzAxis;
uint8_t dPad1 : 4; uint8_t dPad1 : 4;
uint8_t dPad2 : 4; uint8_t dPad2 : 4;
}; };
} HID_GamepadReport_Data_t; } HID_GamepadReport_Data_t;
class Gamepad_ { class Gamepad_ {
public: public:
Gamepad_(void); Gamepad_(void);
void begin(void); void begin(void);
void end(void); void end(void);
void write(void); void write(void);
void press(uint8_t b); void press(uint8_t b);
void release(uint8_t b); void release(uint8_t b);
void releaseAll(void); void releaseAll(void);
void buttons(uint32_t b); void buttons(uint32_t b);
void xAxis(int16_t a); void xAxis(int16_t a);
void yAxis(int16_t a); void yAxis(int16_t a);
void zAxis(int8_t a); void zAxis(int8_t a);
void rxAxis(int16_t a); void rxAxis(int16_t a);
void ryAxis(int16_t a); void ryAxis(int16_t a);
void rzAxis(int8_t a); void rzAxis(int8_t a);
void dPad1(int8_t d); void dPad1(int8_t d);
void dPad2(int8_t d); void dPad2(int8_t d);
void sendReport(void* data, int length); void sendReport(void* data, int length);
protected: protected:
HID_GamepadReport_Data_t _report; HID_GamepadReport_Data_t _report;
}; };
extern Gamepad_ Gamepad; extern Gamepad_ Gamepad;

@ -27,161 +27,161 @@ THE SOFTWARE.
#include "../DescriptorPrimitives.h" #include "../DescriptorPrimitives.h"
static const uint8_t _hidMultiReportDescriptorKeyboard[] PROGMEM = { static const uint8_t _hidMultiReportDescriptorKeyboard[] PROGMEM = {
// NKRO Keyboard // NKRO Keyboard
D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP,
D_USAGE, D_USAGE_KEYBOARD, D_USAGE, D_USAGE_KEYBOARD,
D_COLLECTION, D_APPLICATION, D_COLLECTION, D_APPLICATION,
D_REPORT_ID, HID_REPORTID_NKRO_KEYBOARD, D_REPORT_ID, HID_REPORTID_NKRO_KEYBOARD,
D_USAGE_PAGE, D_PAGE_KEYBOARD, D_USAGE_PAGE, D_PAGE_KEYBOARD,
/* Key modifier byte */ /* Key modifier byte */
D_USAGE_MINIMUM, HID_KEYBOARD_FIRST_MODIFIER, D_USAGE_MINIMUM, HID_KEYBOARD_FIRST_MODIFIER,
D_USAGE_MAXIMUM, HID_KEYBOARD_LAST_MODIFIER, D_USAGE_MAXIMUM, HID_KEYBOARD_LAST_MODIFIER,
D_LOGICAL_MINIMUM, 0x00, D_LOGICAL_MINIMUM, 0x00,
D_LOGICAL_MAXIMUM, 0x01, D_LOGICAL_MAXIMUM, 0x01,
D_REPORT_SIZE, 0x01, D_REPORT_SIZE, 0x01,
D_REPORT_COUNT, 0x08, D_REPORT_COUNT, 0x08,
D_INPUT, (D_DATA|D_VARIABLE|D_ABSOLUTE), D_INPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE),
/* 5 LEDs for num lock etc, 3 left for advanced, custom usage */ /* 5 LEDs for num lock etc, 3 left for advanced, custom usage */
D_USAGE_PAGE, D_PAGE_LEDS, D_USAGE_PAGE, D_PAGE_LEDS,
D_USAGE_MINIMUM, 0x01, D_USAGE_MINIMUM, 0x01,
D_USAGE_MAXIMUM, 0x08, D_USAGE_MAXIMUM, 0x08,
D_REPORT_COUNT, 0x08, D_REPORT_COUNT, 0x08,
D_REPORT_SIZE, 0x01, D_REPORT_SIZE, 0x01,
D_OUTPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE), D_OUTPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE),
/* NKRO Keyboard */ /* NKRO Keyboard */
D_USAGE_PAGE, D_PAGE_KEYBOARD, D_USAGE_PAGE, D_PAGE_KEYBOARD,
// Padding 4 bits, to skip NO_EVENT & 3 error states. // Padding 4 bits, to skip NO_EVENT & 3 error states.
D_REPORT_SIZE, 0x04, D_REPORT_SIZE, 0x04,
D_REPORT_COUNT, 0x01, D_REPORT_COUNT, 0x01,
D_INPUT, (D_CONSTANT), D_INPUT, (D_CONSTANT),
D_USAGE_MINIMUM, HID_KEYBOARD_A_AND_A, D_USAGE_MINIMUM, HID_KEYBOARD_A_AND_A,
D_USAGE_MAXIMUM, HID_LAST_KEY, D_USAGE_MAXIMUM, HID_LAST_KEY,
D_LOGICAL_MINIMUM, 0x00, D_LOGICAL_MINIMUM, 0x00,
D_LOGICAL_MAXIMUM, 0x01, D_LOGICAL_MAXIMUM, 0x01,
D_REPORT_SIZE, 0x01, D_REPORT_SIZE, 0x01,
D_REPORT_COUNT, (HID_LAST_KEY - HID_KEYBOARD_A_AND_A), D_REPORT_COUNT, (HID_LAST_KEY - HID_KEYBOARD_A_AND_A),
D_INPUT, (D_DATA|D_VARIABLE|D_ABSOLUTE), D_INPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE),
// Padding (3 bits) to round up the report to byte boundary. // Padding (3 bits) to round up the report to byte boundary.
D_REPORT_SIZE, 0x03, D_REPORT_SIZE, 0x03,
D_REPORT_COUNT, 0x01, D_REPORT_COUNT, 0x01,
D_INPUT, (D_CONSTANT), D_INPUT, (D_CONSTANT),
D_END_COLLECTION, D_END_COLLECTION,
}; };
Keyboard_::Keyboard_(void) { Keyboard_::Keyboard_(void) {
static HIDSubDescriptor node(_hidMultiReportDescriptorKeyboard, sizeof(_hidMultiReportDescriptorKeyboard)); static HIDSubDescriptor node(_hidMultiReportDescriptorKeyboard, sizeof(_hidMultiReportDescriptorKeyboard));
HID().AppendDescriptor(&node); HID().AppendDescriptor(&node);
} }
void Keyboard_::begin(void) { void Keyboard_::begin(void) {
// Force API to send a clean report. // Force API to send a clean report.
// This is important for and HID bridge where the receiver stays on, // This is important for and HID bridge where the receiver stays on,
// while the sender is resetted. // while the sender is resetted.
releaseAll(); releaseAll();
sendReportUnchecked(); sendReportUnchecked();
} }
void Keyboard_::end(void) { void Keyboard_::end(void) {
releaseAll(); releaseAll();
sendReportUnchecked(); sendReportUnchecked();
} }
int Keyboard_::sendReportUnchecked(void) { int Keyboard_::sendReportUnchecked(void) {
return HID().SendReport(HID_REPORTID_NKRO_KEYBOARD, &keyReport, sizeof(keyReport)); return HID().SendReport(HID_REPORTID_NKRO_KEYBOARD, &keyReport, sizeof(keyReport));
} }
int Keyboard_::sendReport(void) { int Keyboard_::sendReport(void) {
// If the last report is different than the current report, then we need to send a report. // If the last report is different than the current report, then we need to send a report.
// We guard sendReport like this so that calling code doesn't end up spamming the host with empty reports // We guard sendReport like this so that calling code doesn't end up spamming the host with empty reports
// if sendReport is called in a tight loop. // if sendReport is called in a tight loop.
if (memcmp(lastKeyReport.allkeys, keyReport.allkeys, sizeof(keyReport))) { if (memcmp(lastKeyReport.allkeys, keyReport.allkeys, sizeof(keyReport))) {
// if the two reports are different, send a report // if the two reports are different, send a report
// ChromeOS 51-60 (at least) bug: if a modifier and a normal keycode are added in the // ChromeOS 51-60 (at least) bug: if a modifier and a normal keycode are added in the
// same report, in some cases the shift is not applied (e.g. `shift`+`[` doesn't yield // same report, in some cases the shift is not applied (e.g. `shift`+`[` doesn't yield
// `{`). To compensate for this, check to see if the modifier byte has changed. // `{`). To compensate for this, check to see if the modifier byte has changed.
// If modifiers are being turned on at the same time as any change // If modifiers are being turned on at the same time as any change
// to the non-modifier keys in the report, then we send the previous // to the non-modifier keys in the report, then we send the previous
// report with the new modifiers // report with the new modifiers
if ( ( (lastKeyReport.modifiers ^ keyReport.modifiers) & keyReport.modifiers) if (((lastKeyReport.modifiers ^ keyReport.modifiers) & keyReport.modifiers)
&& (memcmp(lastKeyReport.keys,keyReport.keys, sizeof(keyReport.keys)))) { && (memcmp(lastKeyReport.keys, keyReport.keys, sizeof(keyReport.keys)))) {
uint8_t last_mods = lastKeyReport.modifiers; uint8_t last_mods = lastKeyReport.modifiers;
lastKeyReport.modifiers = keyReport.modifiers; lastKeyReport.modifiers = keyReport.modifiers;
int returnCode = HID().SendReport(HID_REPORTID_NKRO_KEYBOARD, &lastKeyReport, sizeof(lastKeyReport)); int returnCode = HID().SendReport(HID_REPORTID_NKRO_KEYBOARD, &lastKeyReport, sizeof(lastKeyReport));
lastKeyReport.modifiers = last_mods; lastKeyReport.modifiers = last_mods;
} else { } else {
// If modifiers are being turned off, then we send the new report with the previous modifiers. // If modifiers are being turned off, then we send the new report with the previous modifiers.
// We need to do this, at least on Linux 4.17 + Wayland. // We need to do this, at least on Linux 4.17 + Wayland.
// Jesse has observed that sending Shift + 3 key up events in the same report // Jesse has observed that sending Shift + 3 key up events in the same report
// will sometimes result in a spurious '3' rather than '#', especially when the keys // will sometimes result in a spurious '3' rather than '#', especially when the keys
// had been held for a while // had been held for a while
if (( (lastKeyReport.modifiers ^ keyReport.modifiers) & lastKeyReport.modifiers) if (((lastKeyReport.modifiers ^ keyReport.modifiers) & lastKeyReport.modifiers)
&& (memcmp(lastKeyReport.keys,keyReport.keys, sizeof(keyReport.keys)))) { && (memcmp(lastKeyReport.keys, keyReport.keys, sizeof(keyReport.keys)))) {
uint8_t mods = keyReport.modifiers; uint8_t mods = keyReport.modifiers;
keyReport.modifiers = lastKeyReport.modifiers; keyReport.modifiers = lastKeyReport.modifiers;
int returnCode = HID().SendReport(HID_REPORTID_NKRO_KEYBOARD, &keyReport, sizeof(lastKeyReport)); int returnCode = HID().SendReport(HID_REPORTID_NKRO_KEYBOARD, &keyReport, sizeof(lastKeyReport));
keyReport.modifiers = mods; keyReport.modifiers = mods;
} }
}
int returnCode = sendReportUnchecked();
if (returnCode > 0)
memcpy(lastKeyReport.allkeys, keyReport.allkeys, sizeof(keyReport));
return returnCode;
} }
return -1;
int returnCode = sendReportUnchecked();
if (returnCode > 0)
memcpy(lastKeyReport.allkeys, keyReport.allkeys, sizeof(keyReport));
return returnCode;
}
return -1;
} }
/* Returns true if the modifer key passed in will be sent during this key report /* Returns true if the modifer key passed in will be sent during this key report
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean Keyboard_::isModifierActive(uint8_t k) { boolean Keyboard_::isModifierActive(uint8_t k) {
if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) {
k = k - HID_KEYBOARD_FIRST_MODIFIER; k = k - HID_KEYBOARD_FIRST_MODIFIER;
return !!(keyReport.modifiers & (1 << k)); return !!(keyReport.modifiers & (1 << k));
} }
return false; return false;
} }
/* Returns true if the modifer key passed in was being sent during the previous key report /* Returns true if the modifer key passed in was being sent during the previous key report
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean Keyboard_::wasModifierActive(uint8_t k) { boolean Keyboard_::wasModifierActive(uint8_t k) {
if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) {
k = k - HID_KEYBOARD_FIRST_MODIFIER; k = k - HID_KEYBOARD_FIRST_MODIFIER;
return !!(lastKeyReport.modifiers & (1 << k)); return !!(lastKeyReport.modifiers & (1 << k));
} }
return false; return false;
} }
/* Returns true if *any* modifier will be sent during this key report /* Returns true if *any* modifier will be sent during this key report
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean Keyboard_::isAnyModifierActive() { boolean Keyboard_::isAnyModifierActive() {
return keyReport.modifiers > 0; return keyReport.modifiers > 0;
} }
/* Returns true if *any* modifier was being sent during the previous key report /* Returns true if *any* modifier was being sent during the previous key report
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean Keyboard_::wasAnyModifierActive() { boolean Keyboard_::wasAnyModifierActive() {
return lastKeyReport.modifiers > 0; return lastKeyReport.modifiers > 0;
} }
@ -189,11 +189,11 @@ boolean Keyboard_::wasAnyModifierActive() {
* Returns false in all other cases * Returns false in all other cases
* */ * */
boolean Keyboard_::isKeyPressed(uint8_t k) { boolean Keyboard_::isKeyPressed(uint8_t k) {
if (k <= HID_LAST_KEY) { if (k <= HID_LAST_KEY) {
uint8_t bit = 1 << (uint8_t(k) % 8); uint8_t bit = 1 << (uint8_t(k) % 8);
return !! (keyReport.keys[k / 8] & bit); return !!(keyReport.keys[k / 8] & bit);
} }
return false; return false;
} }
/* Returns true if the non-modifer key passed in was sent during the previous key report /* Returns true if the non-modifer key passed in was sent during the previous key report
@ -201,55 +201,55 @@ boolean Keyboard_::isKeyPressed(uint8_t k) {
* */ * */
boolean Keyboard_::wasKeyPressed(uint8_t k) { boolean Keyboard_::wasKeyPressed(uint8_t k) {
if (k <= HID_LAST_KEY) { if (k <= HID_LAST_KEY) {
uint8_t bit = 1 << (uint8_t(k) % 8); uint8_t bit = 1 << (uint8_t(k) % 8);
return !! (lastKeyReport.keys[k / 8] & bit); return !!(lastKeyReport.keys[k / 8] & bit);
} }
return false; return false;
} }
size_t Keyboard_::press(uint8_t k) { size_t Keyboard_::press(uint8_t k) {
// If the key is in the range of 'printable' keys // If the key is in the range of 'printable' keys
if (k <= HID_LAST_KEY) { if (k <= HID_LAST_KEY) {
uint8_t bit = 1 << (uint8_t(k) % 8); uint8_t bit = 1 << (uint8_t(k) % 8);
keyReport.keys[k / 8] |= bit; keyReport.keys[k / 8] |= bit;
return 1; return 1;
} else { // It's a modifier key } else { // It's a modifier key
if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) {
// Convert key into bitfield (0 - 7) // Convert key into bitfield (0 - 7)
k = k - HID_KEYBOARD_FIRST_MODIFIER; k = k - HID_KEYBOARD_FIRST_MODIFIER;
keyReport.modifiers |= (1 << k); keyReport.modifiers |= (1 << k);
return 1; return 1;
}
} }
}
// No empty/pressed key was found // No empty/pressed key was found
return 0; return 0;
} }
size_t Keyboard_::release(uint8_t k) { size_t Keyboard_::release(uint8_t k) {
// If we're releasing a printable key // If we're releasing a printable key
if (k <= HID_LAST_KEY) { if (k <= HID_LAST_KEY) {
uint8_t bit = 1 << (k % 8); uint8_t bit = 1 << (k % 8);
keyReport.keys[k / 8] &= ~bit; keyReport.keys[k / 8] &= ~bit;
return 1; return 1;
} else { // It's a modifier key } else { // It's a modifier key
if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) { if (k >= HID_KEYBOARD_FIRST_MODIFIER && k <= HID_KEYBOARD_LAST_MODIFIER) {
// Convert key into bitfield (0 - 7) // Convert key into bitfield (0 - 7)
k = k - HID_KEYBOARD_FIRST_MODIFIER; k = k - HID_KEYBOARD_FIRST_MODIFIER;
keyReport.modifiers &= ~(1 << k); keyReport.modifiers &= ~(1 << k);
return 1; return 1;
}
} }
}
// No empty/pressed key was found // No empty/pressed key was found
return 0; return 0;
} }
void Keyboard_::releaseAll(void) { void Keyboard_::releaseAll(void) {
// Release all keys // Release all keys
memset(&keyReport.allkeys, 0x00, sizeof(keyReport.allkeys)); memset(&keyReport.allkeys, 0x00, sizeof(keyReport.allkeys));
} }
Keyboard_ Keyboard; Keyboard_ Keyboard;

@ -36,39 +36,39 @@ THE SOFTWARE.
#define KEY_BYTES 28 #define KEY_BYTES 28
typedef union { typedef union {
// Modifiers + keymap // Modifiers + keymap
struct { struct {
uint8_t modifiers; uint8_t modifiers;
uint8_t keys[KEY_BYTES ]; uint8_t keys[KEY_BYTES ];
}; };
uint8_t allkeys[1 + KEY_BYTES]; uint8_t allkeys[1 + KEY_BYTES];
} HID_KeyboardReport_Data_t; } HID_KeyboardReport_Data_t;
class Keyboard_ { class Keyboard_ {
public: public:
Keyboard_(void); Keyboard_(void);
void begin(void); void begin(void);
void end(void); void end(void);
size_t press(uint8_t k); size_t press(uint8_t k);
size_t release(uint8_t k); size_t release(uint8_t k);
void releaseAll(void); void releaseAll(void);
int sendReport(void); int sendReport(void);
boolean isKeyPressed(uint8_t k); boolean isKeyPressed(uint8_t k);
boolean wasKeyPressed(uint8_t k); boolean wasKeyPressed(uint8_t k);
boolean isModifierActive(uint8_t k); boolean isModifierActive(uint8_t k);
boolean wasModifierActive(uint8_t k); boolean wasModifierActive(uint8_t k);
boolean isAnyModifierActive(); boolean isAnyModifierActive();
boolean wasAnyModifierActive(); boolean wasAnyModifierActive();
uint8_t getLEDs() { uint8_t getLEDs() {
return HID().getLEDs(); return HID().getLEDs();
} }
HID_KeyboardReport_Data_t keyReport; HID_KeyboardReport_Data_t keyReport;
HID_KeyboardReport_Data_t lastKeyReport; HID_KeyboardReport_Data_t lastKeyReport;
private: private:
int sendReportUnchecked(void); int sendReportUnchecked(void);
}; };
extern Keyboard_ Keyboard; extern Keyboard_ Keyboard;

@ -27,110 +27,110 @@ THE SOFTWARE.
#include "../DescriptorPrimitives.h" #include "../DescriptorPrimitives.h"
static const uint8_t _hidMultiReportDescriptorMouse[] PROGMEM = { static const uint8_t _hidMultiReportDescriptorMouse[] PROGMEM = {
/* Mouse relative */ /* Mouse relative */
D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, // USAGE_PAGE (Generic Desktop) D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, // USAGE_PAGE (Generic Desktop)
D_USAGE, D_USAGE_MOUSE, // USAGE (Mouse) D_USAGE, D_USAGE_MOUSE, // USAGE (Mouse)
D_COLLECTION, D_APPLICATION, // COLLECTION (Application) D_COLLECTION, D_APPLICATION, // COLLECTION (Application)
D_REPORT_ID, HID_REPORTID_MOUSE, // REPORT_ID (Mouse) D_REPORT_ID, HID_REPORTID_MOUSE, // REPORT_ID (Mouse)
/* 8 Buttons */ /* 8 Buttons */
D_USAGE_PAGE, D_PAGE_BUTTON, // USAGE_PAGE (Button) D_USAGE_PAGE, D_PAGE_BUTTON, // USAGE_PAGE (Button)
D_USAGE_MINIMUM, 0x01, // USAGE_MINIMUM (Button 1) D_USAGE_MINIMUM, 0x01, // USAGE_MINIMUM (Button 1)
D_USAGE_MAXIMUM, 0x08, // USAGE_MAXIMUM (Button 8) D_USAGE_MAXIMUM, 0x08, // USAGE_MAXIMUM (Button 8)
D_LOGICAL_MINIMUM, 0x00, // LOGICAL_MINIMUM (0) D_LOGICAL_MINIMUM, 0x00, // LOGICAL_MINIMUM (0)
D_LOGICAL_MAXIMUM, 0x01, // LOGICAL_MAXIMUM (1) D_LOGICAL_MAXIMUM, 0x01, // LOGICAL_MAXIMUM (1)
D_REPORT_COUNT, 0x08, // REPORT_COUNT (8) D_REPORT_COUNT, 0x08, // REPORT_COUNT (8)
D_REPORT_SIZE, 0x01, // REPORT_SIZE (1) D_REPORT_SIZE, 0x01, // REPORT_SIZE (1)
D_INPUT, (D_DATA|D_VARIABLE|D_ABSOLUTE), // INPUT (Data,Var,Abs) D_INPUT, (D_DATA | D_VARIABLE | D_ABSOLUTE), // INPUT (Data,Var,Abs)
/* X, Y, Wheel */ /* X, Y, Wheel */
D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, // USAGE_PAGE (Generic Desktop) D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, // USAGE_PAGE (Generic Desktop)
D_USAGE, 0x30, // USAGE (X) D_USAGE, 0x30, // USAGE (X)
D_USAGE, 0x31, // USAGE (Y) D_USAGE, 0x31, // USAGE (Y)
D_USAGE, 0x38, // USAGE (Wheel) D_USAGE, 0x38, // USAGE (Wheel)
D_LOGICAL_MINIMUM, 0x81, // LOGICAL_MINIMUM (-127) D_LOGICAL_MINIMUM, 0x81, // LOGICAL_MINIMUM (-127)
D_LOGICAL_MAXIMUM, 0x7f, // LOGICAL_MAXIMUM (127) D_LOGICAL_MAXIMUM, 0x7f, // LOGICAL_MAXIMUM (127)
D_REPORT_SIZE, 0x08, // REPORT_SIZE (8) D_REPORT_SIZE, 0x08, // REPORT_SIZE (8)
D_REPORT_COUNT, 0x03, // REPORT_COUNT (3) D_REPORT_COUNT, 0x03, // REPORT_COUNT (3)
D_INPUT, (D_DATA|D_VARIABLE|D_RELATIVE), // INPUT (Data,Var,Rel) D_INPUT, (D_DATA | D_VARIABLE | D_RELATIVE), // INPUT (Data,Var,Rel)
/* Horizontal wheel */ /* Horizontal wheel */
D_USAGE_PAGE, D_PAGE_CONSUMER, // USAGE_PAGE (Consumer) D_USAGE_PAGE, D_PAGE_CONSUMER, // USAGE_PAGE (Consumer)
D_PAGE_ORDINAL, 0x38, 0x02, // PAGE (AC Pan) D_PAGE_ORDINAL, 0x38, 0x02, // PAGE (AC Pan)
D_LOGICAL_MINIMUM, 0x81, // LOGICAL_MINIMUM (-127) D_LOGICAL_MINIMUM, 0x81, // LOGICAL_MINIMUM (-127)
D_LOGICAL_MAXIMUM, 0x7f, // LOGICAL_MAXIMUM (127) D_LOGICAL_MAXIMUM, 0x7f, // LOGICAL_MAXIMUM (127)
D_REPORT_SIZE, 0x08, // REPORT_SIZE (8) D_REPORT_SIZE, 0x08, // REPORT_SIZE (8)
D_REPORT_COUNT, 0x01, // REPORT_COUNT (1) D_REPORT_COUNT, 0x01, // REPORT_COUNT (1)
D_INPUT, (D_DATA|D_VARIABLE|D_RELATIVE), // INPUT (Data,Var,Rel) D_INPUT, (D_DATA | D_VARIABLE | D_RELATIVE), // INPUT (Data,Var,Rel)
/* End */ /* End */
D_END_COLLECTION // END_COLLECTION D_END_COLLECTION // END_COLLECTION
}; };
Mouse_::Mouse_(void) { Mouse_::Mouse_(void) {
} }
void Mouse_::begin(void) { void Mouse_::begin(void) {
static HIDSubDescriptor node(_hidMultiReportDescriptorMouse, sizeof(_hidMultiReportDescriptorMouse)); static HIDSubDescriptor node(_hidMultiReportDescriptorMouse, sizeof(_hidMultiReportDescriptorMouse));
HID().AppendDescriptor(&node); HID().AppendDescriptor(&node);
end(); end();
} }
void Mouse_::end(void) { void Mouse_::end(void) {
releaseAll(); releaseAll();
sendReport(); sendReport();
} }
void Mouse_::click(uint8_t b) { void Mouse_::click(uint8_t b) {
press(b); press(b);
sendReport(); sendReport();
release(b); release(b);
} }
void Mouse_::move(signed char x, signed char y, signed char vWheel, signed char hWheel) { void Mouse_::move(signed char x, signed char y, signed char vWheel, signed char hWheel) {
report.xAxis = x; report.xAxis = x;
report.yAxis = y; report.yAxis = y;
report.vWheel = vWheel; report.vWheel = vWheel;
report.hWheel = hWheel; report.hWheel = hWheel;
} }
void Mouse_::releaseAll(void) { void Mouse_::releaseAll(void) {
memset(&report, 0, sizeof(report)); memset(&report, 0, sizeof(report));
} }
void Mouse_::press(uint8_t b) { void Mouse_::press(uint8_t b) {
report.buttons |= b; report.buttons |= b;
} }
void Mouse_::release(uint8_t b) { void Mouse_::release(uint8_t b) {
report.buttons &= ~b; report.buttons &= ~b;
} }
bool Mouse_::isPressed(uint8_t b) { bool Mouse_::isPressed(uint8_t b) {
if ((b & report.buttons) > 0) if ((b & report.buttons) > 0)
return true; return true;
return false; return false;
} }
void Mouse_::sendReportUnchecked(void) { void Mouse_::sendReportUnchecked(void) {
HID().SendReport(HID_REPORTID_MOUSE, &report, sizeof(report)); HID().SendReport(HID_REPORTID_MOUSE, &report, sizeof(report));
} }
void Mouse_::sendReport(void) { void Mouse_::sendReport(void) {
// If the last report is different than the current report, then we need to send a report. // If the last report is different than the current report, then we need to send a report.
// We guard sendReport like this so that calling code doesn't end up spamming the host with empty reports // We guard sendReport like this so that calling code doesn't end up spamming the host with empty reports
// if sendReport is called in a tight loop. // if sendReport is called in a tight loop.
// if the two reports are the same, check if they're empty, and return early // if the two reports are the same, check if they're empty, and return early
// without a report if they are. // without a report if they are.
static HID_MouseReport_Data_t emptyReport; static HID_MouseReport_Data_t emptyReport;
if (memcmp(&lastReport, &report, sizeof(report)) == 0 && if (memcmp(&lastReport, &report, sizeof(report)) == 0 &&
memcmp(&report, &emptyReport, sizeof(report)) == 0) memcmp(&report, &emptyReport, sizeof(report)) == 0)
return; return;
sendReportUnchecked(); sendReportUnchecked();
memcpy(&lastReport, &report, sizeof(report)); memcpy(&lastReport, &report, sizeof(report));
} }
Mouse_ Mouse; Mouse_ Mouse;

@ -32,46 +32,46 @@ THE SOFTWARE.
#include "../MouseButtons.h" #include "../MouseButtons.h"
typedef union { typedef union {
// Mouse report: 8 buttons, position, wheel // Mouse report: 8 buttons, position, wheel
struct { struct {
uint8_t buttons; uint8_t buttons;
int8_t xAxis; int8_t xAxis;
int8_t yAxis; int8_t yAxis;
int8_t vWheel; int8_t vWheel;
int8_t hWheel; int8_t hWheel;
}; };
} HID_MouseReport_Data_t; } HID_MouseReport_Data_t;
class Mouse_ { class Mouse_ {
public: public:
Mouse_(void); Mouse_(void);
void begin(void); void begin(void);
void end(void); void end(void);
void click(uint8_t b = MOUSE_LEFT); void click(uint8_t b = MOUSE_LEFT);
void move(signed char x, signed char y, signed char vWheel = 0, signed char hWheel = 0); void move(signed char x, signed char y, signed char vWheel = 0, signed char hWheel = 0);
void press(uint8_t b = MOUSE_LEFT); // press LEFT by default void press(uint8_t b = MOUSE_LEFT); // press LEFT by default
void release(uint8_t b = MOUSE_LEFT); // release LEFT by default void release(uint8_t b = MOUSE_LEFT); // release LEFT by default
bool isPressed(uint8_t b = MOUSE_LEFT); // check LEFT by default bool isPressed(uint8_t b = MOUSE_LEFT); // check LEFT by default
/** getReport returns the current report. /** getReport returns the current report.
* *
* The current report is the one to be send next time sendReport() is called. * The current report is the one to be send next time sendReport() is called.
* *
* @returns A copy of the report. * @returns A copy of the report.
*/ */
const HID_MouseReport_Data_t getReport() { const HID_MouseReport_Data_t getReport() {
return report; return report;
} }
void sendReport(void); void sendReport(void);
void releaseAll(void); void releaseAll(void);
protected: protected:
HID_MouseReport_Data_t report; HID_MouseReport_Data_t report;
HID_MouseReport_Data_t lastReport; HID_MouseReport_Data_t lastReport;
private: private:
void sendReportUnchecked(void); void sendReportUnchecked(void);
}; };
extern Mouse_ Mouse; extern Mouse_ Mouse;

@ -27,70 +27,70 @@ THE SOFTWARE.
#include "../DescriptorPrimitives.h" #include "../DescriptorPrimitives.h"
static const uint8_t _hidMultiReportDescriptorSystem[] PROGMEM = { static const uint8_t _hidMultiReportDescriptorSystem[] PROGMEM = {
//TODO(anyone): limit to system keys only? //TODO(anyone): limit to system keys only?
/* System Control (Power Down, Sleep, Wakeup, ...) */ /* System Control (Power Down, Sleep, Wakeup, ...) */
D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) */ D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) */
D_USAGE, 0x80, /* USAGE (System Control) */ D_USAGE, 0x80, /* USAGE (System Control) */
D_COLLECTION, D_APPLICATION, /* COLLECTION (Application) */ D_COLLECTION, D_APPLICATION, /* COLLECTION (Application) */
D_REPORT_ID, HID_REPORTID_SYSTEMCONTROL, /* REPORT_ID */ D_REPORT_ID, HID_REPORTID_SYSTEMCONTROL, /* REPORT_ID */
/* 1 system key */ /* 1 system key */
D_LOGICAL_MINIMUM, 0x00, /* LOGICAL_MINIMUM (0) */ D_LOGICAL_MINIMUM, 0x00, /* LOGICAL_MINIMUM (0) */
D_MULTIBYTE(D_LOGICAL_MAXIMUM), 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */ D_MULTIBYTE(D_LOGICAL_MAXIMUM), 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */
D_USAGE_MINIMUM, 0x00, /* USAGE_MINIMUM (Undefined) */ D_USAGE_MINIMUM, 0x00, /* USAGE_MINIMUM (Undefined) */
D_USAGE_MAXIMUM, 0xff, /* USAGE_MAXIMUM (System Menu Down) */ D_USAGE_MAXIMUM, 0xff, /* USAGE_MAXIMUM (System Menu Down) */
D_REPORT_COUNT, 0x01, /* REPORT_COUNT (1) */ D_REPORT_COUNT, 0x01, /* REPORT_COUNT (1) */
D_REPORT_SIZE, 0x08, /* REPORT_SIZE (8) */ D_REPORT_SIZE, 0x08, /* REPORT_SIZE (8) */
D_INPUT, (D_DATA|D_ARRAY|D_ABSOLUTE), /* INPUT (Data,Ary,Abs) */ D_INPUT, (D_DATA | D_ARRAY | D_ABSOLUTE), /* INPUT (Data,Ary,Abs) */
D_END_COLLECTION /* END_COLLECTION */ D_END_COLLECTION /* END_COLLECTION */
}; };
SystemControl_::SystemControl_(void) { SystemControl_::SystemControl_(void) {
static HIDSubDescriptor node(_hidMultiReportDescriptorSystem, sizeof(_hidMultiReportDescriptorSystem)); static HIDSubDescriptor node(_hidMultiReportDescriptorSystem, sizeof(_hidMultiReportDescriptorSystem));
HID().AppendDescriptor(&node); HID().AppendDescriptor(&node);
} }
void SystemControl_::begin(void) { void SystemControl_::begin(void) {
// release all buttons // release all buttons
end(); end();
} }
void SystemControl_::end(void) { void SystemControl_::end(void) {
uint8_t _report = 0x00; uint8_t _report = 0x00;
sendReport(&_report, sizeof(_report)); sendReport(&_report, sizeof(_report));
} }
void SystemControl_::write(uint8_t s) { void SystemControl_::write(uint8_t s) {
press(s); press(s);
release(); release();
} }
void SystemControl_::release(void) { void SystemControl_::release(void) {
begin(); begin();
} }
void SystemControl_::releaseAll(void) { void SystemControl_::releaseAll(void) {
begin(); begin();
} }
void SystemControl_::press(uint8_t s) { void SystemControl_::press(uint8_t s) {
if (s == HID_SYSTEM_WAKE_UP) { if (s == HID_SYSTEM_WAKE_UP) {
#ifdef __AVR__ #ifdef __AVR__
USBDevice.wakeupHost(); USBDevice.wakeupHost();
#endif #endif
#ifdef ARDUINO_ARCH_SAMD #ifdef ARDUINO_ARCH_SAMD
// This is USBDevice_SAMD21G18x.wakeupHost(). But we can't include that // This is USBDevice_SAMD21G18x.wakeupHost(). But we can't include that
// header, because it redefines a few symbols, and causes linking // header, because it redefines a few symbols, and causes linking
// errors. So we simply reimplement the same thing here. // errors. So we simply reimplement the same thing here.
USB->DEVICE.CTRLB.bit.UPRSM = 1; USB->DEVICE.CTRLB.bit.UPRSM = 1;
#endif #endif
} else { } else {
sendReport(&s, sizeof(s)); sendReport(&s, sizeof(s));
} }
} }
void SystemControl_::sendReport(void* data, int length) { void SystemControl_::sendReport(void* data, int length) {
HID().SendReport(HID_REPORTID_SYSTEMCONTROL, data, length); HID().SendReport(HID_REPORTID_SYSTEMCONTROL, data, length);
} }
SystemControl_ SystemControl; SystemControl_ SystemControl;

@ -32,24 +32,24 @@ THE SOFTWARE.
#include "../HIDTables.h" #include "../HIDTables.h"
typedef union { typedef union {
// Every usable system control key possible // Every usable system control key possible
uint8_t key; uint8_t key;
} HID_SystemControlReport_Data_t; } HID_SystemControlReport_Data_t;
class SystemControl_ { class SystemControl_ {
public: public:
void begin(void); void begin(void);
void end(void); void end(void);
void write(uint8_t s); void write(uint8_t s);
void press(uint8_t s); void press(uint8_t s);
void release(void); void release(void);
void releaseAll(void); void releaseAll(void);
void sendReport(void* data, int length); void sendReport(void* data, int length);
SystemControl_(void); SystemControl_(void);
protected: protected:
}; };

@ -27,91 +27,91 @@ THE SOFTWARE.
#include "HIDReportObserver.h" #include "HIDReportObserver.h"
static const uint8_t _hidSingleReportDescriptorAbsoluteMouse[] PROGMEM = { static const uint8_t _hidSingleReportDescriptorAbsoluteMouse[] PROGMEM = {
D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) 54 */ D_USAGE_PAGE, D_PAGE_GENERIC_DESKTOP, /* USAGE_PAGE (Generic Desktop) 54 */
D_USAGE, D_USAGE_MOUSE, /* USAGE (Mouse) */ D_USAGE, D_USAGE_MOUSE, /* USAGE (Mouse) */
D_COLLECTION, D_APPLICATION, /* COLLECTION (Application) */ D_COLLECTION, D_APPLICATION, /* COLLECTION (Application) */
DESCRIPTOR_ABS_MOUSE_BUTTONS DESCRIPTOR_ABS_MOUSE_BUTTONS
DESCRIPTOR_ABS_MOUSE_XY DESCRIPTOR_ABS_MOUSE_XY
DESCRIPTOR_ABS_MOUSE_WHEEL DESCRIPTOR_ABS_MOUSE_WHEEL
D_END_COLLECTION /* End */ D_END_COLLECTION /* End */
}; };
SingleAbsoluteMouse_::SingleAbsoluteMouse_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1) { SingleAbsoluteMouse_::SingleAbsoluteMouse_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1) {
epType[0] = EP_TYPE_INTERRUPT_IN; epType[0] = EP_TYPE_INTERRUPT_IN;
PluggableUSB().plug(this); PluggableUSB().plug(this);
} }
int SingleAbsoluteMouse_::getInterface(uint8_t* interfaceCount) { int SingleAbsoluteMouse_::getInterface(uint8_t* interfaceCount) {
*interfaceCount += 1; // uses 1 *interfaceCount += 1; // uses 1
HIDDescriptor hidInterface = { HIDDescriptor hidInterface = {
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE), D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
D_HIDREPORT(sizeof(_hidSingleReportDescriptorAbsoluteMouse)), D_HIDREPORT(sizeof(_hidSingleReportDescriptorAbsoluteMouse)),
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01)
}; };
return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
} }
int SingleAbsoluteMouse_::getDescriptor(USBSetup& setup) { int SingleAbsoluteMouse_::getDescriptor(USBSetup& setup) {
// Check if this is a HID Class Descriptor request // Check if this is a HID Class Descriptor request
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) {
return 0; return 0;
} }
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) {
return 0; return 0;
} }
// In a HID Class Descriptor wIndex cointains the interface number // In a HID Class Descriptor wIndex cointains the interface number
if (setup.wIndex != pluggedInterface) { if (setup.wIndex != pluggedInterface) {
return 0; return 0;
} }
// Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol
// due to the USB specs, but Windows and Linux just assumes its in report mode. // due to the USB specs, but Windows and Linux just assumes its in report mode.
protocol = HID_REPORT_PROTOCOL; protocol = HID_REPORT_PROTOCOL;
return USB_SendControl(TRANSFER_PGM, _hidSingleReportDescriptorAbsoluteMouse, sizeof(_hidSingleReportDescriptorAbsoluteMouse)); return USB_SendControl(TRANSFER_PGM, _hidSingleReportDescriptorAbsoluteMouse, sizeof(_hidSingleReportDescriptorAbsoluteMouse));
} }
bool SingleAbsoluteMouse_::setup(USBSetup& setup) { bool SingleAbsoluteMouse_::setup(USBSetup& setup) {
if (pluggedInterface != setup.wIndex) { if (pluggedInterface != setup.wIndex) {
return false; return false;
} }
uint8_t request = setup.bRequest;
uint8_t requestType = setup.bmRequestType;
uint8_t request = setup.bRequest; if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) {
uint8_t requestType = setup.bmRequestType; if (request == HID_GET_REPORT) {
// TODO(anyone): HID_GetReport();
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) { return true;
if (request == HID_GET_REPORT) { }
// TODO(anyone): HID_GetReport(); if (request == HID_GET_PROTOCOL) {
return true; // TODO(anyone): Send8(protocol);
} return true;
if (request == HID_GET_PROTOCOL) {
// TODO(anyone): Send8(protocol);
return true;
}
} }
}
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) { if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) {
if (request == HID_SET_PROTOCOL) { if (request == HID_SET_PROTOCOL) {
protocol = setup.wValueL; protocol = setup.wValueL;
return true; return true;
}
if (request == HID_SET_IDLE) {
idle = setup.wValueL;
return true;
}
if (request == HID_SET_REPORT) {
}
} }
if (request == HID_SET_IDLE) {
idle = setup.wValueL;
return true;
}
if (request == HID_SET_REPORT) {
}
}
return false; return false;
} }
void SingleAbsoluteMouse_::sendReport(void* data, int length) { void SingleAbsoluteMouse_::sendReport(void* data, int length) {
auto result = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, length); auto result = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, length);
HIDReportObserver::observeReport(HID_REPORTID_MOUSE_ABSOLUTE, data, length, result); HIDReportObserver::observeReport(HID_REPORTID_MOUSE_ABSOLUTE, data, length, result);
} }
SingleAbsoluteMouse_ SingleAbsoluteMouse; SingleAbsoluteMouse_ SingleAbsoluteMouse;

@ -33,21 +33,21 @@ THE SOFTWARE.
class SingleAbsoluteMouse_ : public PluggableUSBModule, public AbsoluteMouseAPI { class SingleAbsoluteMouse_ : public PluggableUSBModule, public AbsoluteMouseAPI {
public: public:
SingleAbsoluteMouse_(void); SingleAbsoluteMouse_(void);
uint8_t getLeds(void); uint8_t getLeds(void);
uint8_t getProtocol(void); uint8_t getProtocol(void);
protected: protected:
// Implementation of the PUSBListNode // Implementation of the PUSBListNode
int getInterface(uint8_t* interfaceCount); int getInterface(uint8_t* interfaceCount);
int getDescriptor(USBSetup& setup); int getDescriptor(USBSetup& setup);
bool setup(USBSetup& setup); bool setup(USBSetup& setup);
EPTYPE_DESCRIPTOR_SIZE epType[1]; EPTYPE_DESCRIPTOR_SIZE epType[1];
uint8_t protocol; uint8_t protocol;
uint8_t idle; uint8_t idle;
inline void sendReport(void* data, int length) override; inline void sendReport(void* data, int length) override;
}; };
extern SingleAbsoluteMouse_ SingleAbsoluteMouse; extern SingleAbsoluteMouse_ SingleAbsoluteMouse;

@ -3,11 +3,11 @@
#ifdef ARDUINO_ARCH_SAMD #ifdef ARDUINO_ARCH_SAMD
int USB_SendControl(void* b, unsigned char c) { int USB_SendControl(void* b, unsigned char c) {
USBDevice.sendControl(b, c); USBDevice.sendControl(b, c);
} }
int USB_SendControl(uint8_t a, const void* b, uint8_t c) { int USB_SendControl(uint8_t a, const void* b, uint8_t c) {
USBDevice.sendControl(b, c); USBDevice.sendControl(b, c);
} }
void USB_PackMessages(bool pack) { void USB_PackMessages(bool pack) {

Loading…
Cancel
Save