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/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_