mockband

Firmware for Wii Rock Band guitar and drum kit
git clone https://git.woozle.org/neale/mockband.git

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}