From 02945cc78eaa5fb073290df3af70bc74fcaaac8e Mon Sep 17 00:00:00 2001
From: Martino Facchin <m.facchin@arduino.cc>
Date: Mon, 5 Oct 2015 18:27:43 +0200
Subject: [PATCH] [PluggableUSB] port to stable API

---
 cores/arduino/USB/PluggableUSB.cpp |  95 ++++++++------
 cores/arduino/USB/PluggableUSB.h   |  61 +++++----
 cores/arduino/USB/USBAPI.h         |   3 +-
 cores/arduino/USB/USBCore.cpp      |  10 +-
 cores/arduino/USB/USBCore.h        |   8 +-
 libraries/HID/HID.cpp              | 204 +++++++++++++----------------
 libraries/HID/HID.h                | 132 +++++++++++--------
 7 files changed, 266 insertions(+), 247 deletions(-)

diff --git a/cores/arduino/USB/PluggableUSB.cpp b/cores/arduino/USB/PluggableUSB.cpp
index 1a15dfa4..0f2d08c0 100644
--- a/cores/arduino/USB/PluggableUSB.cpp
+++ b/cores/arduino/USB/PluggableUSB.cpp
@@ -19,80 +19,89 @@
 
 #include "USBAPI.h"
 #include "USBDesc.h"
+#include "USBCore.h"
 #include "PluggableUSB.h"
 
+#if defined(USBCON)
 #ifdef PLUGGABLE_USB_ENABLED
 
-#define MAX_MODULES	6
-
-static uint8_t lastIf = CDC_ACM_INTERFACE + CDC_INTERFACE_COUNT;
-static uint8_t lastEp = CDC_FIRST_ENDPOINT + CDC_ENPOINT_COUNT;
-
 extern uint32_t EndPoints[];
 
-//PUSBCallbacks cbs[MAX_MODULES];
-static uint8_t modules_count = 0;
-
-static PUSBListNode* rootNode = NULL;
-
-int PUSB_GetInterface(uint8_t* interfaceNum)
+int PluggableUSB_::getInterface(uint8_t* interfaceCount)
 {
-	int ret = 0;
-	PUSBListNode* node = rootNode;
-	for (uint8_t i=0; i<modules_count; i++) {
-		ret += node->cb->getInterface(interfaceNum);
-		node = node->next;
+	int sent = 0;
+	PluggableUSBModule* node;
+	for (node = rootNode; node; node = node->next) {
+		int res = node->getInterface(interfaceCount);
+		if (res < 0)
+			return -1;
+		sent += res;
 	}
-	return ret;
+	return sent;
 }
 
-int PUSB_GetDescriptor(int8_t t)
+int PluggableUSB_::getDescriptor(USBSetup& setup)
 {
-	int ret = 0;
-	PUSBListNode* node = rootNode;
-	for (uint8_t i=0; i<modules_count && ret == 0; i++) {
-		ret = node->cb->getDescriptor(t);
-		node = node->next;
+	PluggableUSBModule* node;
+	for (node = rootNode; node; node = node->next) {
+		int ret = node->getDescriptor(setup);
+		// ret!=0 -> request has been processed
+		if (ret)
+			return ret;
 	}
-	return ret;
+	return 0;
 }
 
-bool PUSB_Setup(USBSetup& setup, uint8_t j)
+bool PluggableUSB_::setup(USBSetup& setup)
 {
-	bool ret = false;
-	PUSBListNode* node = rootNode;
-	for (uint8_t i=0; i<modules_count && ret == false; i++) {
-		ret = node->cb->setup(setup, j);
-		node = node->next;
+	PluggableUSBModule* node;
+	for (node = rootNode; node; node = node->next) {
+		if (node->setup(setup)) {
+			return true;
+		}
 	}
-	return ret;
+	return false;
 }
 
-int8_t PUSB_AddFunction(PUSBListNode *node, uint8_t* interface)
+bool PluggableUSB_::plug(PluggableUSBModule *node)
 {
-	if (modules_count >= MAX_MODULES) {
-		return 0;
+	if ((lastEp + node->numEndpoints) > USB_ENDPOINTS) {
+		return false;
 	}
 
-	if (modules_count == 0) {
+	if (!rootNode) {
 		rootNode = node;
 	} else {
-		PUSBListNode *current = rootNode;
-		while(current->next != NULL) {
+		PluggableUSBModule *current = rootNode;
+		while (current->next) {
 			current = current->next;
 		}
 		current->next = node;
 	}
 
-	*interface = lastIf;
-	lastIf += node->cb->numInterfaces;
-	for ( uint8_t i = 0; i< node->cb->numEndpoints; i++) {
-		EndPoints[lastEp] = node->cb->endpointType[i];
+	node->pluggedInterface = lastIf;
+	node->pluggedEndpoint = lastEp;
+	lastIf += node->numInterfaces;
+	for (uint8_t i = 0; i < node->numEndpoints; i++) {
+		EndPoints[lastEp] = node->endpointType[i];
 		lastEp++;
 	}
-	modules_count++;
-	return lastEp - node->cb->numEndpoints;
+	return true;
 	// restart USB layer???
 }
 
+PluggableUSB_& PluggableUSB()
+{
+	static PluggableUSB_ obj;
+	return obj;
+}
+
+PluggableUSB_::PluggableUSB_() : lastIf(CDC_ACM_INTERFACE + CDC_INTERFACE_COUNT),
+                                 lastEp(CDC_FIRST_ENDPOINT + CDC_ENPOINT_COUNT),
+                                 rootNode(NULL)
+{
+	// Empty
+}
+
+#endif
 #endif
\ No newline at end of file
diff --git a/cores/arduino/USB/PluggableUSB.h b/cores/arduino/USB/PluggableUSB.h
index 502da9eb..eb18ca25 100644
--- a/cores/arduino/USB/PluggableUSB.h
+++ b/cores/arduino/USB/PluggableUSB.h
@@ -25,38 +25,47 @@
 
 #if defined(USBCON)
 
-typedef struct __attribute__((packed))
-{
-  bool (*setup)(USBSetup& setup, uint8_t i);
-  int (*getInterface)(uint8_t* interfaceNum);
-  int (*getDescriptor)(int8_t t);
-  int8_t numEndpoints;
-  int8_t numInterfaces;
-  uint32_t *endpointType;
-} PUSBCallbacks;
-
-typedef struct
-{
-  uint8_t interface;
-  uint8_t firstEndpoint;
-} PUSBReturn;
-
-class PUSBListNode {
+class PluggableUSBModule {
 public:
-  PUSBListNode *next = NULL;
-  PUSBCallbacks *cb;
-  PUSBListNode(PUSBCallbacks *ncb) {cb = ncb;}
-};
+  PluggableUSBModule(uint8_t numEps, uint8_t numIfs, uint32_t *epType) :
+    numEndpoints(numEps), numInterfaces(numIfs), endpointType(epType)
+  { }
+
+protected:
+  virtual bool setup(USBSetup& setup) = 0;
+  virtual int getInterface(uint8_t* interfaceCount) = 0;
+  virtual int getDescriptor(USBSetup& setup) = 0;
+
+  uint8_t pluggedInterface;
+  uint8_t pluggedEndpoint;
 
-int8_t PUSB_AddFunction(PUSBListNode *node, uint8_t *interface);
+  const uint8_t numEndpoints;
+  const uint8_t numInterfaces;
+  const uint32_t *endpointType;
 
-int PUSB_GetInterface(uint8_t* interfaceNum);
+  PluggableUSBModule *next = NULL;
 
-int PUSB_GetDescriptor(int8_t t);
+  friend class PluggableUSB_;
+};
 
-bool PUSB_Setup(USBSetup& setup, uint8_t i);
+class PluggableUSB_ {
+public:
+  PluggableUSB_();
+  bool plug(PluggableUSBModule *node);
+  int getInterface(uint8_t* interfaceCount);
+  int getDescriptor(USBSetup& setup);
+  bool setup(USBSetup& setup);
+
+private:
+  uint8_t lastIf;
+  uint8_t lastEp;
+  PluggableUSBModule* rootNode;
+};
 
-void PUSB_Begin();
+// Replacement for global singleton.
+// This function prevents static-initialization-order-fiasco
+// https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use
+PluggableUSB_& PluggableUSB();
 
 #endif
 
diff --git a/cores/arduino/USB/USBAPI.h b/cores/arduino/USB/USBAPI.h
index 23d0ae24..f4a0babf 100644
--- a/cores/arduino/USB/USBAPI.h
+++ b/cores/arduino/USB/USBAPI.h
@@ -78,6 +78,7 @@ public:
 	bool sendStringDescriptor(const uint8_t *string, uint8_t maxlen);
 	void initControl(int end);
 	uint8_t SendInterfaces(uint32_t* total);
+	void packMessages(bool val);
 
 	// Generic EndPoint API
 	void initEndpoints(void);
@@ -100,8 +101,6 @@ public:
 	void ISRHandler();
 
 private:
-	void packMessages(bool val);
-
 	bool initialized;
 };
 
diff --git a/cores/arduino/USB/USBCore.cpp b/cores/arduino/USB/USBCore.cpp
index ddc20146..a584a638 100644
--- a/cores/arduino/USB/USBCore.cpp
+++ b/cores/arduino/USB/USBCore.cpp
@@ -75,10 +75,10 @@ volatile uint32_t _usbConfiguration = 0;
 volatile uint32_t _usbSetInterface = 0;
 
 static __attribute__((__aligned__(4))) //__attribute__((__section__(".bss_hram0")))
-uint8_t udd_ep_out_cache_buffer[6][64];
+uint8_t udd_ep_out_cache_buffer[7][64];
 
 static __attribute__((__aligned__(4))) //__attribute__((__section__(".bss_hram0")))
-uint8_t udd_ep_in_cache_buffer[6][64];
+uint8_t udd_ep_in_cache_buffer[7][64];
 
 //==================================================================
 
@@ -129,7 +129,7 @@ uint8_t USBDeviceClass::SendInterfaces(uint32_t* total)
 #endif
 
 #ifdef PLUGGABLE_USB_ENABLED
-	total[0] += PUSB_GetInterface(&interfaces);
+	total[0] += PluggableUSB().getInterface(&interfaces);
 #endif
 
 	return interfaces;
@@ -180,7 +180,7 @@ bool USBDeviceClass::sendDescriptor(USBSetup &setup)
 	}
 
 #ifdef PLUGGABLE_USB_ENABLED
-	ret = PUSB_GetDescriptor(t);
+	ret = PluggableUSB().getDescriptor(setup);
 	if (ret != 0) {
 		return (ret > 0 ? true : false);
 	}
@@ -360,7 +360,7 @@ bool USBDeviceClass::handleClassInterfaceSetup(USBSetup& setup)
 	#endif
 
 	#if defined(PLUGGABLE_USB_ENABLED)
-	bool ret = PUSB_Setup(setup, i);
+	bool ret = PluggableUSB().setup(setup);
 	if ( ret == false) {
 		sendZlp(0);
 	}
diff --git a/cores/arduino/USB/USBCore.h b/cores/arduino/USB/USBCore.h
index 0d2dc785..853f305a 100644
--- a/cores/arduino/USB/USBCore.h
+++ b/cores/arduino/USB/USBCore.h
@@ -36,6 +36,8 @@
 #define USB_ENDPOINT_OUT(addr)                 ((addr) | 0x00)
 #define USB_ENDPOINT_IN(addr)                  ((addr) | 0x80)
 
+#define USB_ENDPOINTS                          7
+
 #define USB_ENDPOINT_TYPE_MASK                 0x03
 #define USB_ENDPOINT_TYPE_CONTROL              0x00
 #define USB_ENDPOINT_TYPE_ISOCHRONOUS          0x01
@@ -58,9 +60,9 @@
 #define REQUEST_OTHER				0x03
 #define REQUEST_RECIPIENT			0x1F
 
-#define REQUEST_DEVICETOHOST_CLASS_INTERFACE  (REQUEST_DEVICETOHOST + REQUEST_CLASS + REQUEST_INTERFACE)
-#define REQUEST_HOSTTODEVICE_CLASS_INTERFACE  (REQUEST_HOSTTODEVICE + REQUEST_CLASS + REQUEST_INTERFACE)
-
+#define REQUEST_DEVICETOHOST_CLASS_INTERFACE    (REQUEST_DEVICETOHOST | REQUEST_CLASS | REQUEST_INTERFACE)
+#define REQUEST_HOSTTODEVICE_CLASS_INTERFACE    (REQUEST_HOSTTODEVICE | REQUEST_CLASS | REQUEST_INTERFACE)
+#define REQUEST_DEVICETOHOST_STANDARD_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_STANDARD | REQUEST_INTERFACE)
 //	Class requests
 
 #define CDC_SET_LINE_CODING			0x20
diff --git a/libraries/HID/HID.cpp b/libraries/HID/HID.cpp
index c4bbd5cd..94bf9f59 100644
--- a/libraries/HID/HID.cpp
+++ b/libraries/HID/HID.cpp
@@ -19,158 +19,132 @@
 #include "USB/PluggableUSB.h"
 #include "HID.h"
 
-HID_ HID;
+#if defined(USBCON)
 
-static uint8_t HID_ENDPOINT_INT;
-
-//================================================================================
-//================================================================================
-
-//	HID report descriptor
-
-#define LSB(_x) ((_x) & 0xFF)
-#define MSB(_x) ((_x) >> 8)
-
-#define RAWHID_USAGE_PAGE	0xFFC0
-#define RAWHID_USAGE		0x0C00
-#define RAWHID_TX_SIZE 64
-#define RAWHID_RX_SIZE 64
-
-static uint8_t HID_INTERFACE;
-
-HIDDescriptor _hidInterface;
-
-static HIDDescriptorListNode* rootNode = NULL;
-static uint8_t sizeof_hidReportDescriptor = 0;
-static uint8_t modules_count = 0;
-//================================================================================
-//================================================================================
-//	Driver
-
-uint8_t _hid_protocol = 1;
-uint8_t _hid_idle = 1;
+HID_& HID()
+{
+	static HID_ obj;
+	return obj;
+}
 
-int HID_GetInterface(uint8_t* interfaceNum)
+int HID_::getInterface(uint8_t* interfaceCount)
 {
-	interfaceNum[0] += 1;	// uses 1
-	_hidInterface =
-	{
-		D_INTERFACE(HID_INTERFACE,1,3,0,0),
-		D_HIDREPORT(sizeof_hidReportDescriptor),
-		D_ENDPOINT(USB_ENDPOINT_IN (HID_ENDPOINT_INT),USB_ENDPOINT_TYPE_INTERRUPT,0x40,0x01)
+	*interfaceCount += 1; // uses 1
+	HIDDescriptor hidInterface = {
+		D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE),
+		D_HIDREPORT(descriptorSize),
+		D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, 0x40, 0x01)
 	};
-	return USBDevice.sendControl(&_hidInterface,sizeof(_hidInterface));
+	return USBDevice.sendControl(&hidInterface, sizeof(hidInterface));
 }
 
-int HID_GetDescriptor(int8_t t)
+int HID_::getDescriptor(USBSetup& setup)
 {
-	if (HID_REPORT_DESCRIPTOR_TYPE == t) {
-		HIDDescriptorListNode* current = rootNode;
-		int total = 0;
-		while(current != NULL) {
-			total += USBDevice.sendControl(current->cb->descriptor,current->cb->length);
-			current = current->next;
-		}
-		return total;
-	} else {
-		return 0;
+	// Check if this is a HID Class Descriptor request
+	if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; }
+	if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; }
+
+	// In a HID Class Descriptor wIndex cointains the interface number
+	if (setup.wIndex != pluggedInterface) { return 0; }
+
+	int total = 0;
+	HIDSubDescriptor* node;
+	USBDevice.packMessages(true);
+	for (node = rootNode; node; node = node->next) {
+		int res = USBDevice.sendControl(node->data, node->length);
+		if (res == -1)
+			return -1;
+		total += res;
 	}
+	USBDevice.packMessages(false);
+	return total;
 }
 
-void HID_::AppendDescriptor(HIDDescriptorListNode *node)
+void HID_::AppendDescriptor(HIDSubDescriptor *node)
 {
-	if (modules_count == 0) {
+	if (!rootNode) {
 		rootNode = node;
 	} else {
-		HIDDescriptorListNode *current = rootNode;
-		while(current->next != NULL) {
+		HIDSubDescriptor *current = rootNode;
+		while (current->next) {
 			current = current->next;
 		}
 		current->next = node;
 	}
-	modules_count++;
-	sizeof_hidReportDescriptor += node->cb->length;
+	descriptorSize += node->length;
 }
 
 void HID_::SendReport(uint8_t id, const void* data, int len)
 {
-	uint8_t p[8];
-	const uint8_t *d = reinterpret_cast<const uint8_t *>(data);
-
+	uint8_t p[64];
 	p[0] = id;
-	for (uint32_t i=0; i<len; i++)
-	{
-		p[i+1] = d[i];
-	}
-	USBDevice.send(HID_ENDPOINT_INT, p, len+1);
+	memcpy(&p[1], data, len);
+	USBDevice.send(pluggedEndpoint, p, len+1);
 }
 
-bool HID_Setup(USBSetup& setup, uint8_t i)
+bool HID_::setup(USBSetup& setup)
 {
-	if (HID_INTERFACE != i) {
+	if (pluggedInterface != setup.wIndex) {
 		return false;
-	} else {
-		uint8_t r = setup.bRequest;
-		uint8_t requestType = setup.bmRequestType;
+	}
 
-		if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)
-		{
-			if (HID_GET_REPORT == r)
-			{
-				//HID_GetReport();
-				return true;
-			}
-			if (HID_GET_PROTOCOL == r)
-			{
-				//Send8(_hid_protocol);	// TODO
-				return true;
-			}
-			if (HID_GET_IDLE == r)
-			{
-				USBDevice.armSend(0, &_hid_idle, 1);
-				return true;
-			}
+	uint8_t request = setup.bRequest;
+	uint8_t requestType = setup.bmRequestType;
+
+	if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
+	{
+		if (request == HID_GET_REPORT) {
+			// TODO: HID_GetReport();
+			return true;
+		}
+		if (request == HID_GET_PROTOCOL) {
+			// TODO: Send8(protocol);
+			return true;
+		}
+		if (request == HID_GET_IDLE) {
+			USBDevice.armSend(0, &idle, 1);
+			return true;
 		}
+	}
 
-		if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType)
+	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)
 		{
-			if (HID_SET_PROTOCOL == r)
-			{
-				_hid_protocol = setup.wValueL;
-				return true;
-			}
-
-			if (HID_SET_IDLE == r)
-			{
-				_hid_idle = setup.wValueH;
-				return false;
-			}
+			//uint8_t reportID = setup.wValueL;
+			//uint16_t length = setup.wLength;
+			//uint8_t data[length];
+			// Make sure to not read more data than USB_EP_SIZE.
+			// You can read multiple times through a loop.
+			// The first byte (may!) contain the reportID on a multreport.
+			//USB_RecvControl(data, length);
 		}
-		return false;
 	}
+
+	return false;
 }
 
-HID_::HID_(void)
+HID_::HID_(void) : PluggableUSBModule(1, 1, epType),
+                   rootNode(NULL), descriptorSize(0),
+                   protocol(1), idle(1)
 {
-	static uint32_t endpointType[1];
-
-	endpointType[0] = USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_IN(0);
-
-	static PUSBCallbacks cb = {
-		.setup = &HID_Setup,
-		.getInterface = &HID_GetInterface,
-		.getDescriptor = &HID_GetDescriptor,
-		.numEndpoints = 1,
-		.numInterfaces = 1,
-		.endpointType = endpointType,
-	};
-
-	static PUSBListNode node(&cb);
-
-	HID_ENDPOINT_INT = PUSB_AddFunction(&node, &HID_INTERFACE);
+	epType[0] = USB_ENDPOINT_TYPE_INTERRUPT | USB_ENDPOINT_IN(0);;
+	PluggableUSB().plug(this);
 }
 
 int HID_::begin(void)
 {
 	return 0;
-}
\ No newline at end of file
+}
+
+#endif /* if defined(USBCON) */
diff --git a/libraries/HID/HID.h b/libraries/HID/HID.h
index 7288552d..e5dfa339 100644
--- a/libraries/HID/HID.h
+++ b/libraries/HID/HID.h
@@ -1,36 +1,34 @@
 /*
-  HID.h
-
   Copyright (c) 2015, Arduino LLC
   Original code (pre-library): Copyright (c) 2011, Peter Barrett
 
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Lesser General Public License for more details.
+  Permission to use, copy, modify, and/or distribute this software for
+  any purpose with or without fee is hereby granted, provided that the
+  above copyright notice and this permission notice appear in all copies.
 
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-*/
+  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+  WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
+  BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
+  OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+  SOFTWARE.
+ */
 
 #ifndef HID_h
 #define HID_h
 
 #include <stdint.h>
 #include <Arduino.h>
+#include "USB/PluggableUSB.h"
 
-#define _USING_HID
+#if defined(USBCON)
 
-//================================================================================
-//================================================================================
-//  HID 'Driver'
+#define _USING_HID
 
+// HID 'Driver'
+// ------------
 #define HID_GET_REPORT        0x01
 #define HID_GET_IDLE          0x02
 #define HID_GET_PROTOCOL      0x03
@@ -42,52 +40,80 @@
 #define HID_REPORT_DESCRIPTOR_TYPE      0x22
 #define HID_PHYSICAL_DESCRIPTOR_TYPE    0x23
 
-typedef struct __attribute__((packed)) {
-  uint8_t length;
-  const void* descriptor;
-} HID_Descriptor;
+// HID subclass HID1.11 Page 8 4.2 Subclass
+#define HID_SUBCLASS_NONE 0
+#define HID_SUBCLASS_BOOT_INTERFACE 1
+
+// HID Keyboard/Mouse bios compatible protocols HID1.11 Page 9 4.3 Protocols
+#define HID_PROTOCOL_NONE 0
+#define HID_PROTOCOL_KEYBOARD 1
+#define HID_PROTOCOL_MOUSE 2
+
+// Normal or bios protocol (Keyboard/Mouse) HID1.11 Page 54 7.2.5 Get_Protocol Request
+// "protocol" variable is used for this purpose.
+#define HID_BOOT_PROTOCOL	0
+#define HID_REPORT_PROTOCOL	1
+
+typedef struct
+{
+  uint8_t len;      // 9
+  uint8_t dtype;    // 0x21
+  uint8_t addr;
+  uint8_t versionL; // 0x101
+  uint8_t versionH; // 0x101
+  uint8_t country;
+  uint8_t desctype; // 0x22 report
+  uint8_t descLenL;
+  uint8_t descLenH;
+} HIDDescDescriptor;
+
+typedef struct 
+{
+  InterfaceDescriptor hid;
+  HIDDescDescriptor   desc;
+  EndpointDescriptor  in;
+} HIDDescriptor;
 
-class HIDDescriptorListNode {
+class HIDSubDescriptor {
 public:
-  HIDDescriptorListNode *next = NULL;
-  const HID_Descriptor * cb;
-  HIDDescriptorListNode(const HID_Descriptor *ncb) {cb = ncb;}
+  HIDSubDescriptor *next = NULL;
+  HIDSubDescriptor(const void *d, const uint16_t l) : data(d), length(l) { }
+
+  const void* data;
+  const uint16_t length;
 };
 
-class HID_
+class HID_ : public PluggableUSBModule
 {
 public:
   HID_(void);
   int begin(void);
   void SendReport(uint8_t id, const void* data, int len);
-  void AppendDescriptor(HIDDescriptorListNode* node);
-};
+  void AppendDescriptor(HIDSubDescriptor* node);
 
-typedef struct
-{
-  uint8_t len;     // 9
-  uint8_t dtype;   // 0x21
-  uint8_t addr;
-  uint8_t  versionL; // 0x101
-  uint8_t  versionH; // 0x101
-  uint8_t  country;
-  uint8_t  desctype; // 0x22 report
-  uint8_t  descLenL;
-  uint8_t  descLenH;
-} HIDDescDescriptor;
+protected:
+  // Implementation of the PluggableUSBModule
+  int getInterface(uint8_t* interfaceCount);
+  int getDescriptor(USBSetup& setup);
+  bool setup(USBSetup& setup);
 
-typedef struct
-{
-  InterfaceDescriptor     hid;
-  HIDDescDescriptor     desc;
-  EndpointDescriptor      in;
-} HIDDescriptor;
+private:
+  uint32_t epType[1];
+
+  HIDSubDescriptor* rootNode;
+  uint16_t descriptorSize;
+
+  uint8_t protocol;
+  uint8_t idle;
+};
 
-#define HID_TX HID_ENDPOINT_INT
+// Replacement for global singleton.
+// This function prevents static-initialization-order-fiasco
+// https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use
+HID_& HID();
 
-#define D_HIDREPORT(_descriptorLength) \
-  { 9, 0x21, 0x1, 0x1, 0, 1, 0x22, _descriptorLength, 0 }
+#define D_HIDREPORT(length) { 9, 0x21, 0x01, 0x01, 0, 1, 0x22, lowByte(length), highByte(length) }
 
-#define WEAK __attribute__ ((weak))
+#endif
 
-#endif
\ No newline at end of file
+#endif
-- 
GitLab