Add a simple HID device

It currently has a hard-coded report of 4 2-byte joystick values
plus 8 buttons.

Also fix a few things I noticed in the CDC code while working on the HID
code.

Change-Id: Icce6c6ad686fdf974924daab6cb40b7b7d0f7996
diff --git a/motors/pistol_grip/BUILD b/motors/pistol_grip/BUILD
index 364801b..b1239c5 100644
--- a/motors/pistol_grip/BUILD
+++ b/motors/pistol_grip/BUILD
@@ -8,9 +8,10 @@
   ],
   deps = [
     '//motors:util',
+    '//motors/core',
     '//motors/usb',
     '//motors/usb:cdc',
-    '//motors/core',
+    '//motors/usb:hid',
   ],
   restricted_to = mcu_cpus,
 )
diff --git a/motors/pistol_grip/drivers_station.cc b/motors/pistol_grip/drivers_station.cc
index 8632a8d..26f171e 100644
--- a/motors/pistol_grip/drivers_station.cc
+++ b/motors/pistol_grip/drivers_station.cc
@@ -2,17 +2,21 @@
 // communicates over CAN with the one in the pistol grip controller.
 
 #include <stdio.h>
+#include <atomic>
 
 #include "motors/core/time.h"
 #include "motors/core/kinetis.h"
 #include "motors/usb/usb.h"
 #include "motors/usb/cdc.h"
+#include "motors/usb/hid.h"
 #include "motors/util.h"
 
 namespace frc971 {
 namespace motors {
 namespace {
 
+::std::atomic<teensy::AcmTty *> global_stdout{nullptr};
+
 void EchoChunks(teensy::AcmTty *tty1) {
   while (true) {
     char buffer[512];
@@ -93,16 +97,41 @@
   }
 }
 
+void MoveJoysticks(teensy::HidFunction *joysticks) {
+  static uint8_t x_axis = 0, y_axis = 97, z_axis = 5, rz_axis = 8;
+  while (true) {
+    {
+      DisableInterrupts disable_interrupts;
+      uint8_t buttons = 0;
+      if (x_axis % 8u) {
+        buttons = 2;
+      }
+      uint8_t report[10] = {x_axis,  0, y_axis, 0,       z_axis,
+                            rz_axis, 0, 0,      buttons, 0};
+      joysticks->UpdateReport(report, 10, disable_interrupts);
+    }
+    delay(1);
+    x_axis += 1;
+    y_axis += 3;
+    z_axis += 5;
+    rz_axis += 8;
+  }
+}
+
 }  // namespace
 
 extern "C" {
+
 void *__stack_chk_guard = (void *)0x67111971;
-int _write(int file, char *ptr, int len) {
-  (void)file;
-  (void)ptr;
-  (void)len;
-  return -1;
+
+int _write(int /*file*/, char *ptr, int len) {
+  teensy::AcmTty *const tty = global_stdout.load(::std::memory_order_acquire);
+  if (tty != nullptr) {
+    return tty->Write(ptr, len);
+  }
+  return 0;
 }
+
 }  // extern "C"
 
 void __stack_chk_fail(void);
@@ -127,13 +156,20 @@
 
   delay(100);
 
-  teensy::UsbDevice usb_device(0, 0x16c0, 0x0490);
+  teensy::UsbDevice usb_device(0, 0x16c0, 0x0492);
   usb_device.SetManufacturer("FRC 971 Spartan Robotics");
   usb_device.SetProduct("Pistol Grip Controller interface");
+  teensy::HidFunction joysticks(&usb_device, 10);
+  // TODO(Brian): Figure out why Windows refuses to recognize the joystick along
+  // with a TTY or two.
+#if 0
   teensy::AcmTty tty1(&usb_device);
+  teensy::AcmTty tty2(&usb_device);
+  global_stdout.store(&tty2, ::std::memory_order_release);
+#endif
   usb_device.Initialize();
 
-  WriteData(&tty1);
+  MoveJoysticks(&joysticks);
 
   return 0;
 }
diff --git a/motors/usb/BUILD b/motors/usb/BUILD
index 950de0a..7d43447 100644
--- a/motors/usb/BUILD
+++ b/motors/usb/BUILD
@@ -102,3 +102,19 @@
     '//aos/testing:googletest',
   ],
 )
+
+cc_library(
+  name = 'hid',
+  visibility = ['//visibility:public'],
+  hdrs = [
+    'hid.h',
+  ],
+  srcs = [
+    'hid.cc',
+  ],
+  deps = [
+    ':usb',
+    '//motors:util',
+  ],
+  restricted_to = mcu_cpus,
+)
diff --git a/motors/usb/cdc.cc b/motors/usb/cdc.cc
index 0b59aef..5136937 100644
--- a/motors/usb/cdc.cc
+++ b/motors/usb/cdc.cc
@@ -167,11 +167,12 @@
   }
 
   {
-    const auto abstract_control_management =
-        CreateDescriptor(cdc_descriptor_subtype::abstract_control_management_length(),
-                         UsbClassDescriptorType::kInterface);
+    const auto abstract_control_management = CreateDescriptor(
+        cdc_descriptor_subtype::abstract_control_management_length(),
+        UsbClassDescriptorType::kInterface);
     abstract_control_management->AddByte(
-        cdc_descriptor_subtype::abstract_control_management());  // bDescriptorSubtype
+        cdc_descriptor_subtype::
+            abstract_control_management());  // bDescriptorSubtype
     // We support:
     //   line_coding and serial_state
     //   send_break
@@ -198,7 +199,7 @@
                                  m_endpoint_address_in());  // bEndpointAddress
     endpoint_descriptor->AddByte(
         m_endpoint_attributes_interrupt());                // bmAttributes
-    endpoint_descriptor->AddUint16(kStatusMaxPacketSize);  // wMaxEndpointSize
+    endpoint_descriptor->AddUint16(kStatusMaxPacketSize);  // wMaxPacketSize
     // Set it to the max because we have nothing to send, so no point using bus
     // bandwidth asking.
     endpoint_descriptor->AddByte(255);  // bInterval
@@ -225,7 +226,7 @@
         endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
     endpoint_descriptor->AddByte(data_rx_endpoint_);  // bEndpointAddress
     endpoint_descriptor->AddByte(m_endpoint_attributes_bulk());  // bmAttributes
-    endpoint_descriptor->AddUint16(kDataMaxPacketSize);  // wMaxEndpointSize
+    endpoint_descriptor->AddUint16(kDataMaxPacketSize);  // wMaxPacketSize
     endpoint_descriptor->AddByte(1);                     // bInterval
   }
 
@@ -235,7 +236,7 @@
     endpoint_descriptor->AddByte(data_tx_endpoint_ |
                                  m_endpoint_address_in());  // bEndpointAddress
     endpoint_descriptor->AddByte(m_endpoint_attributes_bulk());  // bmAttributes
-    endpoint_descriptor->AddUint16(kDataMaxPacketSize);  // wMaxEndpointSize
+    endpoint_descriptor->AddUint16(kDataMaxPacketSize);  // wMaxPacketSize
     endpoint_descriptor->AddByte(1);                     // bInterval
   }
 }
@@ -365,8 +366,8 @@
     device()->ConfigureEndpointFor(status_endpoint_, false, true, true);
   } else if (endpoint == data_tx_endpoint_) {
     device()->ConfigureEndpointFor(data_tx_endpoint_, false, true, true);
-    next_tx_toggle_ = Data01::kData0;
     DisableInterrupts disable_interrupts;
+    next_tx_toggle_ = Data01::kData0;
     EnqueueTxData(disable_interrupts);
   } else if (endpoint == data_rx_endpoint_) {
     device()->ConfigureEndpointFor(data_rx_endpoint_, true, false, true);
diff --git a/motors/usb/cdc.h b/motors/usb/cdc.h
index f7c7437..4f5aa33 100644
--- a/motors/usb/cdc.h
+++ b/motors/usb/cdc.h
@@ -63,9 +63,6 @@
   // for it.
   static constexpr uint16_t kStatusMaxPacketSize = 1;
 
-  ::std::array<::std::array<char, kDataMaxPacketSize>, 2> tx_buffers_,
-      rx_buffers_;
-
   void Initialize() override;
 
   SetupResponse HandleEndpoint0SetupPacket(
@@ -99,6 +96,9 @@
 
   void EnqueueTxData(const DisableInterrupts &);
 
+  ::std::array<::std::array<char, kDataMaxPacketSize>, 2> tx_buffers_,
+      rx_buffers_;
+
   // In theory, we could sent notifications over this about things like the line
   // state, but we don't have anything to report so we pretty much just ignore
   // this.
diff --git a/motors/usb/hid.cc b/motors/usb/hid.cc
new file mode 100644
index 0000000..24ffe24
--- /dev/null
+++ b/motors/usb/hid.cc
@@ -0,0 +1,247 @@
+#include "motors/usb/hid.h"
+
+namespace frc971 {
+namespace teensy {
+namespace {
+
+constexpr uint8_t hid_class() { return 0x03; }
+
+namespace hid_class_requests {
+constexpr uint8_t get_report() { return 0x01; }
+constexpr uint8_t get_idle() { return 0x02; }
+constexpr uint8_t get_protocol() { return 0x03; }
+constexpr uint8_t set_report() { return 0x09; }
+constexpr uint8_t set_idle() { return 0x0a; }
+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, 0x04,        //     Report Count (4),
+    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, 0x35,        //     Usage (Rz),
+    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 (1),
+    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() {
+  interface_ = AddInterface();
+  in_endpoint_ = AddEndpoint();
+
+  {
+    const auto iad_descriptor = CreateDescriptor(
+        iad_descriptor_length(), UsbDescriptorType::kInterfaceAssociation);
+    iad_descriptor->AddByte(interface_);   // bFirstInterface
+    iad_descriptor->AddByte(1);            // bInterfaceCount
+    iad_descriptor->AddByte(hid_class());  // bFunctionClass
+    iad_descriptor->AddByte(0);            // bFunctionSubClass
+    iad_descriptor->AddByte(0);            // bFunctionProtocol
+    iad_descriptor->AddByte(device()->AddString("HidIad"));  // iFunction
+  }
+
+  {
+    const auto interface_descriptor = CreateDescriptor(
+        interface_descriptor_length(), UsbDescriptorType::kInterface);
+    interface_descriptor->AddByte(interface_);   // bInterfaceNumber
+    interface_descriptor->AddByte(0);            // bAlternateSetting
+    interface_descriptor->AddByte(1);            // bNumEndpoints
+    interface_descriptor->AddByte(hid_class());  // bInterfaceClass
+    interface_descriptor->AddByte(0);            // bInterfaceSubClass
+    interface_descriptor->AddByte(0);            // bInterfaceProtocol
+    interface_descriptor->AddByte(device()->AddString("Hid"));  // iInterface
+  }
+
+  AddPremadeDescriptor(kHidDescriptor, sizeof(kHidDescriptor));
+
+  {
+    const auto endpoint_descriptor = CreateDescriptor(
+        endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
+    endpoint_descriptor->AddByte(in_endpoint_ |
+                                 m_endpoint_address_in());  // bEndpointAddress
+    endpoint_descriptor->AddByte(
+        m_endpoint_attributes_interrupt());                  // bmAttributes
+    endpoint_descriptor->AddUint16(in_endpoint_max_size());  // wMaxPacketSize
+    endpoint_descriptor->AddByte(1);                         // bInterval
+  }
+}
+
+UsbFunction::SetupResponse HidFunction::HandleEndpoint0SetupPacket(
+    const UsbDevice::SetupPacket &setup_packet) {
+  if (G_SETUP_REQUEST_TYPE_TYPE(setup_packet.request_type) !=
+      SetupRequestType::kClass) {
+    return SetupResponse::kIgnored;
+  }
+  if (G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type) !=
+      standard_setup_recipients::kInterface) {
+    return SetupResponse::kIgnored;
+  }
+  if (setup_packet.index != interface_) {
+    return SetupResponse::kIgnored;
+  }
+  const bool in = setup_packet.request_type & M_SETUP_REQUEST_TYPE_IN;
+  switch (setup_packet.request) {
+    case hid_class_requests::get_report():
+      if (!in) {
+        return SetupResponse::kStall;
+      }
+      // If it's not requesting the only Input report, no idea what the host
+      // wants so stall.
+      if (setup_packet.value != 0x0100) {
+        return SetupResponse::kStall;
+      }
+      {
+        DisableInterrupts disable_interrupts;
+        memcpy(get_report_response_buffer_.data(),
+               report_tx_buffer_being_sent(disable_interrupts), kMaxReportSize);
+      }
+      device()->QueueEndpoint0Data(
+          reinterpret_cast<const char *>(get_report_response_buffer_.data()),
+          ::std::min<uint16_t>(setup_packet.length, report_max_size_));
+      return SetupResponse::kHandled;
+
+    case hid_class_requests::set_idle():
+      // Minimum implementation to make the host stack happy.
+      if (in) {
+        return SetupResponse::kStall;
+      }
+      device()->SendEmptyEndpoint0Packet();
+      return SetupResponse::kHandled;
+
+      // TODO(Brian): Should we actually implement the idle stuff?
+
+    default:
+      return SetupResponse::kStall;
+  }
+}
+
+UsbFunction::SetupResponse HidFunction::HandleGetDescriptor(
+    const UsbDevice::SetupPacket &setup_packet) {
+  const uint8_t recipient =
+      G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type);
+  if (recipient != standard_setup_recipients::kInterface) {
+    return SetupResponse::kIgnored;
+  }
+
+  const uint8_t descriptor_type = (setup_packet.value >> 8) & 0xFF;
+  if (G_DESCRIPTOR_TYPE_TYPE(descriptor_type) !=
+      standard_descriptor_type_types::kClass) {
+    return SetupResponse::kIgnored;
+  }
+  if (setup_packet.index != interface_) {
+    return SetupResponse::kIgnored;
+  }
+
+  const uint8_t descriptor_index = setup_packet.value & 0xFF;
+  switch (descriptor_type) {
+    case static_cast<uint8_t>(UsbClassDescriptorType::kHidHid):
+      if (descriptor_index != 0) {
+        return SetupResponse::kStall;
+      }
+      device()->QueueEndpoint0Data(
+          reinterpret_cast<const char *>(kHidDescriptor),
+          ::std::min<int>(setup_packet.length, sizeof(kHidDescriptor)));
+      return SetupResponse::kHandled;
+
+    case static_cast<uint8_t>(UsbClassDescriptorType::kHidReport):
+      if (descriptor_index != 0) {
+        return SetupResponse::kStall;
+      }
+      device()->QueueEndpoint0Data(
+          reinterpret_cast<const char *>(kReportDescriptor),
+          ::std::min<int>(setup_packet.length, sizeof(kReportDescriptor)));
+      return SetupResponse::kHandled;
+
+    case static_cast<uint8_t>(UsbClassDescriptorType::kHidPhysical):
+      static constexpr char kNoPhysicalDescriptors[] = {0, 0, 0};
+      device()->QueueEndpoint0Data(
+          kNoPhysicalDescriptors,
+          ::std::min<int>(setup_packet.length, sizeof(kNoPhysicalDescriptors)));
+      return SetupResponse::kHandled;
+  }
+  return SetupResponse::kStall;
+}
+
+void HidFunction::HandleInFinished(int endpoint, BdtEntry * /*bdt_entry*/,
+                                   EvenOdd odd) {
+  if (endpoint == in_endpoint_) {
+    DisableInterrupts disable_interrupts;
+    if (odd != BufferStateToEmpty(tx_state_)) {
+      __builtin_trap();
+    }
+
+    // Copy the current one into the just-sent buffer.
+    memcpy(report_tx_buffer_being_sent(disable_interrupts),
+           report_tx_buffer_to_fill(disable_interrupts), kMaxReportSize);
+
+    dma_memory_barrier();
+    device()->SetBdtEntry(
+        in_endpoint_, Direction::kTx, BufferStateToFill(tx_state_),
+        {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(report_max_size_) |
+             static_cast<uint32_t>(next_tx_toggle_),
+         report_tx_buffer_to_fill(disable_interrupts)});
+
+    // Advance the state to indicate we've swapped buffers.
+    tx_state_ = BufferStateAfterFill(BufferStateAfterEmpty(tx_state_));
+    next_tx_toggle_ = Data01Inverse(next_tx_toggle_);
+  }
+}
+
+void HidFunction::HandleConfigured(int endpoint) {
+  if (endpoint == in_endpoint_) {
+    device()->ConfigureEndpointFor(in_endpoint_, false, true, true);
+    DisableInterrupts disable_interrupts;
+    next_tx_toggle_ = Data01::kData0;
+
+    EvenOdd to_fill;
+    if (BufferStateHasFull(tx_state_)) {
+      to_fill = BufferStateToEmpty(tx_state_);
+    } else {
+      to_fill = BufferStateToFill(tx_state_);
+      tx_state_ = BufferStateAfterFill(tx_state_);
+    }
+
+    dma_memory_barrier();
+    device()->SetBdtEntry(
+        in_endpoint_, Direction::kTx, to_fill,
+        {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(report_max_size_) |
+             static_cast<uint32_t>(next_tx_toggle_),
+         report_tx_buffer_to_fill(disable_interrupts)});
+    next_tx_toggle_ = Data01Inverse(next_tx_toggle_);
+  }
+}
+
+}  // namespace teensy
+}  // namespace frc971
diff --git a/motors/usb/hid.h b/motors/usb/hid.h
new file mode 100644
index 0000000..4b2dae4
--- /dev/null
+++ b/motors/usb/hid.h
@@ -0,0 +1,94 @@
+#ifndef MOTORS_USB_HID_H_
+#define MOTORS_USB_HID_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <array>
+
+#include "motors/usb/usb.h"
+#include "motors/util.h"
+
+namespace frc971 {
+namespace teensy {
+
+// Implements an HID class device.
+// TODO(Brian): Make this way more generic.
+class HidFunction final : public UsbFunction {
+ public:
+  HidFunction(UsbDevice *device, int report_max_size)
+      : UsbFunction(device), report_max_size_(report_max_size) {
+    if (report_max_size_ > kMaxReportSize) {
+      __builtin_trap();
+    }
+  }
+  ~HidFunction() override = default;
+
+  void UpdateReport(const void *data, int length,
+                    const DisableInterrupts &disable_interrupts) {
+    memcpy(report_tx_buffer_to_fill(disable_interrupts), data, length);
+  }
+
+ private:
+  // Choose 64 for now so it always fits into a single packet.
+  static constexpr int kMaxReportSize = 64;
+
+  uint8_t *report_tx_buffer_for(EvenOdd odd) {
+    return report_tx_buffers_[EvenOddIndex(odd)].data();
+  }
+  uint8_t *report_tx_buffer_being_sent(const DisableInterrupts &) {
+    return report_tx_buffer_for(BufferStateToEmpty(tx_state_));
+  }
+  uint8_t *report_tx_buffer_to_fill(const DisableInterrupts &) {
+    return report_tx_buffer_for(BufferStateToFill(tx_state_));
+  }
+
+  int in_endpoint_max_size() const { return report_max_size_; }
+
+  void Initialize() override;
+
+  SetupResponse HandleEndpoint0SetupPacket(
+      const UsbDevice::SetupPacket &setup_packet) override;
+  SetupResponse HandleGetDescriptor(
+      const UsbDevice::SetupPacket &setup_packet) override;
+
+  void HandleOutFinished(int /*endpoint*/, BdtEntry * /*bdt_entry*/) override {
+    // We don't support any out endpoints.
+  }
+
+  void HandleInFinished(int endpoint, BdtEntry *bdt_entry,
+                        EvenOdd odd) override;
+
+  void HandleConfigured(int endpoint) override;
+  void HandleReset() override {
+    tx_state_ = EndpointBufferState::kBothEmptyEvenFirst;
+    device()->SetBdtEntry(in_endpoint_, Direction::kTx, EvenOdd::kEven,
+                          {0, nullptr});
+    device()->SetBdtEntry(in_endpoint_, Direction::kTx, EvenOdd::kOdd,
+                          {0, nullptr});
+    {
+      DisableInterrupts disable_interrupts;
+      memset(report_tx_buffers_[0].data(), 0, kMaxReportSize);
+      memset(report_tx_buffers_[1].data(), 0, kMaxReportSize);
+    }
+  }
+
+  ::std::array<::std::array<uint8_t, kMaxReportSize>, 2> report_tx_buffers_;
+  ::std::array<uint8_t, kMaxReportSize> get_report_response_buffer_;
+
+  // This is only manipulated with interrupts disabled.
+  EndpointBufferState tx_state_;
+  Data01 next_tx_toggle_;
+
+  // Our interface number.
+  int interface_;
+
+  // The IN endpoint we send reports on.
+  int in_endpoint_;
+
+  const int report_max_size_;
+};
+
+}  // namespace teensy
+}  // namespace frc971
+
+#endif  // MOTORS_USB_HID_H_
diff --git a/motors/usb/usb.cc b/motors/usb/usb.cc
index 0a05fad..fb7fe9f 100644
--- a/motors/usb/usb.cc
+++ b/motors/usb/usb.cc
@@ -641,10 +641,30 @@
           break;
 
         case standard_setup_requests::kGetDescriptor:
-          if (!in || recipient != standard_setup_recipients::kDevice) {
+          if (!in) {
             break;
           }
+
           const uint8_t descriptor_type_byte = (setup_packet.value >> 8) & 0xFF;
+          if (G_DESCRIPTOR_TYPE_TYPE(descriptor_type_byte) !=
+              standard_descriptor_type_types::kStandard) {
+            for (UsbFunction *function : functions_) {
+              switch (function->HandleGetDescriptor(setup_packet)) {
+                case SetupResponse::kIgnored:
+                  continue;
+                case SetupResponse::kHandled:
+                  return;
+                case SetupResponse::kStall:
+                  break;
+              }
+              break;
+            }
+            break;
+          }
+
+          if (recipient != standard_setup_recipients::kDevice) {
+            break;
+          }
           if (descriptor_type_byte < kUsbDescriptorTypeMin ||
               descriptor_type_byte > kUsbDescriptorTypeMax) {
             break;
@@ -674,7 +694,8 @@
               return;
 
             case UsbDescriptorType::kString:
-              if (descriptor_index != 0 && setup_packet.index != english_us_code()) {
+              if (descriptor_index != 0 &&
+                  setup_packet.index != english_us_code()) {
                 break;
               }
               if (descriptor_index >= strings_.size()) {
diff --git a/motors/usb/usb.h b/motors/usb/usb.h
index 8b2cc67..4db29dc 100644
--- a/motors/usb/usb.h
+++ b/motors/usb/usb.h
@@ -2,6 +2,7 @@
 #define MOTORS_USB_USB_H_
 
 #include <assert.h>
+#include <string.h>
 #include <string>
 #include <vector>
 #include <memory>
@@ -76,6 +77,10 @@
   kString = 0x23,
   kInterface = 0x24,
   kEndpoint = 0x25,
+
+  kHidHid = 0x21,
+  kHidReport = 0x22,
+  kHidPhysical = 0x23,
 };
 
 // The names of the setup request types from the standard.
@@ -101,6 +106,16 @@
 constexpr int kOther = 3;
 }  // namespace standard_setup_recipients
 
+// The HID class specification says this. Can't find any mention in the main
+// standard.
+#define G_DESCRIPTOR_TYPE_TYPE(descriptor_type) \
+  ((descriptor_type) >> 5 & UINT8_C(3))
+namespace standard_descriptor_type_types {
+constexpr int kStandard = 0;
+constexpr int kClass = 1;
+constexpr int kVendor = 2;
+}  // namespace standard_descriptor_type_types
+
 class UsbFunction;
 
 // Allows building up a list of descriptors. This supports a much nicer API than
@@ -185,6 +200,13 @@
     return CreateDescriptor(length, static_cast<uint8_t>(descriptor_type));
   }
 
+  void AddPremadeDescriptor(const uint8_t *data, int length) {
+    const int start_index = data_.size();
+    const int end_index = start_index + length;
+    data_.resize(end_index);
+    memcpy(&data_[start_index], data, length);
+  }
+
   void CheckFinished() const { assert(open_descriptors_ == 0); }
 
   int CurrentSize() const { return data_.size(); }
@@ -469,6 +491,9 @@
     return device_->config_descriptor_list_.CreateDescriptor(length,
                                                              descriptor_type);
   }
+  void AddPremadeDescriptor(const uint8_t *data, int length) {
+    device_->config_descriptor_list_.AddPremadeDescriptor(data, length);
+  }
 
   UsbDevice *device() const { return device_; }
 
@@ -485,6 +510,11 @@
     return SetupResponse::kIgnored;
   }
 
+  virtual SetupResponse HandleGetDescriptor(
+      const UsbDevice::SetupPacket & /*setup_packet*/) {
+    return SetupResponse::kIgnored;
+  }
+
   virtual void HandleOutFinished(int endpoint, BdtEntry *bdt_entry) = 0;
   virtual void HandleInFinished(int endpoint, BdtEntry *bdt_entry,
                                 EvenOdd odd) = 0;