Merge "Add footprints and packages I forgot for the LED board"
diff --git a/WORKSPACE b/WORKSPACE
index c896d86..f220d3c 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -105,3 +105,19 @@
   path = '/usr',
   build_file = 'debian/libusb.BUILD',
 )
+
+# Created by combining libusb-1.0-0_2%3a1.0.19-1_amd64,
+# libusb-1.0-0-dev_2%3a1.0.19-1, and libudev1_215-17+deb8u7.
+new_http_archive(
+  name = 'libusb_1_0',
+  build_file = 'debian/libusb-1.0.BUILD',
+  url = 'http://frc971.org/Build-Dependencies/libusb-1.0-1.0.19.tar.xz',
+  sha256 = '12acb30faacd10e9aa7f3a5e074701e167ce9bbd45694db37d13d55de5398816',
+)
+
+# Recompressed from libusb-1.0.21.7z.
+http_file(
+  name = 'libusb_1_0_windows',
+  url = 'http://frc971.org/Build-Dependencies/libusb-1.0.21-windows.tar.xz',
+  sha256 = 'fc2ba03992f343aabbaf9eb90559c6e00cdc6a2bd914d7cebea85857d5244015',
+)
diff --git a/debian/libusb-1.0.BUILD b/debian/libusb-1.0.BUILD
new file mode 100644
index 0000000..a307cec
--- /dev/null
+++ b/debian/libusb-1.0.BUILD
@@ -0,0 +1,14 @@
+cc_library(
+  name = 'libusb_1_0',
+  visibility = ['//visibility:public'],
+  hdrs = [
+    'usr/include/libusb-1.0/libusb.h',
+  ],
+  srcs = [
+    'usr/lib/x86_64-linux-gnu/libusb-1.0.so',
+  ],
+  includes = [
+    'usr/include',
+  ],
+  restricted_to = ['@//tools:k8'],
+)
diff --git a/motors/pistol_grip/BUILD b/motors/pistol_grip/BUILD
index b1239c5..9eda585 100644
--- a/motors/pistol_grip/BUILD
+++ b/motors/pistol_grip/BUILD
@@ -8,10 +8,12 @@
   ],
   deps = [
     '//motors:util',
+    '//motors/peripheral:can',
     '//motors/core',
     '//motors/usb',
     '//motors/usb:cdc',
     '//motors/usb:hid',
+    '//motors/usb:interrupt_out',
   ],
   restricted_to = mcu_cpus,
 )
@@ -20,3 +22,37 @@
   name = 'drivers_station',
   restricted_to = mcu_cpus,
 )
+
+cc_binary(
+  name = 'usb_forward_linux',
+  srcs = [
+    'usb_forward.cc',
+  ],
+  deps = [
+    # Don't add anything else here. :usb_forward_windows still has to build it
+    # without any other dependencies.
+    '@libusb_1_0',
+  ],
+  restricted_to = ['//tools:k8'],
+)
+
+genrule(
+  name = 'usb_forward_windows',
+  outs = [
+    'usb_forward.exe',
+  ],
+  srcs = [
+    'usb_forward.cc',
+    '@libusb_1_0_windows//file',
+  ],
+  tools = [
+    'usb_forward_windows_build.sh',
+  ],
+  cmd = ' '.join([
+    '$(location usb_forward_windows_build.sh)',
+    '$(location usb_forward.cc)',
+    '$(location @libusb_1_0_windows//file)',
+    '$@',
+  ]),
+  output_to_bindir = True,
+)
diff --git a/motors/pistol_grip/drivers_station.cc b/motors/pistol_grip/drivers_station.cc
index ecc5347..4c68afc 100644
--- a/motors/pistol_grip/drivers_station.cc
+++ b/motors/pistol_grip/drivers_station.cc
@@ -6,9 +6,11 @@
 
 #include "motors/core/time.h"
 #include "motors/core/kinetis.h"
+#include "motors/peripheral/can.h"
 #include "motors/usb/usb.h"
 #include "motors/usb/cdc.h"
 #include "motors/usb/hid.h"
+#include "motors/usb/interrupt_out.h"
 #include "motors/util.h"
 
 namespace frc971 {
@@ -97,10 +99,20 @@
   }
 }
 
-void MoveJoysticks(teensy::HidFunction *joysticks) {
+void MoveJoysticks(teensy::HidFunction *joysticks,
+                   teensy::InterruptOut *interrupt_out) {
   static uint8_t x_axis = 0, y_axis = 97, z_axis = 5, rz_axis = 8;
   while (true) {
     {
+      char buffer[64];
+      if (interrupt_out->ReceiveData(buffer) > 0) {
+        if (buffer[0]) {
+          GPIOC_PCOR = 1 << 5;
+        } else {
+          GPIOC_PSOR = 1 << 5;
+        }
+      }
+
       DisableInterrupts disable_interrupts;
       uint8_t buttons = 0;
       if (x_axis % 8u) {
@@ -118,6 +130,29 @@
   }
 }
 
+void ForwardJoystickData(teensy::HidFunction *joysticks) {
+  uint32_t last_command_time = micros();
+  while (true) {
+    uint8_t data[8];
+    int length;
+    can_receive_command(data, &length);
+    if (length == 3) {
+      last_command_time = micros();
+      DisableInterrupts disable_interrupts;
+      joysticks->UpdateReport(data, 3, disable_interrupts);
+    }
+
+    static constexpr uint32_t kTimeout = 10000;
+    if (!time_after(time_add(last_command_time, kTimeout), micros())) {
+      // Avoid wrapping back into the valid range.
+      last_command_time = time_subtract(micros(), kTimeout);
+      uint8_t zeros[] = {0, 0, 0x80};
+      DisableInterrupts disable_interrupts;
+      joysticks->UpdateReport(zeros, 3, disable_interrupts);
+    }
+  }
+}
+
 }  // namespace
 
 extern "C" {
@@ -154,22 +189,25 @@
   GPIO_BITBAND(GPIOC_PDDR, 5) = 1;
   PORTC_PCR5 = PORT_PCR_DSE | PORT_PCR_MUX(1);
 
+  // Set up the CAN pins.
+  PORTA_PCR12 = PORT_PCR_DSE | PORT_PCR_MUX(2);
+  PORTA_PCR13 = PORT_PCR_DSE | PORT_PCR_MUX(2);
+
   delay(100);
 
-  teensy::UsbDevice usb_device(0, 0x16c0, 0x0492);
+  teensy::UsbDevice usb_device(0, 0x16c0, 0x0491);
   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);
+  teensy::HidFunction joysticks(&usb_device, 10);
+  teensy::InterruptOut interrupt_out(&usb_device, "JoystickForce");
   global_stdout.store(&tty2, ::std::memory_order_release);
-#endif
   usb_device.Initialize();
 
-  MoveJoysticks(&joysticks);
+  can_init();
+
+  MoveJoysticks(&joysticks, &interrupt_out);
 
   return 0;
 }
diff --git a/motors/pistol_grip/usb_forward.cc b/motors/pistol_grip/usb_forward.cc
new file mode 100644
index 0000000..830f8f7
--- /dev/null
+++ b/motors/pistol_grip/usb_forward.cc
@@ -0,0 +1,165 @@
+// This file is designed to be compiled for Windows to run on the driver's
+// station. It forwards UDP packets to the pistol grip controller over USB.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <string.h>
+
+#ifdef __WINNT__
+#include <winsock2.h>
+
+#define PRId8 "hhd"
+#define PRIx8 "hhx"
+#else
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#include <string>
+
+#include "libusb-1.0/libusb.h"
+
+#define CHECK_LIBUSB(expression)                                   \
+  ({                                                               \
+    const int result = (expression);                               \
+    if (result < 0) {                                              \
+      fprintf(stderr, #expression " failed with %d: %s\n", result, \
+              libusb_error_name(result));                          \
+      abort();                                                     \
+    }                                                              \
+    result;                                                        \
+  })
+
+#define CHECK(expression)                       \
+  do {                                          \
+    if (!(expression)) {                        \
+      fprintf(stderr, #expression " failed\n"); \
+      abort();                                  \
+    }                                           \
+  } while (false)
+
+#define PCHECK(expression)                                         \
+  ({                                                               \
+    const int result = (expression);                               \
+    if (result == -1) {                                            \
+      fprintf(stderr, #expression " failed with %d: %s\n", result, \
+              strerror(result));                                   \
+      abort();                                                     \
+    }                                                              \
+    result;                                                        \
+  })
+
+namespace {
+
+void OpenDevice(libusb_device_handle **handle) {
+  libusb_device **devices;
+  const ssize_t count = CHECK_LIBUSB(libusb_get_device_list(nullptr, &devices));
+  for (ssize_t i = 0; i < count; ++i) {
+    libusb_device_descriptor device_descriptor;
+    libusb_device *const device = devices[i];
+    CHECK_LIBUSB(libusb_get_device_descriptor(device, &device_descriptor));
+    if (device_descriptor.idVendor == 0x16C0 &&
+        device_descriptor.idProduct == 0x0491) {
+      // TODO(Brian): Check for the right interface too, instead of relying on
+      // us choosing different PIDs for each kind of Teensy.
+      fprintf(stderr, "Found device at %" PRId8 ":%" PRId8 "\n",
+              libusb_get_bus_number(device), libusb_get_port_number(device));
+      CHECK_LIBUSB(libusb_open(device, handle));
+      libusb_free_device_list(devices, true);
+      return;
+    }
+  }
+  fprintf(stderr, "Could not find device\n");
+  abort();
+}
+
+void FindEndpoint(libusb_device_handle *handle, uint8_t *endpoint) {
+  libusb_device *const device = libusb_get_device(handle);
+
+  libusb_config_descriptor *config_descriptor;
+  CHECK_LIBUSB(libusb_get_config_descriptor(device, 0, &config_descriptor));
+
+  for (uint8_t i = 0; i < config_descriptor->bNumInterfaces; ++i) {
+    const libusb_interface *const interface = &config_descriptor->interface[i];
+    CHECK(interface->num_altsetting == 1);
+    const libusb_interface_descriptor *const interface_descriptor =
+        &interface->altsetting[0];
+    if (interface_descriptor->bInterfaceClass == 0xFF &&
+        interface_descriptor->bInterfaceSubClass == 0x97 &&
+        interface_descriptor->bInterfaceProtocol == 0x97) {
+      CHECK(interface_descriptor->bNumEndpoints == 1);
+      const libusb_endpoint_descriptor *const endpoint_descriptor =
+          &interface_descriptor->endpoint[0];
+      CHECK_LIBUSB(libusb_claim_interface(
+          handle, interface_descriptor->bInterfaceNumber));
+      fprintf(stderr, "Found endpoint 0x%" PRIx8 "\n",
+              endpoint_descriptor->bEndpointAddress);
+      *endpoint = endpoint_descriptor->bEndpointAddress;
+      libusb_free_config_descriptor(config_descriptor);
+      return;
+    }
+  }
+
+  fprintf(stderr, "Could not find interface\n");
+  abort();
+}
+
+constexpr uint16_t port_number() { return 10971; }
+
+}  // namespace
+
+int main() {
+  CHECK_LIBUSB(libusb_init(nullptr));
+  libusb_set_debug(nullptr, LIBUSB_LOG_LEVEL_INFO);
+
+  libusb_set_debug(nullptr, LIBUSB_LOG_LEVEL_DEBUG);
+
+#ifdef __WINNT__
+  {
+    WSADATA wsa;
+    PCHECK(WSAStartup(MAKEWORD(2, 2), &wsa));
+  }
+#endif
+
+  libusb_device_handle *handle;
+  OpenDevice(&handle);
+  uint8_t endpoint;
+  FindEndpoint(handle, &endpoint);
+
+  const int socket_fd = PCHECK(socket(AF_INET, SOCK_DGRAM, 0));
+  {
+    sockaddr_in bind_address;
+    memset(&bind_address, 0, sizeof(bind_address));
+    bind_address.sin_family = AF_INET;
+    bind_address.sin_addr.s_addr = htonl(INADDR_ANY);
+    bind_address.sin_port = htons(port_number());
+    PCHECK(bind(socket_fd, reinterpret_cast<sockaddr *>(&bind_address),
+                sizeof(bind_address)));
+  }
+
+  while (true) {
+    char buffer[64];
+#ifdef __WINNT__
+    // TODO(Brian): Figure out how to detect too-large UDP packets.
+    static constexpr int kFlags = 0;
+#else
+    static constexpr int kFlags = MSG_TRUNC;
+#endif
+    const int length = PCHECK(recv(socket_fd, buffer, sizeof(buffer), kFlags));
+    if (static_cast<size_t>(length) > sizeof(buffer)) {
+      fprintf(stderr, "Too-long packet of %d: ignoring\n", length);
+      continue;
+    }
+
+    int transferred;
+    CHECK_LIBUSB(libusb_interrupt_transfer(
+        handle, endpoint, reinterpret_cast<unsigned char *>(buffer), length,
+        &transferred, 100));
+    if (transferred != length) {
+      fprintf(stderr, "Transferred %d instead of %d\n", transferred, length);
+    }
+  }
+}
diff --git a/motors/pistol_grip/usb_forward_windows_build.sh b/motors/pistol_grip/usb_forward_windows_build.sh
new file mode 100755
index 0000000..3f1022b
--- /dev/null
+++ b/motors/pistol_grip/usb_forward_windows_build.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# This script cross-compiles usb_forward for Windows.
+
+set -e
+set -u
+set -o pipefail
+
+check_installed() {
+	if ! dpkg -l "$1" > /dev/null 2>&1 ; then
+		echo "Must install $1" >&2
+		exit 1
+	fi
+}
+
+check_installed g++-mingw-w64-x86-64
+
+readonly SOURCE_FILE="$1"
+readonly LIBUSB_ARCHIVE="$2"
+readonly OUTPUT="$3"
+
+readonly TEMP="${OUTPUT}.build"
+rm -rf "${TEMP}"
+mkdir -p "${TEMP}/libusb"
+tar xJf "${LIBUSB_ARCHIVE}" -C "${TEMP}/libusb"
+
+x86_64-w64-mingw32-g++-posix \
+	--std=gnu++11 \
+	-D__USE_MINGW_ANSI_STDIO \
+	-o "${OUTPUT}" \
+	-I "${TEMP}/libusb/include" \
+	"${SOURCE_FILE}" \
+	-static -static-libgcc -static-libstdc++ \
+	-lws2_32 \
+	-L "${TEMP}/libusb/MinGW64/static" -lusb-1.0
diff --git a/motors/usb/BUILD b/motors/usb/BUILD
index 7d43447..c0600bf 100644
--- a/motors/usb/BUILD
+++ b/motors/usb/BUILD
@@ -63,6 +63,23 @@
 )
 
 cc_library(
+  name = 'interrupt_out',
+  visibility = ['//visibility:public'],
+  hdrs = [
+    'interrupt_out.h',
+  ],
+  srcs = [
+    'interrupt_out.cc',
+  ],
+  deps = [
+    ':usb',
+    '//motors/core',
+    '//motors:util',
+  ],
+  restricted_to = mcu_cpus,
+)
+
+cc_library(
   name = 'queue',
   hdrs = [
     'queue.h',
diff --git a/motors/usb/cdc.cc b/motors/usb/cdc.cc
index 87ce0dc..575b4c1 100644
--- a/motors/usb/cdc.cc
+++ b/motors/usb/cdc.cc
@@ -119,17 +119,12 @@
   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
-  }
+  CreateIadDescriptor(
+      /*first_interface=*/status_interface_,
+      /*interface_count=*/2,
+      /*function_class=*/communications_class(),
+      /*function_subclass=*/communications_subclass::abstract_control_model(),
+      /*function_protocol=*/0, "UsbTty");
 
   {
     const auto interface_descriptor = CreateDescriptor(
diff --git a/motors/usb/cdc.h b/motors/usb/cdc.h
index 00eec4c..c02873d 100644
--- a/motors/usb/cdc.h
+++ b/motors/usb/cdc.h
@@ -116,7 +116,7 @@
   EndpointBufferState tx_state_;
   Data01 next_tx_toggle_;
 
-  // These are BdtEntries which we're holding into without releasing back to the
+  // These are BdtEntries which we're holding onto 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;
diff --git a/motors/usb/hid.cc b/motors/usb/hid.cc
index 24ffe24..0c5075e 100644
--- a/motors/usb/hid.cc
+++ b/motors/usb/hid.cc
@@ -61,16 +61,12 @@
   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
-  }
+  CreateIadDescriptor(
+      /*first_interface=*/interface_,
+      /*interface_count=*/1,
+      /*function_class=*/hid_class(),
+      /*function_subclass=*/0,
+      /*function_protocol=*/0, "HidIad");
 
   {
     const auto interface_descriptor = CreateDescriptor(
diff --git a/motors/usb/hid.h b/motors/usb/hid.h
index 4b2dae4..37aa86b 100644
--- a/motors/usb/hid.h
+++ b/motors/usb/hid.h
@@ -51,10 +51,6 @@
   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;
 
diff --git a/motors/usb/interrupt_out.cc b/motors/usb/interrupt_out.cc
new file mode 100644
index 0000000..9af650a
--- /dev/null
+++ b/motors/usb/interrupt_out.cc
@@ -0,0 +1,91 @@
+#include "motors/usb/interrupt_out.h"
+
+namespace frc971 {
+namespace teensy {
+
+void InterruptOut::Initialize() {
+  interface_ = AddInterface();
+  endpoint_ = AddEndpoint();
+
+  SetMicrosoftDeviceInterfaceGuids("{D4FA286B-C60D-4B99-B49B-9656139F5771}");
+
+  CreateIadDescriptor(
+      /*first_interface=*/interface_,
+      /*interface_count=*/1,
+      /*function_class=*/vendor_specific_class(),
+      /*function_subclass=*/0,
+      /*function_protocol=*/0, name_);
+
+  {
+    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(vendor_specific_class());  // bInterfaceClass
+    interface_descriptor->AddByte(0x97);  // bInterfaceSubClass
+    interface_descriptor->AddByte(0x97);  // bInterfaceProtocol
+    interface_descriptor->AddByte(device()->AddString(name_));  // iInterface
+  }
+
+  {
+    const auto endpoint_descriptor = CreateDescriptor(
+        endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
+    endpoint_descriptor->AddByte(endpoint_);  // bEndpointAddress
+    endpoint_descriptor->AddByte(
+        m_endpoint_attributes_interrupt());  // bmAttributes
+    endpoint_descriptor->AddUint16(kSize);   // wMaxPacketSize
+    endpoint_descriptor->AddByte(1);  // bInterval
+  }
+}
+
+void InterruptOut::HandleOutFinished(int endpoint, BdtEntry *bdt_entry) {
+  if (endpoint == endpoint_) {
+
+    DisableInterrupts disable_interrupts;
+    if (first_rx_held_ == nullptr) {
+      first_rx_held_ = bdt_entry;
+    } else {
+      second_rx_held_ = bdt_entry;
+    }
+  }
+}
+
+int InterruptOut::ReceiveData(char *buffer) {
+  DisableInterrupts disable_interrupts;
+  if (first_rx_held_ == nullptr) {
+    return -1;
+  }
+
+  BdtEntry *const bdt_entry = first_rx_held_;
+  const size_t data_size = G_USB_BD_BC(bdt_entry->buffer_descriptor);
+  memcpy(buffer, bdt_entry->address, kSize);
+  dma_memory_barrier();
+
+  first_rx_held_->buffer_descriptor = M_USB_BD_OWN | M_USB_BD_DTS |
+                                      V_USB_BD_BC(kSize) |
+                                      static_cast<uint32_t>(next_rx_toggle_);
+  next_rx_toggle_ = Data01Inverse(next_rx_toggle_);
+
+  first_rx_held_ = second_rx_held_;
+  second_rx_held_ = nullptr;
+  return data_size;
+}
+
+void InterruptOut::HandleConfigured(int endpoint) {
+  if (endpoint == endpoint_) {
+    device()->ConfigureEndpointFor(endpoint_, true, false, true);
+    next_rx_toggle_ = Data01::kData0;
+    device()->SetBdtEntry(
+        endpoint_, Direction::kRx, EvenOdd::kEven,
+        {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(buffers_[0].size()),
+         buffers_[0].data()});
+    device()->SetBdtEntry(endpoint_, Direction::kRx, EvenOdd::kOdd,
+                          {M_USB_BD_OWN | M_USB_BD_DTS |
+                               V_USB_BD_BC(buffers_[1].size()) | M_USB_BD_DATA1,
+                           buffers_[1].data()});
+  }
+}
+
+}  // namespace teensy
+}  // namespace frc971
diff --git a/motors/usb/interrupt_out.h b/motors/usb/interrupt_out.h
new file mode 100644
index 0000000..9a7232c
--- /dev/null
+++ b/motors/usb/interrupt_out.h
@@ -0,0 +1,64 @@
+#ifndef MOTORS_USB_INTERRUPT_OUT_H_
+#define MOTORS_USB_INTERRUPT_OUT_H_
+
+#include "motors/usb/usb.h"
+#include "motors/util.h"
+
+#include <array>
+#include <string>
+
+namespace frc971 {
+namespace teensy {
+
+// A simple function that just has an interrupt out endpoint and exposes the
+// data received.
+class InterruptOut final : public UsbFunction {
+ public:
+  static constexpr size_t kSize = 64;
+
+  InterruptOut(UsbDevice *device, const ::std::string &name)
+      : UsbFunction(device), name_(name) {}
+  ~InterruptOut() override = default;
+
+  // Copies the next packet into buffer.
+  // buffer must have kSize of space available.
+  // Returns the size of the packet, or -1 if there isn't one.
+  int ReceiveData(char *buffer);
+
+ private:
+  void Initialize() override;
+
+  void HandleOutFinished(int endpoint, BdtEntry *bdt_entry) override;
+  void HandleConfigured(int endpoint) override;
+  void HandleReset() override {
+    device()->SetBdtEntry(endpoint_, Direction::kRx, EvenOdd::kEven,
+                          {0, nullptr});
+    device()->SetBdtEntry(endpoint_, Direction::kRx, EvenOdd::kOdd,
+                          {0, nullptr});
+  }
+
+  ::std::string MicrosoftExtendedCompatibleId() override {
+    ::std::string result = "WINUSB";
+    result.resize(16);
+    return result;
+  }
+
+  ::std::array<::std::array<char, kSize>, 2> buffers_;
+
+  int interface_;
+  int endpoint_;
+
+  // These are BdtEntries which we're holding onto until the data is copied out.
+  // This also has the advantage of avoiding any more data being sent until
+  // we're ready.
+  // They are only manipulated with interrupts disabled.
+  BdtEntry *first_rx_held_ = nullptr, *second_rx_held_ = nullptr;
+  Data01 next_rx_toggle_;
+
+  const ::std::string name_;
+};
+
+}  // namespace teensy
+}  // namespace frc971
+
+#endif  // MOTORS_USB_INTERRUPT_OUT_H_
diff --git a/motors/usb/usb.cc b/motors/usb/usb.cc
index 36c6456..4a1b115 100644
--- a/motors/usb/usb.cc
+++ b/motors/usb/usb.cc
@@ -2,6 +2,8 @@
 
 #include <string.h>
 
+#include <map>
+
 #include "motors/util.h"
 
 namespace frc971 {
@@ -67,6 +69,10 @@
 // The device protocol for using IADs.
 constexpr uint8_t iad_device_protocol() { return 0x01; }
 
+// The Microsoft "vendor code" we're going to use. It's pretty arbitrary (just
+// has to not be some other request we want to use).
+constexpr uint8_t microsoft_vendor_code() { return 0x67; }
+
 // The total number of endpoints supported by this hardware.
 constexpr int number_endpoints() { return 16; }
 
@@ -122,7 +128,9 @@
   device_descriptor_->AddByte(kEndpoint0MaxSize);  // bMaxPacketSize0
   device_descriptor_->AddUint16(vendor_id);  // idVendor
   device_descriptor_->AddUint16(product_id);  // idProduct
-  device_descriptor_->AddUint16(0);  // bcdDevice
+  // Increment this whenever you need Windows boxes to actually pay attention to
+  // changes.
+  device_descriptor_->AddUint16(7);  // bcdDevice
   // We might overwrite these string descriptor indices later if we get strings
   // to put there.
   device_descriptor_->AddByte(0);  // iManufacturer
@@ -148,6 +156,53 @@
     function->Initialize();
   }
 
+  {
+    const uint32_t length = 16 + 24 * functions_.size();
+    microsoft_extended_id_descriptor_.resize(length);
+    int index = 0;
+
+    // dwLength
+    microsoft_extended_id_descriptor_[index++] = length & 0xFF;
+    microsoft_extended_id_descriptor_[index++] = (length >> UINT32_C(8)) & 0xFF;
+    microsoft_extended_id_descriptor_[index++] =
+        (length >> UINT32_C(16)) & 0xFF;
+    microsoft_extended_id_descriptor_[index++] =
+        (length >> UINT32_C(24)) & 0xFF;
+
+    // bcdVersion
+    microsoft_extended_id_descriptor_[index++] = 0x00;
+    microsoft_extended_id_descriptor_[index++] = 0x01;
+
+    // wIndex
+    microsoft_extended_id_descriptor_[index++] =
+        microsoft_feature_descriptors::kExtendedCompatibilityId;
+    microsoft_extended_id_descriptor_[index++] = 0;
+
+    // bCount
+    microsoft_extended_id_descriptor_[index++] = functions_.size();
+
+    // Reserved
+    index += 7;
+
+    for (UsbFunction *function : functions_) {
+      // bFirstInterfaceNumber
+      microsoft_extended_id_descriptor_[index++] = function->first_interface_;
+
+      // Reserved
+      index++;
+
+      // compatibleID and subCompatibleID
+      microsoft_extended_id_descriptor_.replace(
+          index, 16, function->MicrosoftExtendedCompatibleId());
+      index += 16;
+
+      // Reserved
+      index += 6;
+    }
+
+    assert(index == length);
+  }
+
   config_descriptor_->AddUint16(
       config_descriptor_list_.CurrentSize());              // wTotalLength
   config_descriptor_->AddByte(interface_mapping_.size());  // bNumInterfaces
@@ -696,6 +751,29 @@
               return;
 
             case UsbDescriptorType::kString:
+              // Skip any other checks on the other fields. Who knows what
+              // Microsoft is going to set them to; not like they document it
+              // anywhere obvious...
+              if (descriptor_index == 0xEE && setup_packet.index == 0) {
+                static uint8_t
+                    kMicrosoftOsStringDescriptor
+                        [] = {
+                            0x12,  // bLength
+                            static_cast<uint8_t>(
+                                UsbDescriptorType::kString),  // bDescriptorType
+                            0x4D,
+                            0x00, 0x53, 0x00, 0x46, 0x00, 0x54, 0x00, 0x31,
+                            0x00, 0x30, 0x00, 0x30,
+                            0x00,                     // qwSignature
+                            microsoft_vendor_code(),  // bMS_VendorCode
+                            0x00                      // bPad
+                        };
+                QueueEndpoint0Data(
+                    reinterpret_cast<char *>(kMicrosoftOsStringDescriptor),
+                    ::std::min<int>(setup_packet.length,
+                                    sizeof(kMicrosoftOsStringDescriptor)));
+                return;
+              }
               if (descriptor_index != 0 &&
                   setup_packet.index != english_us_code()) {
                 break;
@@ -716,6 +794,62 @@
       }
       break;
 
+    case SetupRequestType::kVendor:
+      switch (setup_packet.request) {
+        case microsoft_vendor_code():
+          if (!in) {
+            break;
+          }
+
+          switch (recipient) {
+            case standard_setup_recipients::kDevice:
+              if (setup_packet.value != 0) {
+                // Ignoring weird things and descriptors larger than 64K for
+                // now.
+                break;
+              }
+              switch (setup_packet.index) {
+                case microsoft_feature_descriptors::kExtendedCompatibilityId:
+                  QueueEndpoint0Data(
+                      microsoft_extended_id_descriptor_.data(),
+                      ::std::min<int>(
+                          setup_packet.length,
+                          microsoft_extended_id_descriptor_.size()));
+                  return;
+              }
+              break;
+
+            case standard_setup_recipients::kInterface:
+              switch (setup_packet.index) {
+                case microsoft_feature_descriptors::kExtendedProperties:
+                  const int interface = setup_packet.value & 0xFF;
+                  const int page_number = (setup_packet.value >> 8) & 0xFF;
+
+                  if (page_number != 0) {
+                    // Ignoring weird things and descriptors larger than 64K for
+                    // now.
+                    break;
+                  }
+
+                  const UsbFunction *const function =
+                      interface_mapping_[interface];
+                  const ::std::string &descriptor =
+                      function->microsoft_extended_property_descriptor_;
+                  if (descriptor.empty() ||
+                      interface != function->first_interface_) {
+                    break;
+                  }
+                  QueueEndpoint0Data(
+                      descriptor.data(),
+                      ::std::min<int>(setup_packet.length, descriptor.size()));
+                  return;
+              }
+              break;
+          }
+          break;
+      }
+      break;
+
     default:
       for (UsbFunction *function : functions_) {
         switch (function->HandleEndpoint0SetupPacket(setup_packet)) {
@@ -827,6 +961,100 @@
   return r;
 }
 
+void UsbFunction::CreateIadDescriptor(int first_interface, int interface_count,
+                                      int function_class, int function_subclass,
+                                      int function_protocol,
+                                      const ::std::string &function) {
+  first_interface_ = first_interface;
+  const auto iad_descriptor = CreateDescriptor(
+      iad_descriptor_length(), UsbDescriptorType::kInterfaceAssociation);
+  iad_descriptor->AddByte(first_interface);                // bFirstInterface
+  iad_descriptor->AddByte(interface_count);                // bInterfaceCount
+  iad_descriptor->AddByte(function_class);                 // bFunctionClass
+  iad_descriptor->AddByte(function_subclass);              // bFunctionSubClass
+  iad_descriptor->AddByte(function_protocol);              // bFunctionProtocol
+  iad_descriptor->AddByte(device()->AddString(function));  // iFunction
+}
+
+void UsbFunction::SetMicrosoftDeviceInterfaceGuids(const ::std::string &guids) {
+  ::std::map<::std::string, ::std::string> properties;
+  properties["DeviceInterfaceGUIDs"] = guids + ::std::string(1, '\0');
+  ::std::string *const descriptor = &microsoft_extended_property_descriptor_;
+
+  uint32_t length = 10;
+  for (auto &pair : properties) {
+    length += 14;
+    length += (pair.first.size() + 1) * 2;
+    length += (pair.second.size() + 1) * 2;
+  }
+  descriptor->resize(length);
+  int index = 0;
+
+  // dwLength
+  (*descriptor)[index++] = length & 0xFF;
+  (*descriptor)[index++] = (length >> UINT32_C(8)) & 0xFF;
+  (*descriptor)[index++] = (length >> UINT32_C(16)) & 0xFF;
+  (*descriptor)[index++] = (length >> UINT32_C(24)) & 0xFF;
+
+  // bcdVersion
+  (*descriptor)[index++] = 0x00;
+  (*descriptor)[index++] = 0x01;
+
+  // wIndex
+  (*descriptor)[index++] = microsoft_feature_descriptors::kExtendedProperties;
+  (*descriptor)[index++] = 0;
+
+  // wCount
+  (*descriptor)[index++] = properties.size() & 0xFF;
+  (*descriptor)[index++] = (properties.size() >> 8) & 0xFF;
+
+  for (auto &pair : properties) {
+    const uint32_t size = 14 + (pair.first.size() + pair.second.size() + 2) * 2;
+    // dwSize
+    (*descriptor)[index++] = size & 0xFF;
+    (*descriptor)[index++] = (size >> UINT32_C(8)) & 0xFF;
+    (*descriptor)[index++] = (size >> UINT32_C(16)) & 0xFF;
+    (*descriptor)[index++] = (size >> UINT32_C(24)) & 0xFF;
+
+    // Need to get this from the map if we ever do others in the future.
+    const uint32_t data_type = 7;  // REG_MULTI_SZ
+    // dwPropertyDataType
+    (*descriptor)[index++] = data_type & 0xFF;
+    (*descriptor)[index++] = (data_type >> UINT32_C(8)) & 0xFF;
+    (*descriptor)[index++] = (data_type >> UINT32_C(16)) & 0xFF;
+    (*descriptor)[index++] = (data_type >> UINT32_C(24)) & 0xFF;
+
+    // wPropertyNameLength
+    (*descriptor)[index++] = ((pair.first.size() + 1) * 2) & 0xFF;
+    (*descriptor)[index++] = (((pair.first.size() + 1) * 2) >> 8) & 0xFF;
+
+    // bPropertyName
+    for (size_t i = 0; i < pair.first.size(); ++i) {
+      (*descriptor)[index] = pair.first[i];
+      index += 2;
+    }
+    index += 2;
+
+    // wPropertyDataLength
+    (*descriptor)[index++] = ((pair.second.size() + 1) * 2) & 0xFF;
+    (*descriptor)[index++] =
+        (((pair.second.size() + 1) * 2) >> UINT8_C(8)) & 0xFF;
+    (*descriptor)[index++] =
+        (((pair.second.size() + 1) * 2) >> UINT8_C(16)) & 0xFF;
+    (*descriptor)[index++] =
+        (((pair.second.size() + 1) * 2) >> UINT8_C(24)) & 0xFF;
+
+    // bPropertyData
+    for (size_t i = 0; i < pair.second.size(); ++i) {
+      (*descriptor)[index] = pair.second[i];
+      index += 2;
+    }
+    index += 2;
+  }
+
+  assert(index == length);
+}
+
 void UsbDevice::SetBdtEntry(int endpoint, Direction direction, EvenOdd odd,
                             BdtEntry bdt_entry) {
   *MutableBdtEntry(endpoint, direction, odd) = bdt_entry;
diff --git a/motors/usb/usb.h b/motors/usb/usb.h
index 4db29dc..8fd2fb3 100644
--- a/motors/usb/usb.h
+++ b/motors/usb/usb.h
@@ -106,6 +106,11 @@
 constexpr int kOther = 3;
 }  // namespace standard_setup_recipients
 
+namespace microsoft_feature_descriptors {
+constexpr int kExtendedCompatibilityId = 4;
+constexpr int kExtendedProperties = 5;
+}  // namespace microsoft_feature_descriptors
+
 // The HID class specification says this. Can't find any mention in the main
 // standard.
 #define G_DESCRIPTOR_TYPE_TYPE(descriptor_type) \
@@ -116,6 +121,8 @@
 constexpr int kVendor = 2;
 }  // namespace standard_descriptor_type_types
 
+constexpr uint8_t vendor_specific_class() { return 0xFF; }
+
 class UsbFunction;
 
 // Allows building up a list of descriptors. This supports a much nicer API than
@@ -430,6 +437,9 @@
   // All of the functions (without duplicates).
   ::std::vector<UsbFunction *> functions_;
 
+  // Filled out during Initialize().
+  ::std::string microsoft_extended_id_descriptor_;
+
   friend void usb_isr(void);
   friend class UsbFunction;
 };
@@ -497,6 +507,23 @@
 
   UsbDevice *device() const { return device_; }
 
+  void CreateIadDescriptor(int first_interface, int interface_count,
+                           int function_class, int function_subclass,
+                           int function_protocol,
+                           const ::std::string &function);
+
+  // Sets the interface GUIDs for this function. Each GUID (one per interface?)
+  // should be followed by a NUL.
+  //
+  // If this is never called, no GUID extended property will be reported.
+  //
+  // This is needed to pass to Windows so WinUSB will be happy. Generate them at
+  // https://www.guidgenerator.com/online-guid-generator.aspx (Uppcase, Braces,
+  // and Hyphens).
+  //
+  // May only be called during setup.
+  void SetMicrosoftDeviceInterfaceGuids(const ::std::string &guids);
+
  private:
   virtual void Initialize() = 0;
 
@@ -515,9 +542,15 @@
     return SetupResponse::kIgnored;
   }
 
-  virtual void HandleOutFinished(int endpoint, BdtEntry *bdt_entry) = 0;
-  virtual void HandleInFinished(int endpoint, BdtEntry *bdt_entry,
-                                EvenOdd odd) = 0;
+  // Returns the concatenated compatible ID and subcompatible ID.
+  virtual ::std::string MicrosoftExtendedCompatibleId() {
+    // Default to both of them being "unused".
+    return ::std::string(16, '\0');
+  }
+
+  virtual void HandleOutFinished(int /*endpoint*/, BdtEntry * /*bdt_entry*/) {}
+  virtual void HandleInFinished(int /*endpoint*/, BdtEntry * /*bdt_entry*/,
+                                EvenOdd /*odd*/) {}
 
   // Called when a given interface is configured (aka "experiences a
   // configuration event"). This means all rx and tx buffers have been cleared
@@ -528,6 +561,11 @@
   // Should reset everything to use the even buffers next.
   virtual void HandleReset() = 0;
 
+  int first_interface_ = -1;
+
+  // Filled out during Initialize().
+  ::std::string microsoft_extended_property_descriptor_;
+
   UsbDevice *const device_;
 
   friend class UsbDevice;