Skip to content

Commit 897e842

Browse files
author
abratchik
committed
Enhanced HID protocol support for Arduino Leonardo & Pro Micro
1 parent 60f0d0b commit 897e842

File tree

4 files changed

+187
-41
lines changed

4 files changed

+187
-41
lines changed

cores/arduino/USBAPI.h

+1
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ bool CDC_Setup(USBSetup& setup);
196196
int USB_SendControl(uint8_t flags, const void* d, int len);
197197
int USB_RecvControl(void* d, int len);
198198
int USB_RecvControlLong(void* d, int len);
199+
bool USB_SendStringDescriptor(const u8*, u8, uint8_t);
199200

200201
uint8_t USB_Available(uint8_t ep);
201202
uint8_t USB_SendSpace(uint8_t ep);

cores/arduino/USBCore.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ int USB_SendControl(u8 flags, const void* d, int len)
426426
// Send a USB descriptor string. The string is stored in PROGMEM as a
427427
// plain ASCII string but is sent out as UTF-16 with the correct 2-byte
428428
// prefix
429-
static bool USB_SendStringDescriptor(const u8*string_P, u8 string_len, uint8_t flags) {
429+
bool USB_SendStringDescriptor(const u8*string_P, u8 string_len, uint8_t flags) {
430430
SendControl(2 + string_len * 2);
431431
SendControl(3);
432432
bool pgm = flags & TRANSFER_PGM;

libraries/HID/src/HID.cpp

+120-19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Copyright (c) 2015, Arduino LLC
33
Original code (pre-library): Copyright (c) 2011, Peter Barrett
4+
Modified code: Copyright (c) 2020, Aleksandr Bratchik
45
56
Permission to use, copy, modify, and/or distribute this software for
67
any purpose with or without fee is hereby granted, provided that the
@@ -30,18 +31,35 @@ int HID_::getInterface(uint8_t* interfaceCount)
3031
{
3132
*interfaceCount += 1; // uses 1
3233
HIDDescriptor hidInterface = {
33-
D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
34+
D_INTERFACE(pluggedInterface, 2, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
3435
D_HIDREPORT(descriptorSize),
35-
D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01)
36+
D_ENDPOINT(USB_ENDPOINT_IN(HID_TX), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x14),
37+
D_ENDPOINT(USB_ENDPOINT_OUT(HID_RX), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x0A)
3638
};
3739
return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
3840
}
3941

4042
int HID_::getDescriptor(USBSetup& setup)
4143
{
44+
45+
u8 t = setup.wValueH;
46+
47+
// HID-specific strings
48+
if(USB_STRING_DESCRIPTOR_TYPE == t) {
49+
50+
// we place all strings in the 0xFF00-0xFFFE range
51+
HIDReport* rep = GetFeature(0xFF00 | setup.wValueL );
52+
if(rep) {
53+
return USB_SendStringDescriptor((u8*)rep->data, strlen_P((char*)rep->data), TRANSFER_PGM);
54+
}
55+
else {
56+
return 0;
57+
}
58+
}
59+
4260
// Check if this is a HID Class Descriptor request
4361
if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; }
44-
if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; }
62+
if (HID_REPORT_DESCRIPTOR_TYPE != t) { return 0; }
4563

4664
// In a HID Class Descriptor wIndex cointains the interface number
4765
if (setup.wIndex != pluggedInterface) { return 0; }
@@ -64,12 +82,23 @@ int HID_::getDescriptor(USBSetup& setup)
6482

6583
uint8_t HID_::getShortName(char *name)
6684
{
85+
if(serial) {
86+
for(byte i=0; i<strlen_P(serial); i++) {
87+
name[i] = pgm_read_byte_near(serial + i);
88+
}
89+
return strlen_P(serial);
90+
}
91+
else {
92+
93+
// default serial number
94+
6795
name[0] = 'H';
6896
name[1] = 'I';
6997
name[2] = 'D';
7098
name[3] = 'A' + (descriptorSize & 0x0F);
7199
name[4] = 'A' + ((descriptorSize >> 4) & 0x0F);
72100
return 5;
101+
}
73102
}
74103

75104
void HID_::AppendDescriptor(HIDSubDescriptor *node)
@@ -86,28 +115,91 @@ void HID_::AppendDescriptor(HIDSubDescriptor *node)
86115
descriptorSize += node->length;
87116
}
88117

89-
int HID_::SendReport(uint8_t id, const void* data, int len)
118+
int HID_::SetFeature(uint16_t id, const void* data, int len)
90119
{
91-
auto ret = USB_Send(pluggedEndpoint, &id, 1);
120+
if(!rootReport) {
121+
rootReport = new HIDReport(id, data, len);
122+
} else {
123+
HIDReport* current;
124+
int i=0;
125+
for ( current = rootReport; current; current = current->next, i++) {
126+
if(current->id == id) {
127+
return i;
128+
}
129+
// check if we are on the last report
130+
if(!current->next) {
131+
current->next = new HIDReport(id, data, len);
132+
break;
133+
}
134+
}
135+
136+
}
137+
138+
reportCount++;
139+
return reportCount;
140+
}
141+
142+
bool HID_::LockFeature(uint16_t id, bool lock) {
143+
if(rootReport) {
144+
HIDReport* current;
145+
for(current = rootReport;current; current=current->next) {
146+
if(current->id == id) {
147+
current->lock = lock;
148+
return true;
149+
}
150+
}
151+
}
152+
return false;
153+
}
154+
155+
156+
int HID_::SendReport(uint16_t id, const void* data, int len)
157+
{
158+
auto ret = USB_Send(HID_TX, &id, 1);
92159
if (ret < 0) return ret;
93-
auto ret2 = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, len);
160+
auto ret2 = USB_Send(HID_TX | TRANSFER_RELEASE, data, len);
94161
if (ret2 < 0) return ret2;
95162
return ret + ret2;
96163
}
97164

98-
bool HID_::setup(USBSetup& setup)
165+
HIDReport* HID_::GetFeature(uint16_t id)
99166
{
167+
HIDReport* current;
168+
int i=0;
169+
for(current=rootReport; current && i<reportCount; current=current->next, i++) {
170+
if(id == current->id) {
171+
return current;
172+
}
173+
}
174+
return (HIDReport*) NULL;
175+
}
176+
177+
bool HID_::setup(USBSetup& setup)
178+
{
100179
if (pluggedInterface != setup.wIndex) {
101180
return false;
102181
}
103182

104183
uint8_t request = setup.bRequest;
105184
uint8_t requestType = setup.bmRequestType;
106-
185+
107186
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
108-
{
187+
{
109188
if (request == HID_GET_REPORT) {
110-
// TODO: HID_GetReport();
189+
190+
if(setup.wValueH == HID_REPORT_TYPE_FEATURE)
191+
{
192+
193+
HIDReport* current = GetFeature(setup.wValueL);
194+
if(current){
195+
if(USB_SendControl(0, &(current->id), 1)>0 &&
196+
USB_SendControl(0, current->data, current->length)>0)
197+
return true;
198+
}
199+
200+
return false;
201+
202+
}
111203
return true;
112204
}
113205
if (request == HID_GET_PROTOCOL) {
@@ -120,7 +212,7 @@ bool HID_::setup(USBSetup& setup)
120212
}
121213

122214
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE)
123-
{
215+
{
124216
if (request == HID_SET_PROTOCOL) {
125217
// The USB Host tells us if we are in boot or report mode.
126218
// This only works with a real boot compatible device.
@@ -133,24 +225,33 @@ bool HID_::setup(USBSetup& setup)
133225
}
134226
if (request == HID_SET_REPORT)
135227
{
136-
//uint8_t reportID = setup.wValueL;
137-
//uint16_t length = setup.wLength;
138-
//uint8_t data[length];
139-
// Make sure to not read more data than USB_EP_SIZE.
140-
// You can read multiple times through a loop.
141-
// The first byte (may!) contain the reportID on a multreport.
142-
//USB_RecvControl(data, length);
228+
if(setup.wValueH == HID_REPORT_TYPE_FEATURE)
229+
{
230+
231+
HIDReport* current = GetFeature(setup.wValueL);
232+
if(!current) return false;
233+
if(setup.wLength != current->length + 1) return false;
234+
uint8_t* data = new uint8_t[setup.wLength];
235+
USB_RecvControl(data, setup.wLength);
236+
if(*data != current->id) return false;
237+
memcpy((uint8_t*)current->data, data+1, current->length);
238+
delete[] data;
239+
return true;
240+
241+
}
242+
143243
}
144244
}
145245

146246
return false;
147247
}
148248

149-
HID_::HID_(void) : PluggableUSBModule(1, 1, epType),
249+
HID_::HID_(void) : PluggableUSBModule(2, 1, epType),
150250
rootNode(NULL), descriptorSize(0),
151251
protocol(HID_REPORT_PROTOCOL), idle(1)
152252
{
153253
epType[0] = EP_TYPE_INTERRUPT_IN;
254+
epType[1] = EP_TYPE_INTERRUPT_OUT;
154255
PluggableUSB().plug(this);
155256
}
156257

libraries/HID/src/HID.h

+65-21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Copyright (c) 2015, Arduino LLC
33
Original code (pre-library): Copyright (c) 2011, Peter Barrett
4+
Modified code: Copyright (c) 2020, Aleksandr Bratchik
45
56
Permission to use, copy, modify, and/or distribute this software for
67
any purpose with or without fee is hereby granted, provided that the
@@ -21,7 +22,8 @@
2122

2223
#include <stdint.h>
2324
#include <Arduino.h>
24-
#include "PluggableUSB.h"
25+
#include <HardwareSerial.h>
26+
#include <PluggableUSB.h>
2527

2628
#if defined(USBCON)
2729

@@ -59,6 +61,14 @@
5961
#define HID_REPORT_TYPE_OUTPUT 2
6062
#define HID_REPORT_TYPE_FEATURE 3
6163

64+
#define HID_INTERFACE (CDC_ACM_INTERFACE + CDC_INTERFACE_COUNT) // HID Interface
65+
#define HID_FIRST_ENDPOINT (CDC_FIRST_ENDPOINT + CDC_ENPOINT_COUNT)
66+
#define HID_ENDPOINT_INT (HID_FIRST_ENDPOINT)
67+
#define HID_ENDPOINT_OUT (HID_FIRST_ENDPOINT+1)
68+
69+
#define HID_TX HID_ENDPOINT_INT
70+
#define HID_RX HID_ENDPOINT_OUT //++ EP HID_RX for ease of use with USB_Available & USB_Rec
71+
6272
typedef struct
6373
{
6474
uint8_t len; // 9
@@ -77,12 +87,24 @@ typedef struct
7787
InterfaceDescriptor hid;
7888
HIDDescDescriptor desc;
7989
EndpointDescriptor in;
90+
EndpointDescriptor out; //added
8091
} HIDDescriptor;
8192

93+
class HIDReport {
94+
public:
95+
HIDReport *next = NULL;
96+
HIDReport(uint16_t i, const void *d, uint8_t l) : id(i), data(d), length(l) {}
97+
98+
uint16_t id;
99+
const void* data;
100+
uint16_t length;
101+
bool lock;
102+
};
103+
82104
class HIDSubDescriptor {
83105
public:
84106
HIDSubDescriptor *next = NULL;
85-
HIDSubDescriptor(const void *d, const uint16_t l) : data(d), length(l) { }
107+
HIDSubDescriptor(const void *d, uint16_t l) : data(d), length(l) { }
86108

87109
const void* data;
88110
const uint16_t length;
@@ -91,34 +113,56 @@ class HIDSubDescriptor {
91113
class HID_ : public PluggableUSBModule
92114
{
93115
public:
94-
HID_(void);
95-
int begin(void);
96-
int SendReport(uint8_t id, const void* data, int len);
97-
void AppendDescriptor(HIDSubDescriptor* node);
98-
116+
HID_(void);
117+
int begin(void);
118+
int SendReport(uint16_t id, const void* data, int len);
119+
int SetFeature(uint16_t id, const void* data, int len);
120+
bool LockFeature(uint16_t id, bool lock);
121+
122+
void AppendDescriptor(HIDSubDescriptor* node);
123+
124+
void setOutput(Serial_& out) {
125+
dbg = &out;
126+
}
127+
128+
void setSerial(const char* s) {
129+
serial = s;
130+
}
131+
132+
HIDReport* GetFeature(uint16_t id);
133+
99134
protected:
100-
// Implementation of the PluggableUSBModule
101-
int getInterface(uint8_t* interfaceCount);
102-
int getDescriptor(USBSetup& setup);
103-
bool setup(USBSetup& setup);
104-
uint8_t getShortName(char* name);
105-
135+
// Implementation of the PluggableUSBModule
136+
int getInterface(uint8_t* interfaceCount);
137+
int getDescriptor(USBSetup& setup);
138+
bool setup(USBSetup& setup);
139+
uint8_t getShortName(char* name);
140+
106141
private:
107-
uint8_t epType[1];
108-
109-
HIDSubDescriptor* rootNode;
110-
uint16_t descriptorSize;
111-
112-
uint8_t protocol;
113-
uint8_t idle;
142+
uint8_t epType[2];
143+
144+
HIDSubDescriptor* rootNode;
145+
uint16_t descriptorSize;
146+
147+
uint8_t protocol;
148+
uint8_t idle;
149+
150+
// Buffer pointer to hold the feature data
151+
HIDReport* rootReport;
152+
uint16_t reportCount;
153+
154+
Serial_ *dbg;
155+
156+
const char *serial;
157+
114158
};
115159

116160
// Replacement for global singleton.
117161
// This function prevents static-initialization-order-fiasco
118162
// https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use
119163
HID_& HID();
120164

121-
#define D_HIDREPORT(length) { 9, 0x21, 0x01, 0x01, 0, 1, 0x22, lowByte(length), highByte(length) }
165+
#define D_HIDREPORT(length) { 9, 0x21, 0x01, 0x01, 0x21, 1, 0x22, lowByte(length), highByte(length) }
122166

123167
#endif // USBCON
124168

0 commit comments

Comments
 (0)