Neale Pickett
·
2023-12-31
hid.hh
1#define _USING_HID
2
3// HID 'Driver'
4// ------------
5#define HID_GET_REPORT 0x01
6#define HID_GET_IDLE 0x02
7#define HID_GET_PROTOCOL 0x03
8#define HID_SET_REPORT 0x09
9#define HID_SET_IDLE 0x0A
10#define HID_SET_PROTOCOL 0x0B
11
12#define HID_HID_DESCRIPTOR_TYPE 0x21
13#define HID_REPORT_DESCRIPTOR_TYPE 0x22
14#define HID_PHYSICAL_DESCRIPTOR_TYPE 0x23
15
16// HID subclass HID1.11 Page 8 4.2 Subclass
17#define HID_SUBCLASS_NONE 0
18#define HID_SUBCLASS_BOOT_INTERFACE 1
19
20// HID Keyboard/Mouse bios compatible protocols HID1.11 Page 9 4.3 Protocols
21#define HID_PROTOCOL_NONE 0
22#define HID_PROTOCOL_KEYBOARD 1
23#define HID_PROTOCOL_MOUSE 2
24
25// Normal or bios protocol (Keyboard/Mouse) HID1.11 Page 54 7.2.5 Get_Protocol Request
26// "protocol" variable is used for this purpose.
27#define HID_BOOT_PROTOCOL 0
28#define HID_REPORT_PROTOCOL 1
29
30// HID Request Type HID1.11 Page 51 7.2.1 Get_Report Request
31#define HID_REPORT_TYPE_INPUT 1
32#define HID_REPORT_TYPE_OUTPUT 2
33#define HID_REPORT_TYPE_FEATURE 3
34
35typedef struct
36{
37 uint8_t len; // 9
38 uint8_t dtype; // 0x21
39 uint8_t addr;
40 uint8_t versionL; // 0x101
41 uint8_t versionH; // 0x101
42 uint8_t country;
43 uint8_t desctype; // 0x22 report
44 uint8_t descLenL;
45 uint8_t descLenH;
46} HIDDescDescriptor;
47
48typedef struct
49{
50 InterfaceDescriptor hid;
51 HIDDescDescriptor desc;
52 EndpointDescriptor in;
53} HIDDescriptor;
54
55class HIDSubDescriptor {
56public:
57 HIDSubDescriptor *next = NULL;
58 HIDSubDescriptor(const void *d, const uint16_t l) : data(d), length(l) { }
59
60 const void* data;
61 const uint16_t length;
62};
63
64class HID_ : public PluggableUSBModule
65{
66public:
67 HID_(void);
68 int begin(void);
69 int SendReport(uint8_t id, const void* data, int len);
70 void AppendDescriptor(HIDSubDescriptor* node);
71
72protected:
73 // Implementation of the PluggableUSBModule
74 int getInterface(uint8_t* interfaceCount);
75 int getDescriptor(USBSetup& setup);
76 bool setup(USBSetup& setup);
77 uint8_t getShortName(char* name);
78
79private:
80 uint8_t epType[1];
81
82 HIDSubDescriptor* rootNode;
83 uint16_t descriptorSize;
84
85 uint8_t protocol;
86 uint8_t idle;
87};
88
89// Replacement for global singleton.
90// This function prevents static-initialization-order-fiasco
91// https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use
92HID_& HID();
93
94#define D_HIDREPORT(length) { 9, 0x21, 0x01, 0x01, 0, 1, 0x22, lowByte(length), highByte(length) }
95
96
97HID_& HID()
98{
99 static HID_ obj;
100 return obj;
101}
102
103int HID_::getInterface(uint8_t* interfaceCount)
104{
105 *interfaceCount += 1; // uses 1
106 HIDDescriptor hidInterface = {
107 D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
108 D_HIDREPORT(descriptorSize),
109 D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01)
110 };
111 return USB_SendControl(0, &hidInterface, sizeof(hidInterface));
112}
113
114int HID_::getDescriptor(USBSetup& setup)
115{
116 // Check if this is a HID Class Descriptor request
117 if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; }
118 if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; }
119
120 // In a HID Class Descriptor wIndex contains the interface number
121 if (setup.wIndex != pluggedInterface) { return 0; }
122
123 int total = 0;
124 HIDSubDescriptor* node;
125 for (node = rootNode; node; node = node->next) {
126 int res = USB_SendControl(TRANSFER_PGM, node->data, node->length);
127 if (res == -1)
128 return -1;
129 total += res;
130 }
131
132 // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol
133 // due to the USB specs, but Windows and Linux just assumes its in report mode.
134 protocol = HID_REPORT_PROTOCOL;
135
136 return total;
137}
138
139uint8_t HID_::getShortName(char *name)
140{
141 name[0] = 'H';
142 name[1] = 'I';
143 name[2] = 'D';
144 name[3] = 'A' + (descriptorSize & 0x0F);
145 name[4] = 'A' + ((descriptorSize >> 4) & 0x0F);
146 return 5;
147}
148
149void HID_::AppendDescriptor(HIDSubDescriptor *node)
150{
151 if (!rootNode) {
152 rootNode = node;
153 } else {
154 HIDSubDescriptor *current = rootNode;
155 while (current->next) {
156 current = current->next;
157 }
158 current->next = node;
159 }
160 descriptorSize += node->length;
161}
162
163int HID_::SendReport(uint8_t id, const void* data, int len)
164{
165 // auto ret = USB_Send(pluggedEndpoint, &id, 1);
166 // if (ret < 0) return ret;
167 auto ret2 = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, len);
168 if (ret2 < 0) return ret2;
169 return ret2;
170}
171
172bool HID_::setup(USBSetup& setup)
173{
174 if (pluggedInterface != setup.wIndex) {
175 return false;
176 }
177
178 uint8_t request = setup.bRequest;
179 uint8_t requestType = setup.bmRequestType;
180
181 if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
182 {
183 if (request == HID_GET_REPORT) {
184 // TODO: HID_GetReport();
185 return true;
186 }
187 if (request == HID_GET_PROTOCOL) {
188 // TODO: Send8(protocol);
189 return true;
190 }
191 if (request == HID_GET_IDLE) {
192 // TODO: Send8(idle);
193 }
194 }
195
196 if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE)
197 {
198 if (request == HID_SET_PROTOCOL) {
199 // The USB Host tells us if we are in boot or report mode.
200 // This only works with a real boot compatible device.
201 protocol = setup.wValueL;
202 return true;
203 }
204 if (request == HID_SET_IDLE) {
205 idle = setup.wValueL;
206 return true;
207 }
208 if (request == HID_SET_REPORT)
209 {
210 //uint8_t reportID = setup.wValueL;
211 //uint16_t length = setup.wLength;
212 //uint8_t data[length];
213 // Make sure to not read more data than USB_EP_SIZE.
214 // You can read multiple times through a loop.
215 // The first byte (may!) contain the reportID on a multreport.
216 //USB_RecvControl(data, length);
217 }
218 }
219
220 return false;
221}
222
223HID_::HID_(void) : PluggableUSBModule(1, 1, epType),
224 rootNode(NULL), descriptorSize(0),
225 protocol(HID_REPORT_PROTOCOL), idle(1)
226{
227 epType[0] = EP_TYPE_INTERRUPT_IN;
228 PluggableUSB().plug(this);
229}
230
231int HID_::begin(void)
232{
233 return 0;
234}