Add support for varying HID descriptors

Change-Id: I8ee01045d2b4bcc98bedb615f0af1b26ebbd66f7
diff --git a/motors/pistol_grip/drivers_station.cc b/motors/pistol_grip/drivers_station.cc
index d58d3d3..155440a 100644
--- a/motors/pistol_grip/drivers_station.cc
+++ b/motors/pistol_grip/drivers_station.cc
@@ -198,6 +198,35 @@
   }
 }
 
+// The HID report descriptor we use.
+constexpr char kReportDescriptor[] = {
+    0x05, 0x01,        // Usage Page (Generic Desktop),
+    0x09, 0x04,        // Usage (Joystick),
+    0xA1, 0x01,        // Collection (Application),
+    0x75, 0x10,        //     Report Size (16),
+    0x95, 0x06,        //     Report Count (6),
+    0x15, 0x00,        //     Logical Minimum (0),
+    0x26, 0xFF, 0xFF,  //     Logical Maximum (65535),
+    0x35, 0x00,        //     Physical Minimum (0),
+    0x46, 0xFF, 0xFF,  //     Physical Maximum (65535),
+    0x09, 0x30,        //     Usage (X),
+    0x09, 0x31,        //     Usage (Y),
+    0x09, 0x32,        //     Usage (Z),
+    0x09, 0x33,        //     Usage (Rz),
+    0x09, 0x34,        //     Usage (?),
+    0x09, 0x35,        //     Usage (?),
+    0x81, 0x02,        //     Input (Variable),
+    0x75, 0x01,        //     Report Size (1),
+    0x95, 0x10,        //     Report Count (16),
+    0x25, 0x01,        //     Logical Maximum (1),
+    0x45, 0x01,        //     Physical Maximum (1),
+    0x05, 0x09,        //     Usage Page (Button),
+    0x19, 0x01,        //     Usage Minimum (01),
+    0x29, 0x10,        //     Usage Maximum (16),
+    0x81, 0x02,        //     Input (Variable),
+    0xC0               // End Collection
+};
+
 }  // namespace
 
 extern "C" {
@@ -244,7 +273,11 @@
   usb_device.SetManufacturer("FRC 971 Spartan Robotics");
   usb_device.SetProduct("Pistol Grip Controller interface");
   teensy::HidFunction throttle_joystick(&usb_device, 14);
+  throttle_joystick.set_report_descriptor(
+      ::std::string(kReportDescriptor, sizeof(kReportDescriptor)));
   teensy::HidFunction wheel_joystick(&usb_device, 14);
+  wheel_joystick.set_report_descriptor(
+      ::std::string(kReportDescriptor, sizeof(kReportDescriptor)));
   teensy::AcmTty tty1(&usb_device);
   teensy::AcmTty tty2(&usb_device);
   teensy::InterruptOut interrupt_out(&usb_device, "JoystickForce");
diff --git a/motors/usb/hid.cc b/motors/usb/hid.cc
index f42930b..d885e75 100644
--- a/motors/usb/hid.cc
+++ b/motors/usb/hid.cc
@@ -15,48 +15,6 @@
 constexpr uint8_t set_protcol() { return 0x0b; }
 }  // namespace hid_class_requests
 
-// The hard-coded HID report descriptor.
-uint8_t kReportDescriptor[] = {
-    0x05, 0x01,        // Usage Page (Generic Desktop),
-    0x09, 0x04,        // Usage (Joystick),
-    0xA1, 0x01,        // Collection (Application),
-    0x75, 0x10,        //     Report Size (16),
-    0x95, 0x06,        //     Report Count (6),
-    0x15, 0x00,        //     Logical Minimum (0),
-    0x26, 0xFF, 0xFF,  //     Logical Maximum (65535),
-    0x35, 0x00,        //     Physical Minimum (0),
-    0x46, 0xFF, 0xFF,  //     Physical Maximum (65535),
-    0x09, 0x30,        //     Usage (X),
-    0x09, 0x31,        //     Usage (Y),
-    0x09, 0x32,        //     Usage (Z),
-    0x09, 0x33,        //     Usage (Rz),
-    0x09, 0x34,        //     Usage (?),
-    0x09, 0x35,        //     Usage (?),
-    0x81, 0x02,        //     Input (Variable),
-    0x75, 0x01,        //     Report Size (1),
-    0x95, 0x10,        //     Report Count (16),
-    0x25, 0x01,        //     Logical Maximum (1),
-    0x45, 0x01,        //     Physical Maximum (1),
-    0x05, 0x09,        //     Usage Page (Button),
-    0x19, 0x01,        //     Usage Minimum (01),
-    0x29, 0x10,        //     Usage Maximum (16),
-    0x81, 0x02,        //     Input (Variable),
-    0xC0               // End Collection
-};
-
-// The hard-coded HID descriptor.
-uint8_t kHidDescriptor[] = {
-    9,                                                      // bLength
-    static_cast<uint8_t>(UsbClassDescriptorType::kHidHid),  // bDescriptorType
-    0x10, 0x01,                                             // bcdHID
-    0,                                                      // bCountryCode
-    1,                                                      // bNumDescriptors
-    static_cast<uint8_t>(
-        UsbClassDescriptorType::kHidReport),  // bDescriptorType
-    sizeof(kReportDescriptor),  // wDescriptorLength
-    0,
-};
-
 }  // namespace
 
 void HidFunction::Initialize() {
@@ -82,7 +40,17 @@
     interface_descriptor->AddByte(device()->AddString("Hid"));  // iInterface
   }
 
-  AddPremadeDescriptor(kHidDescriptor, sizeof(kHidDescriptor));
+  {
+    const auto hid_descriptor = hid_descriptor_list_.CreateDescriptor(
+        9, UsbClassDescriptorType::kHidHid);
+    hid_descriptor->AddUint16(0x0110);  // bcdHID
+    hid_descriptor->AddByte(0);         // bCountryCode
+    hid_descriptor->AddByte(1);         // bNumDescriptors
+    hid_descriptor->AddByte(static_cast<uint8_t>(
+        UsbClassDescriptorType::kHidReport));              // bDescriptorType
+    hid_descriptor->AddUint16(report_descriptor_.size());  // wDescriptorLength
+  }
+  AddPremadeDescriptor(hid_descriptor_list_);
 
   {
     const auto endpoint_descriptor = CreateDescriptor(
@@ -169,8 +137,9 @@
         return SetupResponse::kStall;
       }
       device()->QueueEndpoint0Data(
-          reinterpret_cast<const char *>(kHidDescriptor),
-          ::std::min<int>(setup_packet.length, sizeof(kHidDescriptor)));
+          hid_descriptor_list_.GetData(),
+          ::std::min<int>(setup_packet.length,
+                          hid_descriptor_list_.CurrentSize()));
       return SetupResponse::kHandled;
 
     case static_cast<uint8_t>(UsbClassDescriptorType::kHidReport):
@@ -178,8 +147,8 @@
         return SetupResponse::kStall;
       }
       device()->QueueEndpoint0Data(
-          reinterpret_cast<const char *>(kReportDescriptor),
-          ::std::min<int>(setup_packet.length, sizeof(kReportDescriptor)));
+          report_descriptor_.data(),
+          ::std::min<int>(setup_packet.length, report_descriptor_.size()));
       return SetupResponse::kHandled;
 
     case static_cast<uint8_t>(UsbClassDescriptorType::kHidPhysical):
diff --git a/motors/usb/hid.h b/motors/usb/hid.h
index 37aa86b..019231c 100644
--- a/motors/usb/hid.h
+++ b/motors/usb/hid.h
@@ -23,6 +23,13 @@
   }
   ~HidFunction() override = default;
 
+  // Sets the report descriptor. Must be called at least once.
+  //
+  // May only be called during setup.
+  void set_report_descriptor(const ::std::string &report_descriptor) {
+    report_descriptor_ = report_descriptor;
+  }
+
   void UpdateReport(const void *data, int length,
                     const DisableInterrupts &disable_interrupts) {
     memcpy(report_tx_buffer_to_fill(disable_interrupts), data, length);
@@ -82,6 +89,9 @@
   int in_endpoint_;
 
   const int report_max_size_;
+
+  ::std::string report_descriptor_;
+  UsbDescriptorList hid_descriptor_list_;
 };
 
 }  // namespace teensy
diff --git a/motors/usb/usb.h b/motors/usb/usb.h
index 8fd2fb3..299df59 100644
--- a/motors/usb/usb.h
+++ b/motors/usb/usb.h
@@ -214,10 +214,22 @@
     memcpy(&data_[start_index], data, length);
   }
 
+  void AddPremadeDescriptor(const UsbDescriptorList &other_list) {
+    other_list.CheckFinished();
+    AddPremadeDescriptor(
+        reinterpret_cast<const uint8_t *>(other_list.data_.data()),
+        other_list.data_.size());
+  }
+
   void CheckFinished() const { assert(open_descriptors_ == 0); }
 
   int CurrentSize() const { return data_.size(); }
 
+  const char *GetData() const {
+    CheckFinished();
+    return data_.data();
+  }
+
  private:
   ::std::unique_ptr<Descriptor> CreateDescriptor(uint8_t length,
                                                  uint8_t descriptor_type) {
@@ -504,6 +516,9 @@
   void AddPremadeDescriptor(const uint8_t *data, int length) {
     device_->config_descriptor_list_.AddPremadeDescriptor(data, length);
   }
+  void AddPremadeDescriptor(const UsbDescriptorList &other_list) {
+    device_->config_descriptor_list_.AddPremadeDescriptor(other_list);
+  }
 
   UsbDevice *device() const { return device_; }