Add a working CDC TTY USB device

I'm pretty sure it reliably moves data in both directions, but it has a
lot of TODOs left wrt error handling.

Change-Id: I1d96a63eea7bdc08f6ec78c868c4a1ccbfbda600
diff --git a/motors/usb/cdc.h b/motors/usb/cdc.h
new file mode 100644
index 0000000..f7c7437
--- /dev/null
+++ b/motors/usb/cdc.h
@@ -0,0 +1,133 @@
+#ifndef MOTORS_USB_CDC_H_
+#define MOTORS_USB_CDC_H_
+
+#include <array>
+
+#include "motors/usb/usb.h"
+#include "motors/usb/queue.h"
+#include "motors/util.h"
+
+// CDC (Communications Device Class) is a "class standard" which takes 30 pages
+// to explain that a communications device has communications and data, and they
+// can come via separate interfaces or the same ones, and the data can be in a
+// lot of formats. The only commonality across the "class" seems to be some
+// constants and a few descriptors.
+
+namespace frc971 {
+namespace teensy {
+
+struct CdcLineCoding {
+  static constexpr uint8_t stop_bits_1() { return 0; }
+  static constexpr uint8_t stop_bits_1_5() { return 1; }
+  static constexpr uint8_t stop_bits_2() { return 2; }
+
+  static constexpr uint8_t parity_none() { return 0; }
+  static constexpr uint8_t parity_odd() { return 1; }
+  static constexpr uint8_t parity_even() { return 2; }
+  static constexpr uint8_t parity_mark() { return 3; }
+  static constexpr uint8_t parity_space() { return 4; }
+
+  // The baud rate in bits/second.
+  uint32_t rate;  // dwDTERate
+
+  uint8_t stop_bits;  // bCharFormat
+
+  uint8_t parity;  // bParityType
+
+  // 5, 6, 7, 8, or 16 according to the standard.
+  uint8_t data_bits;  // bDataBits
+} __attribute__((packed));
+static_assert(sizeof(CdcLineCoding) == 7, "wrong size");
+
+// Implements a pretty dumb serial port via CDC's ACM (Abstract Control
+// Management) and Call Management functions.
+class AcmTty final : public UsbFunction {
+ public:
+  AcmTty(UsbDevice *device) : UsbFunction(device) {}
+  ~AcmTty() override = default;
+
+  size_t Read(void *buffer, size_t buffer_size);
+  size_t Write(const void *buffer, size_t buffer_size);
+
+ private:
+  enum class NextEndpoint0Out {
+    kNone,
+    kLineCoding,
+  };
+
+  // We're going with the largest allowable sizes for full speed devices for
+  // the data endpoints to maximize throughput.
+  static constexpr uint16_t kDataMaxPacketSize = 64;
+
+  // We have no information to send here, so no point allocating bus bandwidth
+  // for it.
+  static constexpr uint16_t kStatusMaxPacketSize = 1;
+
+  ::std::array<::std::array<char, kDataMaxPacketSize>, 2> tx_buffers_,
+      rx_buffers_;
+
+  void Initialize() override;
+
+  SetupResponse HandleEndpoint0SetupPacket(
+      const UsbDevice::SetupPacket &setup_packet) override;
+  SetupResponse HandleEndpoint0OutPacket(void *data, int data_length) override;
+  void HandleOutFinished(int endpoint, BdtEntry *bdt_entry) override;
+  void HandleInFinished(int endpoint, BdtEntry *bdt_entry,
+                        EvenOdd odd) override;
+  void HandleConfigured(int endpoint) override;
+  void HandleReset() override {
+    // TODO(Brian): Handle data already in the buffers correctly.
+    DisableInterrupts disable_interrupts;
+    tx_state_ = EndpointBufferState::kBothEmptyEvenFirst;
+    device()->SetBdtEntry(status_endpoint_, Direction::kTx, EvenOdd::kEven,
+                          {0, nullptr});
+    device()->SetBdtEntry(status_endpoint_, Direction::kTx, EvenOdd::kOdd,
+                          {0, nullptr});
+    device()->SetBdtEntry(data_tx_endpoint_, Direction::kTx, EvenOdd::kEven,
+                          {0, nullptr});
+    device()->SetBdtEntry(data_tx_endpoint_, Direction::kTx, EvenOdd::kOdd,
+                          {0, nullptr});
+    device()->SetBdtEntry(data_rx_endpoint_, Direction::kRx, EvenOdd::kEven,
+                          {0, nullptr});
+    device()->SetBdtEntry(data_rx_endpoint_, Direction::kRx, EvenOdd::kOdd,
+                          {0, nullptr});
+  }
+
+  char *tx_buffer_for(EvenOdd odd) {
+    return tx_buffers_[EvenOddIndex(odd)].data();
+  }
+
+  void EnqueueTxData(const DisableInterrupts &);
+
+  // 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.
+  int status_interface_;
+  int data_interface_;
+  int status_endpoint_, data_tx_endpoint_, data_rx_endpoint_;
+
+  Queue tx_queue_{1024}, rx_queue_{1024};
+
+  NextEndpoint0Out next_endpoint0_out_ = NextEndpoint0Out::kNone;
+
+  // This is only manipulated with interrupts disabled.
+  EndpointBufferState tx_state_;
+  Data01 next_tx_toggle_;
+
+  // These are BdtEntries which we're holding into without releasing back to the
+  // hardware so that we won't receive any more data until we have space for it.
+  // They are only manipulated with interrupts disabled.
+  BdtEntry *first_rx_held_ = nullptr, *second_rx_held_ = nullptr;
+  Data01 next_rx_toggle_;
+
+  CdcLineCoding line_coding_{0, CdcLineCoding::stop_bits_1(),
+                             CdcLineCoding::parity_none(), 8};
+  CdcLineCoding line_coding_to_send_;
+
+  uint16_t control_line_state_ = 0;
+};
+
+}  // namespace teensy
+}  // namespace frc971
+
+#endif  // MOTORS_USB_CDC_H_