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.cc b/motors/usb/cdc.cc
new file mode 100644
index 0000000..b082750
--- /dev/null
+++ b/motors/usb/cdc.cc
@@ -0,0 +1,426 @@
+#include "motors/usb/cdc.h"
+
+#include <string.h>
+
+#define CHECK(c)                             \
+  do {                                       \
+    if (!(c)) {                              \
+      while (true) {                         \
+        for (int i = 0; i < 10000000; ++i) { \
+          GPIOC_PSOR = 1 << 5;               \
+        }                                    \
+        for (int i = 0; i < 10000000; ++i) { \
+          GPIOC_PCOR = 1 << 5;               \
+        }                                    \
+      }                                      \
+    }                                        \
+  } while (false)
+
+namespace frc971 {
+namespace teensy {
+namespace {
+
+// Aka the Communications Device Class code, the Communications Class code,
+// and the Communications Interface Class code.
+constexpr uint8_t communications_class() { return 0x02; }
+constexpr uint8_t data_interface_class() { return 0x0A; }
+
+namespace cdc_descriptor_subtype {
+constexpr uint8_t header() { return 0x00; }
+constexpr uint8_t header_length() { return 5; }
+constexpr uint8_t call_management() { return 0x01; }
+constexpr uint8_t call_management_length() { return 5; }
+constexpr uint8_t abstract_control_management() { return 0x02; }
+constexpr uint8_t abstract_control_management_length() { return 4; }
+constexpr uint8_t direct_line_management() { return 0x03; }
+constexpr uint8_t telephone_ringer() { return 0x04; }
+constexpr uint8_t telephone_call_etc() { return 0x05; }
+// Can't just call this "union" because that's a keyword...
+constexpr uint8_t union_function() { return 0x06; }
+constexpr uint8_t union_length(int number_subordinates) {
+  return 4 + number_subordinates;
+}
+constexpr uint8_t country_selection() { return 0x07; }
+constexpr uint8_t telephone_operational_modes() { return 0x08; }
+constexpr uint8_t usb_terminal() { return 0x09; }
+constexpr uint8_t network_channel() { return 0x0A; }
+constexpr uint8_t protocol_unit() { return 0x0B; }
+constexpr uint8_t extension_unit() { return 0x0C; }
+constexpr uint8_t multichannel_management() { return 0x0D; }
+constexpr uint8_t capi_control() { return 0x0E; }
+constexpr uint8_t ethernet_networking() { return 0x0F; }
+constexpr uint8_t atm_networking() { return 0x10; }
+constexpr uint8_t wireless_handset_control() { return 0x11; }
+constexpr uint8_t mobile_direct_line() { return 0x12; }
+constexpr uint8_t mdlm_detail() { return 0x13; }
+constexpr uint8_t device_management() { return 0x14; }
+constexpr uint8_t obex() { return 0x15; }
+constexpr uint8_t command_set() { return 0x16; }
+constexpr uint8_t command_set_detail() { return 0x17; }
+constexpr uint8_t telephone_control() { return 0x18; }
+constexpr uint8_t obex_service() { return 0x19; }
+constexpr uint8_t ncm() { return 0x1A; }
+}  // namespace cdc_descriptor_subtype
+
+namespace communications_subclass {
+constexpr uint8_t direct_line_control_model() { return 0x01; }
+constexpr uint8_t abstract_control_model() { return 0x02; }
+constexpr uint8_t telephone_control_model() { return 0x03; }
+constexpr uint8_t multichannel_control_model() { return 0x04; }
+constexpr uint8_t capi_control_model() { return 0x05; }
+constexpr uint8_t ethernet_networking_control_model() { return 0x06; }
+constexpr uint8_t atm_networking_control_model() { return 0x07; }
+constexpr uint8_t wireless_handset_control_model() { return 0x08; }
+constexpr uint8_t device_management() { return 0x09; }
+constexpr uint8_t mobile_direct_line_model() { return 0x0A; }
+constexpr uint8_t obex() { return 0x0B; }
+constexpr uint8_t ethernet_emulation_model() { return 0x0C; }
+constexpr uint8_t network_control_model() { return 0x0D; }
+}  // namespace communications_subclass
+
+namespace cdc_class_requests {
+constexpr uint8_t send_encapsulated_command() { return 0x00; }
+constexpr uint8_t get_encapsulated_response() { return 0x01; }
+constexpr uint8_t set_comm_feature() { return 0x02; }
+constexpr uint8_t get_comm_feature() { return 0x03; }
+constexpr uint8_t clear_comm_feature() { return 0x04; }
+constexpr uint8_t set_aux_line_state() { return 0x10; }
+constexpr uint8_t set_hook_state() { return 0x11; }
+constexpr uint8_t pulse_setup() { return 0x12; }
+constexpr uint8_t send_pulse() { return 0x13; }
+constexpr uint8_t set_pulse_time() { return 0x14; }
+constexpr uint8_t ring_aux_jack() { return 0x15; }
+constexpr uint8_t set_line_coding() { return 0x20; }
+constexpr uint8_t get_line_coding() { return 0x21; }
+constexpr uint8_t set_control_line_state() { return 0x22; }
+constexpr uint8_t send_break() { return 0x23; }
+constexpr uint8_t set_ringer_parms() { return 0x30; }
+constexpr uint8_t get_ringer_parms() { return 0x31; }
+constexpr uint8_t set_operation_parms() { return 0x32; }
+constexpr uint8_t get_operation_parms() { return 0x33; }
+constexpr uint8_t set_line_parms() { return 0x34; }
+constexpr uint8_t get_line_parms() { return 0x35; }
+constexpr uint8_t dial_digits() { return 0x36; }
+constexpr uint8_t set_unit_parameter() { return 0x37; }
+constexpr uint8_t get_unit_parameter() { return 0x38; }
+constexpr uint8_t clear_unit_parameter() { return 0x39; }
+constexpr uint8_t get_profile() { return 0x3A; }
+}  // namespace cdc_class_requests
+
+}  // namespace
+
+void AcmTty::Initialize() {
+  status_interface_ = AddInterface();
+  data_interface_ = AddInterface();
+  status_endpoint_ = AddEndpoint();
+  data_tx_endpoint_ = AddEndpoint();
+  data_rx_endpoint_ = AddEndpoint();
+
+  {
+    const auto iad_descriptor = CreateDescriptor(
+        iad_descriptor_length(), UsbDescriptorType::kInterfaceAssociation);
+    iad_descriptor->AddByte(status_interface_);       // bFirstInterface
+    iad_descriptor->AddByte(2);                       // bInterfaceCount
+    iad_descriptor->AddByte(communications_class());  // bFunctionClass
+    iad_descriptor->AddByte(communications_subclass::
+                                abstract_control_model());  // bFunctionSubClass
+    iad_descriptor->AddByte(0);                             // bFunctionProtocol
+    iad_descriptor->AddByte(device()->AddString("UsbTty"));  // iFunction
+  }
+
+  {
+    const auto interface_descriptor = CreateDescriptor(
+        interface_descriptor_length(), UsbDescriptorType::kInterface);
+    interface_descriptor->AddByte(status_interface_);  // bInterfaceNumber
+    interface_descriptor->AddByte(0);  // bAlternateSetting
+    interface_descriptor->AddByte(1);                       // bNumEndpoints
+    interface_descriptor->AddByte(communications_class());  // bInterfaceClass
+    interface_descriptor->AddByte(
+        communications_subclass::
+            abstract_control_model());  // bInterfaceSubClass
+    interface_descriptor->AddByte(0);   // bInterfaceProtocol
+    interface_descriptor->AddByte(
+        device()->AddString("UsbTty.status"));  // iInterface
+  }
+
+  {
+    const auto cdc_header =
+        CreateDescriptor(cdc_descriptor_subtype::header_length(),
+                         UsbClassDescriptorType::kInterface);
+    cdc_header->AddByte(
+        cdc_descriptor_subtype::header());  // bDescriptorSubtype
+    cdc_header->AddUint16(0x0110);          // bcdCDC
+  }
+
+  {
+    const auto call_management =
+        CreateDescriptor(cdc_descriptor_subtype::call_management_length(),
+                         UsbClassDescriptorType::kInterface);
+    call_management->AddByte(
+        cdc_descriptor_subtype::call_management());  // bDescriptorSubtype
+    // We don't do call management.
+    call_management->AddByte(0);  // bmCapabilities
+    call_management->AddByte(data_interface_);  // bDataInterface
+  }
+
+  {
+    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
+    // We support:
+    //   line_coding and serial_state
+    //   send_break
+    // We don't support:
+    //   comm_feature
+    //   network_notification
+    abstract_control_management->AddByte(6);  // bmCapabilities
+  }
+
+  {
+    const auto cdc_union_descriptor =
+        CreateDescriptor(cdc_descriptor_subtype::union_length(1),
+                         UsbClassDescriptorType::kInterface);
+    cdc_union_descriptor->AddByte(
+        cdc_descriptor_subtype::union_function());     // bDescriptorSubtype
+    cdc_union_descriptor->AddByte(status_interface_);  // bMasterInterface
+    cdc_union_descriptor->AddByte(data_interface_);  // bSlaveInterface
+  }
+
+  {
+    const auto endpoint_descriptor = CreateDescriptor(
+        endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
+    endpoint_descriptor->AddByte(status_endpoint_ |
+                                 m_endpoint_address_in());  // bEndpointAddress
+    endpoint_descriptor->AddByte(
+        m_endpoint_attributes_interrupt());                // bmAttributes
+    endpoint_descriptor->AddUint16(kStatusMaxPacketSize);  // wMaxEndpointSize
+    // Set it to the max because we have nothing to send, so no point using bus
+    // bandwidth asking.
+    endpoint_descriptor->AddByte(255);  // bInterval
+  }
+
+  {
+    const auto interface_descriptor = CreateDescriptor(
+        interface_descriptor_length(), UsbDescriptorType::kInterface);
+    interface_descriptor->AddByte(data_interface_);         // bInterfaceNumber
+    interface_descriptor->AddByte(0);                       // bAlternateSetting
+    interface_descriptor->AddByte(2);                       // bNumEndpoints
+    interface_descriptor->AddByte(data_interface_class());  // bInterfaceClass
+    interface_descriptor->AddByte(0);  // bInterfaceSubClass
+    interface_descriptor->AddByte(0);  // bInterfaceProtocol
+    interface_descriptor->AddByte(
+        device()->AddString("UsbTty.data"));  // iInterface
+  }
+
+  // Kernel seems to think tx and rx belong in the other order, but it deals
+  // with either one. Everybody's examples seem to use this order, and the
+  // kernel deals with it, so going to go with this.
+  {
+    const auto endpoint_descriptor = CreateDescriptor(
+        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->AddByte(1);                     // bInterval
+  }
+
+  {
+    const auto endpoint_descriptor = CreateDescriptor(
+        endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
+    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->AddByte(1);                     // bInterval
+  }
+}
+
+// We deliberately don't implement SendEncapsulatedCommand and
+// GetEncapsulatedResponse because there doesn't seem to be any reason for
+// anybody to send those to us, despite them being "required".
+UsbFunction::SetupResponse AcmTty::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 != status_interface_) {
+    return SetupResponse::kIgnored;
+  }
+  const bool in = setup_packet.request_type & M_SETUP_REQUEST_TYPE_IN;
+  switch (setup_packet.request) {
+    case cdc_class_requests::set_line_coding():
+      if (in || setup_packet.value != 0 ||
+          setup_packet.length != sizeof(line_coding_)) {
+        return SetupResponse::kStall;
+      }
+      next_endpoint0_out_ = NextEndpoint0Out::kLineCoding;
+      return SetupResponse::kHandled;
+
+    case cdc_class_requests::get_line_coding():
+      if (!in || setup_packet.value != 0 ||
+          setup_packet.length != sizeof(line_coding_)) {
+        return SetupResponse::kStall;
+      }
+      line_coding_to_send_ = line_coding_;
+      device()->QueueEndpoint0Data(
+          reinterpret_cast<const char *>(&line_coding_to_send_),
+          setup_packet.length);
+      return SetupResponse::kHandled;
+
+    case cdc_class_requests::set_control_line_state():
+      if (in || setup_packet.length != 0) {
+        return SetupResponse::kStall;
+      }
+      control_line_state_ = setup_packet.value;
+      device()->SendEmptyEndpoint0Packet();
+      return SetupResponse::kHandled;
+
+    case cdc_class_requests::send_break():
+      if (in || setup_packet.length != 0) {
+        return SetupResponse::kStall;
+      }
+      // TODO(Brian): setup_packet.value is the length of the break in ms.
+      // 0xFFFF means keep sending break until receiving another one.
+      device()->SendEmptyEndpoint0Packet();
+      return SetupResponse::kHandled;
+  }
+  return SetupResponse::kStall;
+}
+
+UsbFunction::SetupResponse AcmTty::HandleEndpoint0OutPacket(void *data,
+                                                            int data_length) {
+  switch (next_endpoint0_out_) {
+    case NextEndpoint0Out::kNone:
+      return SetupResponse::kIgnored;
+
+    case NextEndpoint0Out::kLineCoding:
+      if (data_length != sizeof(line_coding_)) {
+        return SetupResponse::kStall;
+      }
+      memcpy(&line_coding_, data, data_length);
+      device()->SendEmptyEndpoint0Packet();
+      return SetupResponse::kHandled;
+
+    default:
+      return SetupResponse::kIgnored;
+  }
+}
+
+void AcmTty::HandleOutFinished(int endpoint, BdtEntry *bdt_entry) {
+  if (endpoint == data_rx_endpoint_) {
+    const size_t data_size = G_USB_BD_BC(bdt_entry->buffer_descriptor);
+    CHECK(rx_queue_.Write(static_cast<char *>(bdt_entry->address), data_size) ==
+          data_size);
+    dma_memory_barrier();
+
+    DisableInterrupts disable_interrupts;
+    // If we don't have space to handle it, don't return the entry so we'll
+    // ask the host to wait to transmit more data.
+    if (rx_queue_.space_available() >= kDataMaxPacketSize * 2) {
+      bdt_entry->buffer_descriptor = M_USB_BD_OWN | M_USB_BD_DTS |
+                                     V_USB_BD_BC(kDataMaxPacketSize) |
+                                     static_cast<uint32_t>(next_rx_toggle_);
+      next_rx_toggle_ = Data01Inverse(next_rx_toggle_);
+    } else {
+      if (first_rx_held_ == nullptr) {
+        first_rx_held_ = bdt_entry;
+      } else {
+        CHECK(second_rx_held_ == nullptr);
+        second_rx_held_ = bdt_entry;
+      }
+    }
+  }
+}
+
+void AcmTty::HandleInFinished(int endpoint, BdtEntry * /*bdt_entry*/,
+                              EvenOdd odd) {
+  if (endpoint == data_tx_endpoint_) {
+    DisableInterrupts disable_interrupts;
+    CHECK(odd == BufferStateToEmpty(tx_state_));
+    tx_state_ = BufferStateAfterEmpty(tx_state_);
+    EnqueueTxData(disable_interrupts);
+  }
+}
+
+void AcmTty::HandleConfigured(int endpoint) {
+  // TODO(Brian): Handle data already in the buffers correctly.
+  if (endpoint == status_endpoint_) {
+    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;
+    EnqueueTxData(disable_interrupts);
+  } else if (endpoint == data_rx_endpoint_) {
+    device()->ConfigureEndpointFor(data_rx_endpoint_, true, false, true);
+    next_rx_toggle_ = Data01::kData0;
+    device()->SetBdtEntry(
+        data_rx_endpoint_, Direction::kRx, EvenOdd::kEven,
+        {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(tx_buffers_[0].size()),
+         tx_buffers_[0].data()});
+    device()->SetBdtEntry(
+        data_rx_endpoint_, Direction::kRx, EvenOdd::kOdd,
+        {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(tx_buffers_[1].size()) |
+             M_USB_BD_DATA1,
+         tx_buffers_[1].data()});
+  }
+}
+
+size_t AcmTty::Read(void *buffer, size_t buffer_size) {
+  const size_t r = rx_queue_.Read(static_cast<char *>(buffer), buffer_size);
+
+  DisableInterrupts disable_interrupts;
+  if (rx_queue_.space_available() >= kDataMaxPacketSize * 2) {
+    if (first_rx_held_ != nullptr) {
+      first_rx_held_->buffer_descriptor =
+          M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kDataMaxPacketSize) |
+          static_cast<uint32_t>(next_rx_toggle_);
+      next_rx_toggle_ = Data01Inverse(next_rx_toggle_);
+    }
+    if (second_rx_held_ != nullptr) {
+      second_rx_held_->buffer_descriptor =
+          M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kDataMaxPacketSize) |
+          static_cast<uint32_t>(next_rx_toggle_);
+      next_rx_toggle_ = Data01Inverse(next_rx_toggle_);
+    }
+    first_rx_held_ = second_rx_held_ = nullptr;
+  }
+
+  return r;
+}
+
+size_t AcmTty::Write(const void *buffer, size_t buffer_size) {
+  const size_t r =
+      tx_queue_.Write(static_cast<const char *>(buffer), buffer_size);
+  DisableInterrupts disable_interrupts;
+  EnqueueTxData(disable_interrupts);
+  return r;
+}
+
+// TODO(Brian): Could this critical section be broken up per buffer we fill?
+void AcmTty::EnqueueTxData(const DisableInterrupts &) {
+  while (BufferStateHasEmpty(tx_state_) && !tx_queue_.empty()) {
+    const EvenOdd next_tx_odd = BufferStateToFill(tx_state_);
+    const size_t buffer_size =
+        tx_queue_.Read(tx_buffer_for(next_tx_odd), kDataMaxPacketSize);
+    CHECK(buffer_size > 0);
+    dma_memory_barrier();
+    device()->SetBdtEntry(
+        data_tx_endpoint_, Direction::kTx, next_tx_odd,
+        {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(buffer_size) |
+             static_cast<uint32_t>(next_tx_toggle_),
+         tx_buffer_for(next_tx_odd)});
+    tx_state_ = BufferStateAfterFill(tx_state_);
+    next_tx_toggle_ = Data01Inverse(next_tx_toggle_);
+  }
+}
+
+}  // namespace teensy
+}  // namespace frc971