Get a basic USB device working

It enumerates, takes its address, gets configured, and then Linux kind
of gives up because it has no endpoints.

Change-Id: I01f75acee419b585e455f428ee45bcd37f0ce189
diff --git a/aos/common/BUILD b/aos/common/BUILD
index e3160c4..7a15731 100644
--- a/aos/common/BUILD
+++ b/aos/common/BUILD
@@ -1,6 +1,7 @@
 package(default_visibility = ['//visibility:public'])
 
 load('/aos/build/queues', 'queue_library')
+load("//tools:environments.bzl", "mcu_cpus")
 
 queue_library(
   name = 'test_queue',
@@ -21,6 +22,7 @@
   hdrs = [
     'macros.h',
   ],
+  compatible_with = mcu_cpus,
 )
 
 cc_library(
diff --git a/motors/BUILD b/motors/BUILD
index ab9f239..e652069 100644
--- a/motors/BUILD
+++ b/motors/BUILD
@@ -1,3 +1,6 @@
+load('//motors:macros.bzl', 'hex_from_elf')
+load("//tools:environments.bzl", "mcu_cpus")
+
 cc_binary(
   name = 'medium_salsa.elf',
   srcs = [
@@ -6,29 +9,21 @@
   deps = [
     ':util',
     '//motors/core',
-    '//motors/usb',
+    '//motors/usb:legacy',
   ],
-  restricted_to = ['//tools:cortex-m4f'],
+  restricted_to = mcu_cpus,
 )
 
-genrule(
+hex_from_elf(
   name = 'medium_salsa',
-  srcs = [
-    'medium_salsa.elf',
-  ],
-  outs = [
-    'medium_salsa.hex',
-  ],
-  cmd = '$(OBJCOPY) -O ihex $<  $@',
-  executable = True,
-  output_to_bindir = True,
-  restricted_to = ['//tools:cortex-m4f'],
+  restricted_to = mcu_cpus,
 )
 
 cc_library(
   name = 'util',
+  visibility = ['//visibility:public'],
   hdrs = [
     'util.h',
   ],
-  restricted_to = ['//tools:cortex-m4f'],
+  restricted_to = mcu_cpus,
 )
diff --git a/motors/core/BUILD b/motors/core/BUILD
index 73a9d31..107ae5d 100644
--- a/motors/core/BUILD
+++ b/motors/core/BUILD
@@ -1,3 +1,5 @@
+load("//tools:environments.bzl", "mcu_cpus")
+
 filegroup(
   name = 'linkerscript',
   visibility = ['//tools/cpp:__pkg__'],
@@ -17,5 +19,5 @@
     'nonstd.c',
     'time.c',
   ],
-  restricted_to = ['//tools:cortex-m4f'],
+  restricted_to = mcu_cpus,
 )
diff --git a/motors/macros.bzl b/motors/macros.bzl
new file mode 100644
index 0000000..3b1d379
--- /dev/null
+++ b/motors/macros.bzl
@@ -0,0 +1,10 @@
+def hex_from_elf(name, restricted_to=None):
+  native.genrule(
+    name = name,
+    srcs = [ '%s.elf' % name ],
+    outs = [ '%s.hex' % name ],
+    cmd = '$(OBJCOPY) -O ihex $< $@',
+    executable = True,
+    output_to_bindir = True,
+    restricted_to = restricted_to,
+  )
diff --git a/motors/pistol_grip/BUILD b/motors/pistol_grip/BUILD
new file mode 100644
index 0000000..d83b284
--- /dev/null
+++ b/motors/pistol_grip/BUILD
@@ -0,0 +1,20 @@
+load('//motors:macros.bzl', 'hex_from_elf')
+load("//tools:environments.bzl", "mcu_cpus")
+
+cc_binary(
+  name = 'drivers_station.elf',
+  srcs = [
+    'drivers_station.cc',
+  ],
+  deps = [
+    '//motors:util',
+    '//motors/usb',
+    '//motors/core',
+  ],
+  restricted_to = mcu_cpus,
+)
+
+hex_from_elf(
+  name = 'drivers_station',
+  restricted_to = mcu_cpus,
+)
diff --git a/motors/pistol_grip/drivers_station.cc b/motors/pistol_grip/drivers_station.cc
new file mode 100644
index 0000000..370483e
--- /dev/null
+++ b/motors/pistol_grip/drivers_station.cc
@@ -0,0 +1,66 @@
+// This file has the main for the Teensy in the driver's station that
+// communicates over CAN with the one in the pistol grip controller.
+
+#include <stdio.h>
+
+#include "motors/core/time.h"
+#include "motors/core/kinetis.h"
+#include "motors/usb/usb.h"
+#include "motors/util.h"
+
+namespace frc971 {
+namespace motors {
+
+extern "C" {
+void *__stack_chk_guard = (void *)0x67111971;
+int _write(int file, char *ptr, int len) {
+  (void)file;
+  (void)ptr;
+  (void)len;
+  return -1;
+}
+}  // extern "C"
+
+void __stack_chk_fail(void);
+
+extern "C" int main(void) {
+  // for background about this startup delay, please see these conversations
+  // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980
+  // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273
+  delay(400);
+
+  // Set all interrupts to the second-lowest priority to start with.
+  for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) NVIC_SET_SANE_PRIORITY(i, 0xD);
+
+  // Now set priorities for all the ones we care about. They only have meaning
+  // relative to each other, which means centralizing them here makes it a lot
+  // more manageable.
+  NVIC_SET_SANE_PRIORITY(IRQ_USBOTG, 0x7);
+
+  // Set the LED's pin to output mode.
+  GPIO_BITBAND(GPIOC_PDDR, 5) = 1;
+  PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
+
+  delay(100);
+
+  teensy::UsbDevice usb_device(0, 0x16c0, 0x0490);
+  usb_device.Initialize();
+
+  //GPIOC_PSOR = 1 << 5;
+  while (true) {}
+
+  return 0;
+}
+
+void __stack_chk_fail(void) {
+  while (true) {
+    GPIOC_PSOR = (1 << 5);
+    printf("Stack corruption detected\n");
+    delay(1000);
+    GPIOC_PCOR = (1 << 5);
+    delay(1000);
+  }
+}
+
+}  // namespace motors
+}  // namespace frc971
diff --git a/motors/usb/BUILD b/motors/usb/BUILD
index ddb1bcc..55b1790 100644
--- a/motors/usb/BUILD
+++ b/motors/usb/BUILD
@@ -1,5 +1,7 @@
+load("//tools:environments.bzl", "mcu_cpus")
+
 cc_library(
-  name = 'usb',
+  name = 'legacy',
   visibility = ['//visibility:public'],
   hdrs = [
     'usb_desc.h',
@@ -21,5 +23,42 @@
   deps = [
     '//motors/core',
   ],
-  restricted_to = ['//tools:cortex-m4f'],
+  restricted_to = mcu_cpus,
+)
+
+cc_library(
+  name = 'usb',
+  visibility = ['//visibility:public'],
+  hdrs = [
+    'usb.h',
+  ],
+  srcs = [
+    'usb.cc',
+  ],
+  deps = [
+    ':constants',
+    '//aos/common:macros',
+    '//motors/core',
+    '//motors:util',
+  ],
+  restricted_to = mcu_cpus,
+)
+
+cc_library(
+  name = 'constants',
+  hdrs = [
+    'constants.h',
+  ],
+  compatible_with = mcu_cpus,
+)
+
+cc_test(
+  name = 'constants_test',
+  srcs = [
+    'constants_test.cc',
+  ],
+  deps = [
+    ':constants',
+    '//aos/testing:googletest',
+  ],
 )
diff --git a/motors/usb/constants.h b/motors/usb/constants.h
new file mode 100644
index 0000000..58c9ecc
--- /dev/null
+++ b/motors/usb/constants.h
@@ -0,0 +1,114 @@
+#ifndef MOTORS_USB_CONSTANTS_H_
+#define MOTORS_USB_CONSTANTS_H_
+
+#include <stdint.h>
+
+namespace frc971 {
+namespace teensy {
+
+enum class Direction : uint32_t {
+  kTx = 1 << 1,
+  kRx = 0,
+};
+
+enum class EvenOdd : uint32_t {
+  kOdd = 1 << 0,
+  kEven = 0,
+};
+
+constexpr static inline EvenOdd EvenOddInverse(EvenOdd odd) {
+  return static_cast<EvenOdd>(static_cast<uint32_t>(odd) ^
+                              static_cast<uint32_t>(EvenOdd::kOdd));
+}
+
+// Returns 0 for kEven and 1 for kOdd. This is useful for indexing into arrays
+// and similar things.
+constexpr static inline int EvenOddIndex(EvenOdd odd) {
+  static_assert(static_cast<int>(EvenOdd::kOdd) == 1, "Value changed");
+  return static_cast<int>(odd);
+}
+
+enum class EndpointBufferState : int {
+  // The values are chosen carefully so bit arithmetic can efficiently
+  // manipulate these values. This math is all encapsulated in methods
+  // immediately following.
+  // Bit 0 is even full.
+  // Bit 1 is odd full.
+  // Bit 2 is which one to fill next (1 for odd).
+  // Bit 3 is which one to empty next (1 for odd).
+
+  // Both are empty and we should fill the even one first.
+  kBothEmptyEvenFirst = 0x0,
+  kBothEmptyOddFirst = 0xC,
+  kEvenFull = 0x5,
+  kOddFull = 0xA,
+  // Both are full and we should empty the even one first.
+  kBothFullEvenFirst = 0x3,
+  kBothFullOddFirst = 0xF,
+};
+
+// kBothEmptyEvenFirst fill even kEvenFull fill odd kBothFullEvenFirst
+//   empty even kOddFull empty odd kBothEmptyEvenFirst
+
+// Returns true if state has at least one empty buffer.
+constexpr static inline bool BufferStateHasEmpty(EndpointBufferState state) {
+  return (static_cast<int>(state) & 0x3) != 0x3;
+}
+
+// Returns true if state has at least one full buffer.
+constexpr static inline bool BufferStateHasFull(EndpointBufferState state) {
+  return (static_cast<int>(state) & 0x3) != 0;
+}
+
+// Returns the next buffer to fill from state.
+//
+// This won't make sense if !BufferStateHasEmpty(state).
+constexpr static inline EvenOdd BufferStateToFill(EndpointBufferState state) {
+  return (static_cast<int>(state) & 0x4) ? EvenOdd::kOdd : EvenOdd::kEven;
+}
+
+// Returns the next buffer to empty from state.
+//
+// This won't make sense if !BufferStateHasFull(state).
+constexpr static inline EvenOdd BufferStateToEmpty(EndpointBufferState state) {
+  return (static_cast<int>(state) & 0x8) ? EvenOdd::kOdd : EvenOdd::kEven;
+}
+
+// Returns the new state after filling BufferStateToFill(state).
+//
+// This won't make sense if !BufferStateHasEmpty(state).
+constexpr static inline EndpointBufferState BufferStateAfterFill(
+    EndpointBufferState state) {
+  return static_cast<EndpointBufferState>(
+      // XOR with bit 2 to toggle which is next.
+      (static_cast<int>(state) ^ 0x4) |
+      // Set the bit corresponding to the buffer which was filled.
+      (1 << EvenOddIndex(BufferStateToFill(state))));
+}
+
+// Returns the new state after emptying BufferStateToEmpty(state).
+//
+// This won't make sense if !BufferStateHasFull(state).
+constexpr static inline EndpointBufferState BufferStateAfterEmpty(
+    EndpointBufferState state) {
+  return static_cast<EndpointBufferState>(
+      // XOR with bit 3 to toggle which is next.
+      (static_cast<int>(state) ^ 0x8) &
+      // Clear the bit corresponding to the buffer which was emptied.
+      ~(1 << EvenOddIndex(BufferStateToEmpty(state))));
+}
+
+enum class Data01 : uint32_t {
+  kData1 = 1 << 6,
+  kData0 = 0,
+};
+
+constexpr static inline Data01 Data01Inverse(Data01 toggle) {
+  return static_cast<Data01>(static_cast<uint32_t>(toggle) ^
+                             static_cast<uint32_t>(Data01::kData1));
+}
+
+}  // namespace teensy
+}  // namespace frc971
+
+#endif  // MOTORS_USB_CONSTANTS_H_
diff --git a/motors/usb/constants_test.cc b/motors/usb/constants_test.cc
new file mode 100644
index 0000000..4d11349
--- /dev/null
+++ b/motors/usb/constants_test.cc
@@ -0,0 +1,66 @@
+#include "motors/usb/constants.h"
+
+#include "gtest/gtest.h"
+
+namespace frc971 {
+namespace teensy {
+namespace testing {
+
+TEST(EndpointBufferStateTest, Filling) {
+  EXPECT_TRUE(BufferStateHasEmpty(EndpointBufferState::kBothEmptyEvenFirst));
+  EXPECT_EQ(EvenOdd::kEven,
+            BufferStateToFill(EndpointBufferState::kBothEmptyEvenFirst));
+
+  EXPECT_TRUE(BufferStateHasEmpty(EndpointBufferState::kBothEmptyOddFirst));
+  EXPECT_EQ(EvenOdd::kOdd,
+            BufferStateToFill(EndpointBufferState::kBothEmptyOddFirst));
+
+  EXPECT_TRUE(BufferStateHasEmpty(EndpointBufferState::kEvenFull));
+  EXPECT_EQ(EvenOdd::kOdd, BufferStateToFill(EndpointBufferState::kEvenFull));
+
+  EXPECT_TRUE(BufferStateHasEmpty(EndpointBufferState::kOddFull));
+  EXPECT_EQ(EvenOdd::kEven, BufferStateToFill(EndpointBufferState::kOddFull));
+
+  EXPECT_FALSE(BufferStateHasEmpty(EndpointBufferState::kBothFullEvenFirst));
+}
+
+TEST(EndpointBufferStateTest, Emptying) {
+  EXPECT_FALSE(BufferStateHasFull(EndpointBufferState::kBothEmptyEvenFirst));
+
+  EXPECT_FALSE(BufferStateHasFull(EndpointBufferState::kBothEmptyOddFirst));
+
+  EXPECT_TRUE(BufferStateHasFull(EndpointBufferState::kEvenFull));
+  EXPECT_EQ(EvenOdd::kEven, BufferStateToEmpty(EndpointBufferState::kEvenFull));
+
+  EXPECT_TRUE(BufferStateHasFull(EndpointBufferState::kOddFull));
+  EXPECT_EQ(EvenOdd::kOdd, BufferStateToEmpty(EndpointBufferState::kOddFull));
+
+  EXPECT_TRUE(BufferStateHasFull(EndpointBufferState::kBothFullEvenFirst));
+  EXPECT_EQ(EvenOdd::kEven,
+            BufferStateToEmpty(EndpointBufferState::kBothFullEvenFirst));
+}
+
+TEST(EndpointBufferStateTest, Transitions) {
+  EXPECT_EQ(EndpointBufferState::kEvenFull,
+            BufferStateAfterFill(EndpointBufferState::kBothEmptyEvenFirst));
+
+  EXPECT_EQ(EndpointBufferState::kOddFull,
+            BufferStateAfterFill(EndpointBufferState::kBothEmptyOddFirst));
+
+  EXPECT_EQ(EndpointBufferState::kBothFullEvenFirst,
+            BufferStateAfterFill(EndpointBufferState::kEvenFull));
+  EXPECT_EQ(EndpointBufferState::kBothEmptyOddFirst,
+            BufferStateAfterEmpty(EndpointBufferState::kEvenFull));
+
+  EXPECT_EQ(EndpointBufferState::kBothFullOddFirst,
+            BufferStateAfterFill(EndpointBufferState::kOddFull));
+  EXPECT_EQ(EndpointBufferState::kBothEmptyEvenFirst,
+            BufferStateAfterEmpty(EndpointBufferState::kOddFull));
+
+  EXPECT_EQ(EndpointBufferState::kOddFull,
+            BufferStateAfterEmpty(EndpointBufferState::kBothFullEvenFirst));
+}
+
+}  // namespace testing
+}  // namespace teensy
+}  // namespace frc971
diff --git a/motors/usb/usb.cc b/motors/usb/usb.cc
new file mode 100644
index 0000000..3067695
--- /dev/null
+++ b/motors/usb/usb.cc
@@ -0,0 +1,812 @@
+#include "motors/usb/usb.h"
+
+#include <string.h>
+
+#include "motors/util.h"
+
+namespace frc971 {
+namespace teensy {
+namespace {
+
+// The mask of interrupts we care about.
+constexpr uint32_t usb_enabled_interrupts() {
+  // Deliberately not turning the sleep interrupt on here because we just
+  // want to ignore that anyways.
+  return USB_INTEN_TOKDNEEN | USB_INTEN_SOFTOKEN | USB_INTEN_ERROREN |
+         USB_INTEN_USBRSTEN;
+}
+
+// The names of all the standard setup requests which come in on endpoint 0.
+namespace standard_setup_requests {
+constexpr int kGetStatus = 0;
+constexpr int kClearFeature = 1;
+constexpr int kSetFeature = 3;
+constexpr int kSetAddress = 5;
+constexpr int kGetDescriptor = 6;
+constexpr int kSetDescriptor = 7;
+constexpr int kGetConfiguration = 8;
+constexpr int kSetConfiguration = 9;
+constexpr int kGetInterface = 10;
+constexpr int kSetInterface = 11;
+constexpr int kSynchFrame = 12;
+}  // namespace standard_setup_requests
+
+// The names of the standard feature selectors.
+namespace standard_feature_selectors {
+constexpr int kDeviceRemoteWakeup = 1;
+constexpr int kEndpointHalt = 0;
+constexpr int kTestMode = 2;
+}  // namespace standard_feature_selectors
+
+// The names of all the PIDs (Packet IDs) from the USB standard. Note that this
+// USB hardware doesn't expose most of them, especially in device mode.
+enum class UsbPid {
+  kOut = 0x1,
+  kIn = 0x9,
+  kSof = 0x5,
+  kSetup = 0xD,
+  kData0 = 0x3,
+  kData1 = 0xB,
+  kData2 = 0x7,
+  kMData = 0xF,
+  kAck = 0x2,
+  kNak = 0xA,
+  kStall = 0xE,
+  kNYet = 0x6,
+  kPre = 0xC,
+  kErr = 0xC,
+  kSplit = 0x8,
+  kPing = 0x4,
+  kReserved = 0x0,
+};
+
+// The device class for using IADs.
+constexpr uint8_t iad_device_class() { return 0xEF; }
+// The device subclass for using IADs.
+constexpr uint8_t iad_device_subclass() { return 0x02; }
+// The device protocol for using IADs.
+constexpr uint8_t iad_device_protocol() { return 0x01; }
+
+// The total number of endpoints supported by this hardware.
+constexpr int number_endpoints() { return 16; }
+
+__attribute__((aligned(512))) BdtEntry
+    usb0_buffer_descriptor_table[number_endpoints() * 2 /* rx/tx */ *
+                                 2 /* even/odd */];
+
+// Returns the specified BDT entry.
+BdtEntry *MutableBdtEntry(int endpoint, Direction direction, EvenOdd odd) {
+  return &usb0_buffer_descriptor_table[static_cast<uint32_t>(endpoint << 2) |
+                                       static_cast<uint32_t>(direction) |
+                                       static_cast<uint32_t>(odd)];
+}
+
+// Returns the BDT entry corresponding to a USBx_STAT value.
+BdtEntry *MutableBdtEntryFromStat(uint8_t stat) {
+  return &usb0_buffer_descriptor_table[static_cast<uint32_t>(stat) >> 2];
+}
+
+// A pointer to the object we're going to ask to handle interrupts.
+UsbDevice *volatile global_usb0_device = nullptr;
+
+}  // namespace
+
+UsbDevice::UsbDevice(int index, uint16_t vendor_id, uint16_t product_id)
+    : index_(index) {
+  // TODO(Brian): Pass index_ into all the register access macros. Also sort out
+  // how to deal with it for the interrupts.
+  assert(index == 0);
+
+  assert(global_usb0_device == nullptr);
+  global_usb0_device = this;
+
+  // Endpoint 0 isn't a normal endpoint, so it doesn't show up in here.
+  endpoint_mapping_.push_back(nullptr);
+
+  // Set up the "String Descriptor Zero, Specifying Languages Supported by the
+  // Device" (aka english_us_code() only).
+  strings_.emplace_back(4, '\0');
+  strings_.back()[0] = 4;
+  strings_.back()[1] = static_cast<uint8_t>(UsbDescriptorType::kString);
+  strings_.back()[2] = english_us_code() & 0xFF;
+  strings_.back()[3] = (english_us_code() >> 8) & 0xFF;
+
+  device_descriptor_ =
+      device_descriptor_list_.CreateDescriptor(18, UsbDescriptorType::kDevice);
+  device_descriptor_->AddUint16(0x0200);  // bcdUSB
+  device_descriptor_->AddByte(iad_device_class());  // bDeviceClass
+  device_descriptor_->AddByte(iad_device_subclass());  // bDeviceSubClass
+  device_descriptor_->AddByte(iad_device_protocol());  // bDeviceProtocol
+  device_descriptor_->AddByte(kEndpoint0MaxSize);  // bMaxPacketSize0
+  device_descriptor_->AddUint16(vendor_id);  // idVendor
+  device_descriptor_->AddUint16(product_id);  // idProduct
+  device_descriptor_->AddUint16(0);  // bcdDevice
+  // We might overwrite these string descriptor indices later if we get strings
+  // to put there.
+  device_descriptor_->AddByte(0);  // iManufacturer
+  device_descriptor_->AddByte(0);  // iProduct
+  device_descriptor_->AddByte(0);  // iSerialNumber
+  device_descriptor_->AddByte(1);  // bNumConfigurations
+
+  config_descriptor_ = config_descriptor_list_.CreateDescriptor(
+      9, UsbDescriptorType::kConfiguration);
+}
+
+UsbDevice::~UsbDevice() {
+  NVIC_DISABLE_IRQ(IRQ_USBOTG);
+  dma_memory_barrier();
+  assert(global_usb0_device == this);
+  global_usb0_device = nullptr;
+}
+
+void UsbDevice::Initialize() {
+  assert(!is_set_up_);
+
+  for (UsbFunction *function : functions_) {
+    function->Initialize();
+  }
+
+  config_descriptor_->AddUint16(
+      config_descriptor_list_.CurrentSize());              // wTotalLength
+  config_descriptor_->AddByte(interface_mapping_.size());  // bNumInterfaces
+  config_descriptor_->AddByte(1);  // bConfigurationValue
+  // Doesn't seem to be much point naming our one and only configuration.
+  config_descriptor_->AddByte(0);  // iConfiguration
+  config_descriptor_->AddByte((1 << 7) /* Reserved */ |
+                              (1 << 6) /* Self-powered */);  // bmAttribute
+  config_descriptor_->AddByte(2 /* 4mA */);  // bMaxPower
+
+  device_descriptor_.reset();
+  config_descriptor_.reset();
+  device_descriptor_list_.CheckFinished();
+  config_descriptor_list_.CheckFinished();
+  is_set_up_ = true;
+
+  // Make sure all the buffer descriptors are clear.
+  for (int i = 0; i < number_endpoints(); ++i) {
+    for (Direction direction : {Direction::kTx, Direction::kRx}) {
+      for (EvenOdd odd : {EvenOdd::kOdd, EvenOdd::kEven}) {
+        MutableBdtEntry(i, direction, odd)->buffer_descriptor = 0;
+        MutableBdtEntry(i, direction, odd)->address = nullptr;
+      }
+    }
+  }
+  dma_memory_barrier();
+
+  // The other startup code handles getting the incoming 48MHz clock running.
+  SIM_SCGC4 |= SIM_SCGC4_USBOTG;
+  MPU_RGDAAC0 |= 0x03000000;
+
+  // Reset it.
+  USB0_USBTRC0 = USB_USBTRC_USBRESET;
+  // TRM says to wait "two USB clock cycles", so assume that's at 48MHz and then
+  // round up, being pessimistic in assuming each read from the peripheral is
+  // only a single core clock. This wildly overapproximates how long we need to
+  // wait, but whatever.
+  for (int i = 0; i < ((F_CPU / 48000000) + 1) * 2; ++i) {
+    while ((USB0_USBTRC0 & USB_USBTRC_USBRESET) != 0) {
+    }
+  }
+
+  USB0_BDTPAGE1 =
+      reinterpret_cast<uintptr_t>(&usb0_buffer_descriptor_table[0]) >> 8;
+  USB0_BDTPAGE2 =
+      reinterpret_cast<uintptr_t>(&usb0_buffer_descriptor_table[0]) >> 16;
+  USB0_BDTPAGE3 =
+      reinterpret_cast<uintptr_t>(&usb0_buffer_descriptor_table[0]) >> 24;
+
+  // The Quick Reference User Guide says to clear all the interrupts.
+  ClearInterrupts();
+  USB0_OTGISTAT = USB_OTGISTAT_ONEMSEC | USB_OTGISTAT_LINE_STATE_CHG;
+
+  // Now enable the module.
+  USB0_CTL = USB_CTL_USBENSOFEN;
+
+  // Un-suspend the transceiver and disable weak pulldowns.
+  USB0_USBCTRL = 0;
+  // And enable the D+ pullup which indicates we're a full-speed device.
+  USB0_CONTROL = USB_CONTROL_DPPULLUPNONOTG;
+
+  // Enable the reset interrupt (which is the first one we care about).
+  USB0_INTEN = USB_INTEN_USBRSTEN;
+
+  dma_memory_barrier();
+  NVIC_ENABLE_IRQ(IRQ_USBOTG);
+}
+
+void usb_isr(void) {
+  UsbDevice *const usb0_device = global_usb0_device;
+  if (usb0_device == nullptr) {
+    NVIC_DISABLE_IRQ(IRQ_USBOTG);
+  } else {
+    usb0_device->HandleInterrupt();
+  }
+}
+
+void UsbDevice::ClearInterrupts() {
+  USB0_ISTAT = USB_ISTAT_ATTACH | USB_ISTAT_RESUME | USB_ISTAT_SLEEP |
+               USB_ISTAT_TOKDNE | USB_ISTAT_SOFTOK | USB_ISTAT_ERROR |
+               USB_ISTAT_USBRST;
+  USB0_ERRSTAT = USB_ERRSTAT_BTSERR | USB_ERRSTAT_DMAERR | USB_ERRSTAT_BTOERR |
+                 USB_ERRSTAT_DFN8 | USB_ERRSTAT_CRC16 | USB_ERRSTAT_CRC5EOF |
+                 USB_ERRSTAT_PIDERR;
+}
+
+void UsbDevice::HandleInterrupt() {
+  while (true) {
+    const uint32_t status = USB0_ISTAT;
+    if ((status & usb_enabled_interrupts()) == 0) {
+      return;
+    }
+
+    // If we just got a start-of-frame token, then ask all the functions what to
+    // do.
+    if (status & USB_ISTAT_SOFTOK) {
+      // TODO(Brian): Actually ask the functions, maybe only if we're
+      // configured.
+      USB0_ISTAT = USB_ISTAT_SOFTOK;
+    }
+
+    // If we just finished processing a token.
+    if (status & USB_ISTAT_TOKDNE) {
+      const uint8_t stat = USB0_STAT;
+      const int endpoint = G_USB_STAT_ENDP(stat);
+
+      if (endpoint == 0) {
+        HandleEndpoint0Token(stat);
+      } else {
+        BdtEntry *const bdt_entry = MutableBdtEntryFromStat(stat);
+        const UsbPid pid = G_USB_BD_PID(bdt_entry->buffer_descriptor);
+        UsbFunction *const function = endpoint_mapping_[endpoint];
+        if (function == nullptr) {
+          // Should never happen, so stall if we do get here somehow.
+          StallEndpoint(endpoint);
+        } else {
+          switch (pid) {
+            case UsbPid::kOut:
+              function->HandleOutFinished(endpoint, bdt_entry);
+              break;
+
+            case UsbPid::kIn:
+              function->HandleInFinished(
+                  endpoint, bdt_entry,
+                  (stat & M_USB_STAT_ODD) ? EvenOdd::kOdd : EvenOdd::kEven);
+              break;
+
+            case UsbPid::kSetup:
+            default:
+              // Should never happen, so stall if we do get here somehow.
+              StallEndpoint(endpoint);
+              break;
+          }
+        }
+      }
+
+      USB0_ISTAT = USB_ISTAT_TOKDNE;
+    }
+
+    if (status & USB_ISTAT_USBRST) {
+      // Use DATA0 for all endpoints.
+      USB0_CTL = USB_CTL_ODDRST;
+      endpoint0_tx_odd_ = EvenOdd::kEven;
+      endpoint0_tx_toggle_ = Data01::kData0;
+
+      for (UsbFunction *function : functions_) {
+        function->HandleReset();
+      }
+
+      MutableBdtEntry(0, Direction::kRx, EvenOdd::kEven)->buffer_descriptor =
+          M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kEndpoint0MaxSize);
+      MutableBdtEntry(0, Direction::kRx, EvenOdd::kEven)->address =
+          &endpoint0_receive_buffer_[0][0];
+
+      MutableBdtEntry(0, Direction::kRx, EvenOdd::kOdd)->buffer_descriptor =
+          M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kEndpoint0MaxSize);
+      MutableBdtEntry(0, Direction::kRx, EvenOdd::kOdd)->address =
+          &endpoint0_receive_buffer_[1][0];
+
+      MutableBdtEntry(0, Direction::kTx, EvenOdd::kEven)->buffer_descriptor = 0;
+      MutableBdtEntry(0, Direction::kTx, EvenOdd::kOdd)->buffer_descriptor = 0;
+
+      USB0_ENDPT0 = USB_ENDPT_EPRXEN | USB_ENDPT_EPTXEN | USB_ENDPT_EPHSHK;
+
+      ClearInterrupts();
+
+      // Set the address to 0 for enumeration.
+      USB0_ADDR = 0;
+      new_address_ = 0;
+
+      endpoint0_data_ = nullptr;
+      endpoint0_data_left_ = 0;
+
+      USB0_INTEN = usb_enabled_interrupts();
+      USB0_ERREN = USB_ERREN_BTSERREN | USB_ERREN_DMAERREN |
+                   USB_ERREN_BTOERREN | USB_ERREN_DFN8EN | USB_ERREN_CRC16EN |
+                   USB_ERREN_CRC5EOFEN | USB_ERREN_PIDERREN;
+
+      // Start the peripheral going.
+      dma_memory_barrier();
+      USB0_CTL = USB_CTL_USBENSOFEN;
+
+      continue;
+    }
+
+    // TODO(Brian): Handle errors more intelligently.
+    if (status & USB_ISTAT_ERROR) {
+      const uint8_t error = USB0_ERRSTAT;
+      USB0_ERRSTAT = error;
+      USB0_ISTAT = USB_ISTAT_ERROR;
+    }
+  }
+}
+
+void UsbDevice::HandleEndpoint0Token(const uint8_t stat) {
+  BdtEntry *const bdt_entry = MutableBdtEntryFromStat(stat);
+  const UsbPid pid = G_USB_BD_PID(bdt_entry->buffer_descriptor);
+  switch (pid) {
+    case UsbPid::kSetup:
+      // Unstall it if it was previously stalled.
+      USB0_ENDPT0 = USB_ENDPT_EPRXEN | USB_ENDPT_EPTXEN | USB_ENDPT_EPHSHK;
+
+      SetupPacket setup_packet;
+      memcpy(&setup_packet, bdt_entry->address, sizeof(setup_packet));
+
+      // Give the buffer back now.
+      dma_memory_barrier();
+      // Next IN and OUT packet for this endpoint (data stage/status stage)
+      // should both be DATA1.
+      // TODO(Brian): Does this actually deal with received toggles correctly?
+      bdt_entry->buffer_descriptor =
+          M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kEndpoint0MaxSize);
+      MutableBdtEntryFromStat(stat ^ M_USB_STAT_ODD)->buffer_descriptor =
+          M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kEndpoint0MaxSize) |
+          M_USB_BD_DATA1;
+      endpoint0_tx_toggle_ = Data01::kData1;
+
+      // TODO(Brian): Tell the functions a new setup packet is starting.
+      // CdcTty: next_endpoint0_out_ = NextEndpoint0Out::kNone;
+
+      // Forget about any pending transactions on this endpoint. There shouldn't
+      // be any, so if we think there are something's out of sync and we should
+      // just drop it. Important to do this before clearing TXD_SUSPEND in
+      // USBx_CTL. Standard says "If a Setup transaction is received by an
+      // endpoint before a previously initiated control transfer is completed,
+      // the device must abort the current transfer/operation".
+      endpoint0_data_ = nullptr;
+      endpoint0_data_left_ = 0;
+      MutableBdtEntry(0, Direction::kTx, EvenOdd::kEven)->buffer_descriptor = 0;
+      MutableBdtEntry(0, Direction::kTx, EvenOdd::kOdd)->buffer_descriptor = 0;
+
+      HandleEndpoint0SetupPacket(setup_packet);
+
+      break;
+
+    case UsbPid::kOut:
+     for (UsbFunction *function : functions_) {
+       switch (function->HandleEndpoint0OutPacket(
+           bdt_entry->address, G_USB_BD_BC(bdt_entry->buffer_descriptor))) {
+          case SetupResponse::kIgnored:
+            break;
+          case SetupResponse::kHandled:
+            dma_memory_barrier();
+            bdt_entry->buffer_descriptor =
+                M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kEndpoint0MaxSize);
+            return;
+          case SetupResponse::kStall:
+            bdt_entry->buffer_descriptor =
+                M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kEndpoint0MaxSize);
+            StallEndpoint0();
+            return;
+        }
+      }
+      bdt_entry->buffer_descriptor =
+          M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kEndpoint0MaxSize);
+      StallEndpoint0();
+      return;
+
+    case UsbPid::kIn:
+      // The functions are allowed to queue data in {endpoint0_data_,
+      // endpoint0_data_left_}, so this case deals with sending their data too.
+
+      // An IN transaction completed, so set up for the next one if appropriate.
+      if (!BufferEndpoint0TxPacket()) {
+        // After we're done, any further requests from the host should result in
+        // stalls (until the next setup token).
+        // TODO(Brian): Keep track of which direction it is and how much we've
+        // finished so we actually know when to stall it, both here and for
+        // kOut tokens.
+        //StallEndpoint0();
+      }
+
+      // If we have a new address, there is nothing left in the setup request
+      // besides a single IN packet forming the status stage, so we know the
+      // changes must be done now.
+      if (new_address_ != 0) {
+        USB0_ADDR = new_address_;
+        new_address_ = 0;
+      }
+
+      break;
+
+    default:
+      // Should never happen, but give the buffer back anyways if necessary.
+      if (!(bdt_entry->buffer_descriptor & M_USB_BD_OWN)) {
+        bdt_entry->buffer_descriptor =
+            M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kEndpoint0MaxSize);
+      }
+      break;
+  }
+
+  // Clear the TXD_SUSPEND flag.
+  dma_memory_barrier();
+  USB0_CTL = USB_CTL_USBENSOFEN;
+}
+
+void UsbDevice::HandleEndpoint0SetupPacket(const SetupPacket &setup_packet) {
+  const bool in = setup_packet.request_type & M_SETUP_REQUEST_TYPE_IN;
+  const uint8_t recipient =
+      G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type);
+  switch (G_SETUP_REQUEST_TYPE_TYPE(setup_packet.request_type)) {
+    case SetupRequestType::kStandard:
+      switch (setup_packet.request) {
+        case standard_setup_requests::kSetAddress:
+          if (in || recipient != standard_setup_recipients::kDevice ||
+              setup_packet.index != 0 || setup_packet.length != 0) {
+            break;
+          }
+          new_address_ = setup_packet.value;
+          SendEmptyEndpoint0Packet();
+          return;
+
+        case standard_setup_requests::kSetConfiguration:
+          if (in || recipient != standard_setup_recipients::kDevice ||
+              setup_packet.index != 0 || setup_packet.length != 0) {
+            break;
+          }
+          configuration_ = setup_packet.value;
+
+          // No need to mess with endpoint0_tx_toggle_ because we reset it with
+          // each setup packet anyways.
+
+          for (int endpoint = 0;
+               endpoint < static_cast<int>(endpoint_mapping_.size());
+               ++endpoint) {
+            if (endpoint_mapping_[endpoint]) {
+              endpoint_mapping_[endpoint]->HandleConfigured(endpoint);
+            }
+          }
+
+          SendEmptyEndpoint0Packet();
+          return;
+
+        case standard_setup_requests::kClearFeature:
+          if (in || setup_packet.length != 0) {
+            break;
+          }
+          if (recipient == standard_setup_recipients::kEndpoint &&
+              setup_packet.value == standard_feature_selectors::kEndpointHalt) {
+            const int endpoint =
+                G_SETUP_REQUEST_INDEX_ENDPOINT(setup_packet.index);
+            // Our endpoint 0 doesn't support the halt feature because that's
+            // weird and not recommended by the standard.
+            if (endpoint == 0) {
+              break;
+            }
+            if (endpoint >= number_endpoints()) {
+              break;
+            }
+            USB0_ENDPTn(endpoint) &= ~USB_ENDPT_EPSTALL;
+            if (endpoint_mapping_[endpoint] != nullptr) {
+              endpoint_mapping_[endpoint]->HandleConfigured(endpoint);
+            }
+            SendEmptyEndpoint0Packet();
+            return;
+          }
+          // We should never get kDeviceRemoteWakeup because we don't advertise
+          // support for it in our configuration descriptors.
+          // We should never get kTestMode because we're not high-speed.
+          break;
+
+        case standard_setup_requests::kSetFeature:
+          if (in || setup_packet.length != 0) {
+            break;
+          }
+          if (recipient == standard_setup_recipients::kEndpoint &&
+              setup_packet.value == standard_feature_selectors::kEndpointHalt) {
+            const int endpoint =
+                G_SETUP_REQUEST_INDEX_ENDPOINT(setup_packet.index);
+            // Our endpoint 0 doesn't support the halt feature because that's
+            // weird and not recommended by the standard.
+            if (endpoint == 0) {
+              break;
+            }
+            if (endpoint >= number_endpoints()) {
+              break;
+            }
+            StallEndpoint(endpoint);
+            // TODO(Brian): Tell the appropriate function it's now stalled.
+            SendEmptyEndpoint0Packet();
+            return;
+          }
+          // We should never get kDeviceRemoteWakeup because we don't advertise
+          // support for it in our configuration descriptors.
+          // We should never get kTestMode because we're not high-speed.
+          break;
+
+        case standard_setup_requests::kGetConfiguration:
+          if (!in || recipient != standard_setup_recipients::kDevice ||
+              setup_packet.index != 0 || setup_packet.length != 1) {
+            break;
+          }
+          endpoint0_transmit_buffer_[0] = configuration_;
+          QueueEndpoint0Data(endpoint0_transmit_buffer_, 1);
+          return;
+
+        case standard_setup_requests::kGetInterface:
+          if (!in || recipient != standard_setup_recipients::kInterface ||
+              setup_packet.value != 0 || setup_packet.length != 1) {
+            break;
+          }
+          // Standard says it's unspecified in the default state and must
+          // respond with an error in the address state, so just do an error for
+          // both of them.
+          if (configuration_ == 0) {
+            break;
+          }
+          // TODO(Brian): Ask the appropriate function what alternate setting
+          // the interface has, and stall if there isn't one.
+          endpoint0_transmit_buffer_[0] = 0;
+          QueueEndpoint0Data(endpoint0_transmit_buffer_, 1);
+          return;
+
+        case standard_setup_requests::kSetInterface:
+          if (in || recipient != standard_setup_recipients::kInterface ||
+              setup_packet.length != 0) {
+            break;
+          }
+          // Standard says it's unspecified in the default state and must
+          // respond with an error in the address state, so just do an error for
+          // both of them.
+          if (configuration_ == 0) {
+            break;
+          }
+
+          // TODO(Brian): Pass to the appropriate function instead.
+          if (setup_packet.value != 0) {
+            break;
+          }
+          SendEmptyEndpoint0Packet();
+          return;
+
+        case standard_setup_requests::kGetStatus:
+          if (!in || setup_packet.value != 0 || setup_packet.length != 2) {
+            break;
+          }
+          if (recipient == standard_setup_recipients::kDevice) {
+            if (setup_packet.index != 0) {
+              break;
+            }
+            // Say that we're currently self powered.
+            endpoint0_transmit_buffer_[0] = 1;
+            endpoint0_transmit_buffer_[1] = 0;
+            QueueEndpoint0Data(endpoint0_transmit_buffer_, 2);
+            return;
+          }
+          if ((recipient == standard_setup_recipients::kInterface &&
+               setup_packet.index == 0) ||
+              (recipient == standard_setup_recipients::kEndpoint &&
+               G_SETUP_REQUEST_INDEX_ENDPOINT(setup_packet.index) == 0)) {
+            endpoint0_transmit_buffer_[0] = 0;
+            endpoint0_transmit_buffer_[1] = 0;
+            QueueEndpoint0Data(endpoint0_transmit_buffer_, 2);
+            return;
+          }
+          // Standard says it's unspecified in the default state and must
+          // respond with an error in the address state, so just do an error
+          // for both of them.
+          if (configuration_ == 0) {
+            break;
+          }
+
+          if (recipient == standard_setup_recipients::kInterface) {
+            // TODO(Brian): Check if it's actually an interface we have?
+            endpoint0_transmit_buffer_[0] = 0;
+            endpoint0_transmit_buffer_[1] = 0;
+            QueueEndpoint0Data(endpoint0_transmit_buffer_, 2);
+            return;
+          }
+
+          if (recipient == standard_setup_recipients::kEndpoint) {
+            const int endpoint =
+                G_SETUP_REQUEST_INDEX_ENDPOINT(setup_packet.index);
+            // TODO(Brian): Check if it's actually an endpoint we have?
+            if (USB0_ENDPTn(endpoint) & USB_ENDPT_EPSTALL) {
+              endpoint0_transmit_buffer_[0] = 1;
+            } else {
+              endpoint0_transmit_buffer_[0] = 0;
+            }
+            endpoint0_transmit_buffer_[1] = 0;
+            QueueEndpoint0Data(endpoint0_transmit_buffer_, 2);
+            return;
+          }
+          break;
+
+        case standard_setup_requests::kSetDescriptor:
+          // Not implementing anything for this.
+          break;
+
+        case standard_setup_requests::kSynchFrame:
+          // We don't implement any classes which use this.
+          break;
+
+        case standard_setup_requests::kGetDescriptor:
+          if (!in || recipient != standard_setup_recipients::kDevice) {
+            break;
+          }
+          const uint8_t descriptor_type_byte = (setup_packet.value >> 8) & 0xFF;
+          if (descriptor_type_byte < kUsbDescriptorTypeMin ||
+              descriptor_type_byte > kUsbDescriptorTypeMax) {
+            break;
+          }
+          const UsbDescriptorType descriptor_type =
+              static_cast<UsbDescriptorType>(descriptor_type_byte);
+          const uint8_t descriptor_index = setup_packet.value & 0xFF;
+          switch (descriptor_type) {
+            case UsbDescriptorType::kDevice:
+              if (setup_packet.index != 0 || descriptor_index != 0) {
+                break;
+              }
+              QueueEndpoint0Data(
+                  device_descriptor_list_.data_.data(),
+                  ::std::min<int>(setup_packet.length,
+                                  device_descriptor_list_.data_.size()));
+              return;
+
+            case UsbDescriptorType::kConfiguration:
+              if (setup_packet.index != 0 || descriptor_index != 0) {
+                break;
+              }
+              QueueEndpoint0Data(
+                  config_descriptor_list_.data_.data(),
+                  ::std::min<int>(setup_packet.length,
+                                  config_descriptor_list_.data_.size()));
+              return;
+
+            case UsbDescriptorType::kString:
+              if (descriptor_index != 0 && setup_packet.index != english_us_code()) {
+                break;
+              }
+              if (descriptor_index >= strings_.size()) {
+                break;
+              }
+              QueueEndpoint0Data(
+                  strings_[descriptor_index].data(),
+                  ::std::min<int>(setup_packet.length,
+                                  strings_[descriptor_index].size()));
+              return;
+
+            default:
+              // TODO(Brian): Handle other types of descriptor too.
+              break;
+          }
+      }
+      break;
+
+    default:
+      for (UsbFunction *function : functions_) {
+        switch (function->HandleEndpoint0SetupPacket(setup_packet)) {
+          case SetupResponse::kIgnored:
+            continue;
+          case SetupResponse::kHandled:
+            return;
+          case SetupResponse::kStall:
+            break;
+        }
+        break;
+      }
+      break;
+  }
+
+  StallEndpoint0();
+}
+
+// We're supposed to continue returning stalls until the next kSetup packet.
+// Code might continue putting stuff in the TX buffers, but the hardware won't
+// actually send it as long as the EPSTALL bit is set.
+void UsbDevice::StallEndpoint0() {
+  USB0_ENDPT0 = USB_ENDPT_EPSTALL | USB_ENDPT_EPRXEN | USB_ENDPT_EPTXEN |
+                USB_ENDPT_EPHSHK;
+}
+
+bool UsbDevice::BufferEndpoint0TxPacket() {
+  if (endpoint0_data_ == nullptr) {
+    return false;
+  }
+
+  const int to_transmit = ::std::min(endpoint0_data_left_, kEndpoint0MaxSize);
+  BdtEntry *const tx_bdt_entry =
+      MutableBdtEntry(0, Direction::kTx, endpoint0_tx_odd_);
+  // const_cast is safe because the hardware is only going to read from
+  // this, not write.
+  tx_bdt_entry->address =
+      const_cast<void *>(static_cast<const void *>(endpoint0_data_));
+  dma_memory_barrier();
+  tx_bdt_entry->buffer_descriptor =
+      V_USB_BD_BC(to_transmit) | static_cast<uint32_t>(endpoint0_tx_toggle_) |
+      M_USB_BD_OWN | M_USB_BD_DTS;
+
+  endpoint0_tx_odd_ = EvenOddInverse(endpoint0_tx_odd_);
+  endpoint0_tx_toggle_ = Data01Inverse(endpoint0_tx_toggle_);
+
+  endpoint0_data_ += to_transmit;
+  endpoint0_data_left_ -= to_transmit;
+  if (to_transmit < kEndpoint0MaxSize) {
+    endpoint0_data_ = nullptr;
+  }
+
+  return true;
+}
+
+void UsbDevice::SendEmptyEndpoint0Packet() {
+  // Really doesn't matter what we put here as long as it's not nullptr.
+  endpoint0_data_ = reinterpret_cast<char *>(this);
+  endpoint0_data_left_ = 0;
+  BufferEndpoint0TxPacket();
+}
+
+void UsbDevice::QueueEndpoint0Data(const char *data, int size) {
+  endpoint0_data_ = data;
+  endpoint0_data_left_ = size;
+  // There are 2 TX buffers, so fill them both up.
+  BufferEndpoint0TxPacket();
+  BufferEndpoint0TxPacket();
+}
+
+void UsbDevice::StallEndpoint(int endpoint) {
+  for (Direction direction : {Direction::kTx, Direction::kRx}) {
+    for (EvenOdd odd : {EvenOdd::kOdd, EvenOdd::kEven}) {
+      MutableBdtEntry(endpoint, direction, odd)->buffer_descriptor = 0;
+      dma_memory_barrier();
+      MutableBdtEntry(endpoint, direction, odd)->address = nullptr;
+    }
+  }
+  USB0_ENDPTn(endpoint) |= USB_ENDPT_EPSTALL;
+}
+
+void UsbDevice::ConfigureEndpointFor(int endpoint, bool rx, bool tx,
+                                     bool handshake) {
+  uint8_t control = 0;
+  if (rx) {
+    control |= USB_ENDPT_EPRXEN;
+  }
+  if (tx) {
+    control |= USB_ENDPT_EPTXEN;
+  }
+  if (handshake) {
+    control |= USB_ENDPT_EPHSHK;
+  }
+  USB0_ENDPTn(endpoint) = control;
+}
+
+int UsbFunction::AddEndpoint() {
+  const int r = device_->endpoint_mapping_.size();
+  assert(r < number_endpoints());
+  device_->endpoint_mapping_.push_back(this);
+  return r;
+}
+
+int UsbFunction::AddInterface() {
+  const int r = device_->interface_mapping_.size();
+  // bInterfaceNumber is only one byte.
+  assert(r < 255);
+  device_->interface_mapping_.push_back(this);
+  return r;
+}
+
+void UsbDevice::SetBdtEntry(int endpoint, Direction direction, EvenOdd odd,
+                            BdtEntry bdt_entry) {
+  *MutableBdtEntry(endpoint, direction, odd) = bdt_entry;
+}
+
+}  // namespace teensy
+}  // namespace frc971
diff --git a/motors/usb/usb.h b/motors/usb/usb.h
new file mode 100644
index 0000000..1322ce1
--- /dev/null
+++ b/motors/usb/usb.h
@@ -0,0 +1,506 @@
+#ifndef MOTORS_USB_USB_H_
+#define MOTORS_USB_USB_H_
+
+#include <assert.h>
+#include <string>
+#include <vector>
+#include <memory>
+
+#include "aos/common/macros.h"
+#include "motors/core/kinetis.h"
+#include "motors/usb/constants.h"
+
+namespace frc971 {
+namespace teensy {
+
+// A sufficient memory barrier between writing some data and telling the USB
+// hardware to read it or having the USB hardware say some data is readable and
+// actually reading it.
+static inline void dma_memory_barrier() {
+  __asm__ __volatile__("" :: : "memory");
+}
+
+// Aligned for faster access via memcpy etc.
+typedef void *DataPointer __attribute__((aligned(4)));
+
+// An entry in the Buffer Descriptor Table.
+struct BdtEntry {
+  uint32_t buffer_descriptor;
+  DataPointer address;
+};
+
+#define V_USB_BD_BC(value) \
+  static_cast<uint32_t>(static_cast<uint32_t>(value) << 16)
+#define G_USB_BD_BC(bd) (((bd) >> 16) & UINT32_C(0x3FF))
+#define M_USB_BD_OWN UINT32_C(1 << 7)
+#define M_USB_BD_DATA1 UINT32_C(1 << 6)
+static_assert(static_cast<uint32_t>(Data01::kData1) == M_USB_BD_DATA1,
+              "Wrong value");
+#define M_USB_BD_KEEP UINT32_C(1 << 5)
+#define M_USB_BD_NINC UINT32_C(1 << 4)
+#define M_USB_BD_DTS UINT32_C(1 << 3)
+#define M_USB_BD_STALL UINT32_C(1 << 2)
+#define V_USB_BD_PID(value) \
+  static_cast<uint32_t>(static_cast<uint32_t>(value) << 2)
+#define G_USB_BD_PID(bd) static_cast<UsbPid>(((bd) >> 2) & UINT32_C(0xF))
+
+#define G_USB_STAT_ENDP(stat) (((stat) >> 4) & UINT32_C(0xF))
+#define M_USB_STAT_TX UINT32_C(1 << 3)
+#define M_USB_STAT_ODD UINT32_C(1 << 2)
+
+// The various types of descriptors defined in the standard for retrieval via
+// GetDescriptor.
+static constexpr uint8_t kUsbDescriptorTypeMin = 1;
+static constexpr uint8_t kUsbDescriptorTypeMax = 11;
+enum class UsbDescriptorType : uint8_t {
+  kDevice = 1,
+  kConfiguration = 2,
+  kString = 3,
+  kInterface = 4,
+  kEndpoint = 5,
+  kDeviceQualifier = 6,
+  kOtherSpeedConfiguration = 7,
+  kInterfacePower = 8,
+  kOtg = 9,
+  kDebug = 10,
+  kInterfaceAssociation = 11,
+};
+
+// The class-specific descriptor types.
+enum class UsbClassDescriptorType : uint8_t {
+  kDevice = 0x21,
+  kConfiguration = 0x22,
+  kString = 0x23,
+  kInterface = 0x24,
+  kEndpoint = 0x25,
+};
+
+// The names of the setup request types from the standard.
+enum class SetupRequestType {
+  kStandard = 0,
+  kClass = 1,
+  kVendor = 2,
+  kReserved = 3,
+};
+
+// Set means device-to-host, clear means host-to-device.
+#define M_SETUP_REQUEST_TYPE_IN UINT8_C(1 << 7)
+#define G_SETUP_REQUEST_TYPE_TYPE(type) \
+  static_cast<SetupRequestType>(((type) >> 5) & UINT8_C(3))
+#define G_SETUP_REQUEST_TYPE_RECIPIENT(type) ((type)&UINT8_C(0x1F))
+#define G_SETUP_REQUEST_INDEX_ENDPOINT(index) ((index)&UINT8_C(0x7F))
+
+// The names of the standard recipients for setup requests.
+namespace standard_setup_recipients {
+constexpr int kDevice = 0;
+constexpr int kInterface = 1;
+constexpr int kEndpoint = 2;
+constexpr int kOther = 3;
+}  // namespace standard_setup_recipients
+
+class UsbFunction;
+
+// Allows building up a list of descriptors. This supports a much nicer API than
+// the usual "hard-code a char[] with all the sizes and offsets at compile
+// time". Space for each descriptor is reserved, and then it may be filled out
+// from beginning to end at any time.
+//
+// An instance is the thing that the GetDescriptor operation sends to the host.
+// This is not the concept that the core and class standards call "Foo
+// Descriptor" etc; see Descriptor for that.
+class UsbDescriptorList {
+ public:
+  // Represents a single descriptor. All of the contents must be written before
+  // this object is destroyed.
+  //
+  // Create one via UsbDescriptorList::CreateDescriptor.
+  class Descriptor {
+   public:
+    // All of the allocated space must be filled first.
+    ~Descriptor() {
+      if (descriptor_list_ == nullptr) {
+        return;
+      }
+      // Verify we wrote all the bytes first.
+      assert(next_index_ == end_index_);
+      --descriptor_list_->open_descriptors_;
+    }
+
+    void AddUint16(uint16_t value) {
+      AddByte(value & 0xFF);
+      AddByte((value >> 8) & 0xFF);
+    }
+
+    void AddByte(uint8_t value) {
+      assert(next_index_ < end_index_);
+      data()[next_index_] = value;
+      ++next_index_;
+    }
+
+    // Overwrites an already-written byte.
+    void SetByte(int index, uint8_t value) {
+      assert(index + start_index_ < end_index_);
+      data()[index + start_index_] = value;
+    }
+
+   private:
+    Descriptor(UsbDescriptorList *descriptor_list, int start_index,
+               int end_index)
+        : descriptor_list_(descriptor_list),
+          start_index_(start_index),
+          end_index_(end_index),
+          next_index_(start_index_) {}
+
+    char *data() const {
+      return &descriptor_list_->data_[0];
+    }
+
+    UsbDescriptorList *const descriptor_list_;
+    const int start_index_, end_index_;
+    int next_index_;
+
+    friend class UsbDescriptorList;
+
+    DISALLOW_COPY_AND_ASSIGN(Descriptor);
+  };
+
+  UsbDescriptorList() = default;
+  ~UsbDescriptorList() = default;
+
+  // Creates a new descriptor at the end of the list.
+  // length is the number of bytes, including the length byte.
+  // descriptor_type is the descriptor type, which is the second byte after the
+  // length.
+  ::std::unique_ptr<Descriptor> CreateDescriptor(
+      uint8_t length, UsbDescriptorType descriptor_type) {
+    return CreateDescriptor(length, static_cast<uint8_t>(descriptor_type));
+  }
+
+  ::std::unique_ptr<Descriptor> CreateDescriptor(
+      uint8_t length, UsbClassDescriptorType descriptor_type) {
+    assert(data_.size() > 0);
+    return CreateDescriptor(length, static_cast<uint8_t>(descriptor_type));
+  }
+
+  void CheckFinished() const { assert(open_descriptors_ == 0); }
+
+  int CurrentSize() const { return data_.size(); }
+
+ private:
+  ::std::unique_ptr<Descriptor> CreateDescriptor(uint8_t length,
+                                                 uint8_t descriptor_type) {
+    const int start_index = data_.size();
+    const int end_index = start_index + length;
+    data_.resize(end_index);
+    ++open_descriptors_;
+    auto r = ::std::unique_ptr<Descriptor>(
+        new Descriptor(this, start_index, end_index));
+    r->AddByte(length);           // bLength
+    r->AddByte(descriptor_type);  // bDescriptorType
+    return r;
+  }
+
+  int open_descriptors_ = 0;
+
+  ::std::string data_;
+
+  friend class UsbDevice;
+
+  DISALLOW_COPY_AND_ASSIGN(UsbDescriptorList);
+};
+
+extern "C" void usb_isr(void);
+
+// USB state events are managed by asking each function if it wants to handle
+// them, sequentially. For the small number of functions which can be
+// practically supported with the limited number of endpoints, this performs
+// better than fancier things like hash maps.
+
+// Manages one of the Teensy's USB peripherals as a USB slave device.
+//
+// This supports being a composite device with multiple functions.
+//
+// Attaching functions etc is called "setup", and must be completed before
+// Initialize() is called.
+//
+// Detaching functions is called "teardown" and must happen after Shutdown().
+// TODO(Brian): Implement Shutdown().
+class UsbDevice final {
+ public:
+  // Represents the data that comes with a UsbPid::kSetup.
+  // Note that the order etc is important because we memcpy into this.
+  struct SetupPacket {
+    uint8_t request_type;  // bmRequestType
+    uint8_t request;       // bRequest
+    uint16_t value;        // wValue
+    uint16_t index;        // wIndex
+    uint16_t length;       // wLength
+  } __attribute__((aligned(4)));
+  static_assert(sizeof(SetupPacket) == 8, "wrong size");
+
+  enum class SetupResponse {
+    // Indicates this function doesn't recognize the setup packet.
+    kIgnored,
+
+    // Indicates the endpoint should be stalled.
+    //
+    // Don't return this if the packet is for another function.
+    kStall,
+
+    // Indicates this setup packet was handled. Functions must avoid eating
+    // packets intended for other functions.
+    kHandled,
+  };
+
+  static constexpr int kEndpoint0MaxSize = 64;
+
+  // The only language code we support.
+  static constexpr uint16_t english_us_code() { return 0x0409; }
+
+  UsbDevice(int index, uint16_t vendor_id, uint16_t product_id);
+  ~UsbDevice();
+
+  // Ends setup and starts being an actual USB device.
+  void Initialize();
+
+  // Adds a string to the table and returns its index.
+  //
+  // For simplicity, we only support strings with english_us_code().
+  //
+  // May only be called during setup.
+  int AddString(const ::std::string &string) {
+    assert(!is_set_up_);
+    const int r = strings_.size();
+    strings_.emplace_back(string.size() * 2 + 2, '\0');
+    strings_.back()[0] = 2 + string.size() * 2;
+    strings_.back()[1] = static_cast<uint8_t>(UsbDescriptorType::kString);
+    for (size_t i = 0; i < string.size(); ++i) {
+      strings_.back()[i * 2 + 2] = string[i];
+    }
+    return r;
+  }
+
+  // Sets the manufacturer string.
+  //
+  // May only be called during setup.
+  void SetManufacturer(const ::std::string &string) {
+    device_descriptor_->SetByte(14, AddString(string));  // iManufacturer
+  }
+
+  // Sets the product string.
+  //
+  // May only be called during setup.
+  void SetProduct(const ::std::string &string) {
+    device_descriptor_->SetByte(15, AddString(string));  // iProduct
+  }
+
+  // Sets the serial number string.
+  //
+  // May only be called during setup.
+  void SetSerialNumber(const ::std::string &string) {
+    device_descriptor_->SetByte(16, AddString(string));  // iSerialNumber
+  }
+
+  // Queues up an empty IN packet for endpoint 0. This is a common way to
+  // respond to various kinds of configuration commands.
+  //
+  // This may only be called from the appropriate function callbacks.
+  void SendEmptyEndpoint0Packet();
+
+  // Queues some data to send on endpoint 0. This includes putting the initial
+  // packets into the TX buffers.
+  //
+  // This may only be called from the appropriate function callbacks.
+  void QueueEndpoint0Data(const char *data, int size);
+
+  // Stalls an endpoint until it's cleared.
+  //
+  // This should only be called by or on behalf of the function which owns
+  // endpoint.
+  void StallEndpoint(int endpoint);
+
+  // Configures an endpoint to send and/or receive, with or without DATA0/DATA1
+  // handshaking. handshake should probably be true for everything except
+  // isochronous endpoints.
+  //
+  // This should only be called by or on behalf of the function which owns
+  // endpoint.
+  void ConfigureEndpointFor(int endpoint, bool rx, bool tx, bool handshake);
+
+  void SetBdtEntry(int endpoint, Direction direction, EvenOdd odd,
+                   BdtEntry bdt_entry);
+
+ private:
+  // Clears all pending interrupts.
+  void ClearInterrupts();
+
+  // Deals with an interrupt that has occured.
+  void HandleInterrupt();
+
+  // Processes a token on endpoint 0.
+  void HandleEndpoint0Token(uint8_t stat);
+
+  // Processes a setup packet on endpoint 0.
+  void HandleEndpoint0SetupPacket(const SetupPacket &setup_packet);
+
+  // Sets endpoint 0 to return STALL tokens. We clear this condition upon
+  // receiving the next SETUP token.
+  void StallEndpoint0();
+
+  // Places the first packet from {endpoint0_data_, endpoint0_data_left_} into
+  // the TX buffers (if there is any data). This may only be called when the
+  // next TX buffer is empty.
+  bool BufferEndpoint0TxPacket();
+
+  // Which USB peripheral this is.
+  const int index_;
+
+  // The string descriptors in order.
+  ::std::vector<::std::string> strings_;
+
+  // TODO(Brian): Refactor into something more generic, because I think this is
+  // shared with all non-isochronous endpoints?
+  Data01 endpoint0_tx_toggle_;
+  EvenOdd endpoint0_tx_odd_;
+  uint8_t endpoint0_receive_buffer_[2][kEndpoint0MaxSize]
+      __attribute__((aligned(4)));
+
+  // A temporary buffer for holding data to transmit on endpoint 0. Sometimes
+  // this is used and sometimes the data is sent directly from some other
+  // location (like for descriptors).
+  char endpoint0_transmit_buffer_[kEndpoint0MaxSize];
+
+  // The data we're waiting to send from endpoint 0. The data must remain
+  // constant until this transmission is done.
+  //
+  // When overwriting this, we ignore if it's already non-nullptr. The host is
+  // supposed to read all of the data before asking for more. If it doesn't do
+  // that, it will just get garbage data because it's unclear what it expects.
+  //
+  // Do note that endpoint0_data_ != nullptr && endpoint0_data_left_ == 0 is an
+  // important state. This means we're going to return a 0-length packet the
+  // next time the host asks. However, depending on the length it asked for,
+  // that might never happen.
+  const char *endpoint0_data_ = nullptr;
+  int endpoint0_data_left_ = 0;
+
+  // If non-0, the new address we're going to start using once the status stage
+  // of the current setup request is finished.
+  uint16_t new_address_ = 0;
+
+  UsbDescriptorList device_descriptor_list_;
+  UsbDescriptorList config_descriptor_list_;
+
+  ::std::unique_ptr<UsbDescriptorList::Descriptor> device_descriptor_,
+      config_descriptor_;
+
+  int configuration_ = 0;
+
+  bool is_set_up_ = false;
+
+  // The function which owns each endpoint.
+  ::std::vector<UsbFunction *> endpoint_mapping_;
+  // The function which owns each interface.
+  ::std::vector<UsbFunction *> interface_mapping_;
+  // All of the functions (without duplicates).
+  ::std::vector<UsbFunction *> functions_;
+
+  friend void usb_isr(void);
+  friend class UsbFunction;
+};
+
+// Represents a USB function. This consists of a set of descriptors and
+// interfaces.
+//
+// Each instance is a single function, so there can be multiple instances of the
+// same subclass in the same devices (ie two serial ports).
+class UsbFunction {
+ public:
+  UsbFunction(UsbDevice *device) : device_(device) {
+    device_->functions_.push_back(this);
+  }
+  virtual ~UsbFunction() = default;
+
+ protected:
+  using SetupResponse = UsbDevice::SetupResponse;
+
+  static constexpr uint8_t iad_descriptor_length() { return 8; }
+  static constexpr uint8_t interface_descriptor_length() { return 9; }
+  static constexpr uint8_t endpoint_descriptor_length() { return 7; }
+
+  static constexpr uint8_t m_endpoint_address_in() { return 1 << 7; }
+  static constexpr uint8_t m_endpoint_attributes_control() { return 0x00; }
+  static constexpr uint8_t m_endpoint_attributes_isochronous() { return 0x01; }
+  static constexpr uint8_t m_endpoint_attributes_bulk() { return 0x03; }
+  static constexpr uint8_t m_endpoint_attributes_interrupt() { return 0x03; }
+
+  // Adds a new endpoint and returns its index.
+  //
+  // Note that at least one descriptor for this newly created endpoint must be
+  // added via CreateConfigDescriptor.
+  //
+  // TODO(Brian): Does this hardware actually only support a single direction
+  // per endpoint number, or can it get a total of 30 endpoints max?
+  //
+  // May only be called during setup.
+  int AddEndpoint();
+
+  // Adds a new interface and returns its index.
+  //
+  // You'll probably want to put this new interface in at least one descriptor
+  // added via CreateConfigDescriptor.
+  //
+  // May only be called during setup.
+  int AddInterface();
+
+  // Adds a new descriptor in the configuration descriptor list. See
+  // UsbDescriptorList::CreateDescriptor for details.
+  //
+  // Note that the order of calls to this is highly significant. In general,
+  // this should only be called from Initialize().
+  //
+  // May only be called during setup.
+  template <typename T>
+  ::std::unique_ptr<UsbDescriptorList::Descriptor> CreateDescriptor(
+      uint8_t length, T descriptor_type) {
+    return device_->config_descriptor_list_.CreateDescriptor(length,
+                                                             descriptor_type);
+  }
+
+  UsbDevice *device() const { return device_; }
+
+ private:
+  virtual void Initialize() = 0;
+
+  virtual SetupResponse HandleEndpoint0SetupPacket(
+      const UsbDevice::SetupPacket & /*setup_packet*/) {
+    return SetupResponse::kIgnored;
+  }
+
+  virtual SetupResponse HandleEndpoint0OutPacket(void * /*data*/,
+                                                 int /*data_length*/) {
+    return SetupResponse::kIgnored;
+  }
+
+  virtual void HandleOutFinished(int endpoint, BdtEntry *bdt_entry) = 0;
+  virtual void HandleInFinished(int endpoint, BdtEntry *bdt_entry,
+                                EvenOdd odd) = 0;
+
+  // Called when a given interface is configured (aka "experiences a
+  // configuration event"). This means all rx and tx buffers have been cleared
+  // and should be filled as appropriate, starting from data0. Also,
+  // ConfigureEndpointFor should be called with the appropriate arguments.
+  virtual void HandleConfigured(int endpoint) = 0;
+
+  // Should reset everything to use the even buffers next.
+  virtual void HandleReset() = 0;
+
+  UsbDevice *const device_;
+
+  friend class UsbDevice;
+};
+
+}  // namespace teensy
+}  // namespace frc971
+
+#endif  // MOTORS_USB_USB_H_
diff --git a/motors/util.h b/motors/util.h
index 05edd9d..b215996 100644
--- a/motors/util.h
+++ b/motors/util.h
@@ -147,6 +147,8 @@
 #define dma_chN_isr(n) DO_CONCATENATE(dma_ch, n, _isr)
 #define IRQ_DMA_CHn(n) DO_CONCATENATE(IRQ_DMA, _CH, n)
 
+#define USB0_ENDPTn(n) (*(volatile uint8_t *)(0x400720C0 + ((n)*4)))
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/tools/ci/run-tests.sh b/tools/ci/run-tests.sh
index 98762e7..cac8dce 100755
--- a/tools/ci/run-tests.sh
+++ b/tools/ci/run-tests.sh
@@ -3,3 +3,4 @@
 
 bazel --batch test -c opt --curses=no --color=no --jobs=1 //...
 bazel --batch build -c opt --curses=no --color=no --jobs=1 //... --cpu=roborio
+bazel --batch build -c opt --curses=no --color=no --jobs=1 //motors/... --cpu=cortex-m4f
diff --git a/tools/cpp/CROSSTOOL b/tools/cpp/CROSSTOOL
index 7e6efdb..6e0c158 100644
--- a/tools/cpp/CROSSTOOL
+++ b/tools/cpp/CROSSTOOL
@@ -242,7 +242,6 @@
 
   compiler_flag: "-Wall"
   compiler_flag: "-Wextra"
-  compiler_flag: "-Wswitch-enum"
   compiler_flag: "-Wpointer-arith"
   compiler_flag: "-Wstrict-aliasing"
   compiler_flag: "-Wcast-qual"
@@ -489,7 +488,6 @@
 
   compiler_flag: "-Wall"
   compiler_flag: "-Wextra"
-  compiler_flag: "-Wswitch-enum"
   compiler_flag: "-Wpointer-arith"
   compiler_flag: "-Wstrict-aliasing"
   compiler_flag: "-Wcast-qual"
@@ -744,7 +742,6 @@
 
   compiler_flag: "-Wall"
   compiler_flag: "-Wextra"
-  compiler_flag: "-Wswitch-enum"
   compiler_flag: "-Wpointer-arith"
   compiler_flag: "-Wstrict-aliasing"
   compiler_flag: "-Wcast-qual"
@@ -889,10 +886,17 @@
   compiler_flag: "-D__STDC_FORMAT_MACROS"
   compiler_flag: "-D__STDC_CONSTANT_MACROS"
   compiler_flag: "-D__STDC_LIMIT_MACROS"
+
+  # Some identifiers for what MCU we're using.
   compiler_flag: "-D__MK64FX512__"
   compiler_flag: "-DF_CPU=120000000"
+
   compiler_flag: "-Wl,--gc-sections"
 
+  # Newlib's stdint.h does this, but GCC's freestanding stdint.h doesn't use
+  # newlib's so we have to do it manually...
+  compiler_flag: "-D__have_long32"
+
   # Make C++ compilation deterministic. Use linkstamping instead of these
   # compiler symbols.
   unfiltered_cxx_flag: "-Wno-builtin-macro-redefined"
@@ -925,10 +929,10 @@
   linker_flag: "-Tmotors/core/mk64fx512.ld"
 
   compiler_flag: "-fmessage-length=80"
+  compiler_flag: "-fmax-errors=20"
 
   compiler_flag: "-Wall"
   compiler_flag: "-Wextra"
-  compiler_flag: "-Wswitch-enum"
   compiler_flag: "-Wpointer-arith"
   compiler_flag: "-Wcast-qual"
   compiler_flag: "-Wwrite-strings"
@@ -953,10 +957,16 @@
   # Enable debug symbols.
   compiler_flag: "-g"
 
-  # Commons symbols are weird and not what we want, so just give multiple
+  # Common symbols are weird and not what we want, so just give multiple
   # declaration errors instead.
   compiler_flag: "-fno-common"
 
+  # We're not a hosted environment (no file IO, main is called from our code,
+  # etc).
+  compiler_flag: "-ffreestanding"
+  # However, we still want to optimize things like memcpy.
+  compiler_flag: "-fbuiltin"
+
   compilation_mode_flags {
     mode: OPT
 
@@ -993,13 +1003,16 @@
       action: 'c++-header-preprocessing'
       action: 'c++-module-compile'
       flag_group {
+        iterate_over: 'quote_include_paths'
         flag: '-iquote'
         flag: '%{quote_include_paths}'
       }
       flag_group {
+        iterate_over: 'include_paths'
         flag: '-I%{include_paths}'
       }
       flag_group {
+        iterate_over: 'system_include_paths'
         flag: '-iquote'
         flag: '%{system_include_paths}'
       }
diff --git a/tools/environments.bzl b/tools/environments.bzl
new file mode 100644
index 0000000..68e78de
--- /dev/null
+++ b/tools/environments.bzl
@@ -0,0 +1,5 @@
+# Contains some helpers for working with environments.
+
+mcu_cpus = [
+  "@//tools:cortex-m4f",
+]