Add a USB forwarding application that works on Windows too

It has been tested with a Windows VM sending a few messages. The VM may
have gained some necessary state during testing so it won't work on
other Windows machines, but I've tried to clear it all out and it still
worked.

Change-Id: I814fd57653cd8d86534516e8c4f626746146e626
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;