Remove stupid wpilib forwarding target

It's silly to need to use NO_BUILD_AMD64 for //aos/...

Change-Id: I103b3c3a0f5cfb72c081c5aafef624cf0533acb0
diff --git a/frc971/wpilib/BUILD b/frc971/wpilib/BUILD
index 288f809..6ea778a 100644
--- a/frc971/wpilib/BUILD
+++ b/frc971/wpilib/BUILD
@@ -18,11 +18,12 @@
     'encoder_and_potentiometer.h',
   ],
   deps = [
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
     ':dma_edge_counting',
     '//aos/linux_code:init',
     '//aos/common/logging',
     '//aos/common:mutex',
+    ':dma',
   ],
 )
 
@@ -35,8 +36,9 @@
     'dma_edge_counting.h',
   ],
   deps = [
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
     '//aos/common/logging',
+    ':dma',
   ],
 )
 
@@ -49,7 +51,7 @@
     'interrupt_edge_counting.h',
   ],
   deps = [
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
     '//aos/common/logging',
     '//aos/common:stl_mutex',
     '//aos/common:time',
@@ -68,7 +70,7 @@
     'buffered_pcm.h',
   ],
   deps = [
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
     '//aos/common/logging',
   ],
 )
@@ -82,7 +84,7 @@
     'gyro_interface.h',
   ],
   deps = [
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
     '//aos/common/logging',
     '//aos/common:time',
   ],
@@ -118,7 +120,7 @@
   ],
   deps = [
     '//aos/common:mutex',
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
     '//frc971/queues:gyro',
     '//aos/common:time',
   ],
@@ -150,7 +152,7 @@
     'joystick_sender.h',
   ],
   deps = [
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
     '//aos/common/messages:robot_state',
     '//aos/linux_code:init',
     '//aos/common/network:team_number',
@@ -168,7 +170,7 @@
   ],
   deps = [
     '//aos/common/messages:robot_state',
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
     '//aos/common/logging:queue_logging',
   ],
 )
@@ -190,7 +192,7 @@
   ],
   deps = [
     ':pdp_values',
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
     '//aos/common/logging:queue_logging',
     '//aos/linux_code:init',
     '//aos/common/util:phased_loop',
@@ -203,7 +205,7 @@
     'wpilib_robot_base.h',
   ],
   deps = [
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
   ],
 )
 
@@ -223,7 +225,7 @@
     'ADIS16448.cc',
   ],
   deps = [
-    '//aos/externals:wpilib',
+    '//third_party/allwpilib_2016:wpilib',
     '//aos/common/logging',
     '//aos/common/logging:queue_logging',
     '//aos/common:time',
@@ -231,3 +233,16 @@
     ':imu_queue',
   ],
 )
+
+cc_library(
+  name = 'dma',
+  hdrs = [
+    'dma.h',
+  ],
+  srcs = [
+    'dma.cc',
+  ],
+  deps = [
+    '//third_party/allwpilib_2016:wpilib',
+  ],
+)
diff --git a/frc971/wpilib/dma.cc b/frc971/wpilib/dma.cc
new file mode 100644
index 0000000..ea9f64c
--- /dev/null
+++ b/frc971/wpilib/dma.cc
@@ -0,0 +1,484 @@
+#include "frc971/wpilib/dma.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <type_traits>
+
+#include "DigitalSource.h"
+#include "AnalogInput.h"
+#include "Encoder.h"
+
+// Interface to the roboRIO FPGA's DMA features.
+
+// Like tEncoder::tOutput with the bitfields reversed.
+typedef union {
+  struct {
+    unsigned Direction: 1;
+    signed Value: 31;
+  };
+  struct {
+    unsigned value: 32;
+  };
+} t1Output;
+
+static const uint32_t kNumHeaders = 10;
+
+#ifdef WPILIB2015
+static constexpr ssize_t kChannelSize[18] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3,
+                                             2, 1, 4, 4, 4, 4, 4, 4};
+#else
+static constexpr ssize_t kChannelSize[20] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3,
+                                             2, 1, 4, 4, 4, 4, 4, 4, 4, 4};
+#endif
+
+enum DMAOffsetConstants {
+  kEnable_AI0_Low = 0,
+  kEnable_AI0_High = 1,
+  kEnable_AIAveraged0_Low = 2,
+  kEnable_AIAveraged0_High = 3,
+  kEnable_AI1_Low = 4,
+  kEnable_AI1_High = 5,
+  kEnable_AIAveraged1_Low = 6,
+  kEnable_AIAveraged1_High = 7,
+  kEnable_Accumulator0 = 8,
+  kEnable_Accumulator1 = 9,
+  kEnable_DI = 10,
+  kEnable_AnalogTriggers = 11,
+  kEnable_Counters_Low = 12,
+  kEnable_Counters_High = 13,
+  kEnable_CounterTimers_Low = 14,
+  kEnable_CounterTimers_High = 15,
+#ifdef WPILIB2015
+  kEnable_Encoders = 16,
+  kEnable_EncoderTimers = 17,
+#else
+  kEnable_Encoders_Low = 16,
+  kEnable_Encoders_High = 17,
+  kEnable_EncoderTimers_Low = 18,
+  kEnable_EncoderTimers_High = 19,
+#endif
+};
+
+DMA::DMA() {
+  tRioStatusCode status = 0;
+  tdma_config_ = tDMA::create(&status);
+  tdma_config_->writeConfig_ExternalClock(false, &status);
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+#ifdef WPILIB2015
+  NiFpga_WriteU32(0x10000, 0x1832c, 0x0);
+#endif
+  if (status != 0) {
+    return;
+  }
+  SetRate(1);
+  SetPause(false);
+}
+
+DMA::~DMA() {
+  tRioStatusCode status = 0;
+
+  manager_->stop(&status);
+  delete tdma_config_;
+}
+
+void DMA::SetPause(bool pause) {
+  tRioStatusCode status = 0;
+  tdma_config_->writeConfig_Pause(pause, &status);
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::SetRate(uint32_t cycles) {
+  if (cycles < 1) {
+    cycles = 1;
+  }
+  tRioStatusCode status = 0;
+  tdma_config_->writeRate(cycles, &status);
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::Add(Encoder *encoder) {
+  tRioStatusCode status = 0;
+
+  if (manager_) {
+    wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+        "DMA::Add() only works before DMA::Start()");
+    return;
+  }
+  const int index = encoder->GetFPGAIndex();
+
+#ifdef WPILIB2015
+  if (index < 4) {
+    // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
+    tdma_config_->writeConfig_Enable_Encoders(true, &status);
+  } else {
+    wpi_setErrorWithContext(
+        NiFpga_Status_InvalidParameter,
+        "FPGA encoder index is not in the 4 that get logged.");
+    return;
+  }
+#else
+  if (index < 4) {
+    // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
+    tdma_config_->writeConfig_Enable_Encoders_Low(true, &status);
+  } else if (index < 8) {
+    // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
+    tdma_config_->writeConfig_Enable_Encoders_High(true, &status);
+  } else {
+    wpi_setErrorWithContext(
+        NiFpga_Status_InvalidParameter,
+        "FPGA encoder index is not in the 4 that get logged.");
+    return;
+  }
+#endif
+
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::Add(DigitalSource * /*input*/) {
+  tRioStatusCode status = 0;
+
+  if (manager_) {
+    wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+        "DMA::Add() only works before DMA::Start()");
+    return;
+  }
+
+  tdma_config_->writeConfig_Enable_DI(true, &status);
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::Add(AnalogInput *input) {
+  tRioStatusCode status = 0;
+
+  if (manager_) {
+    wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+        "DMA::Add() only works before DMA::Start()");
+    return;
+  }
+
+  if (input->GetChannel() <= 3) {
+    tdma_config_->writeConfig_Enable_AI0_Low(true, &status);
+  } else {
+    tdma_config_->writeConfig_Enable_AI0_High(true, &status);
+  }
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::SetExternalTrigger(DigitalSource *input, bool rising, bool falling) {
+  tRioStatusCode status = 0;
+
+  if (manager_) {
+    wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+        "DMA::SetExternalTrigger() only works before DMA::Start()");
+    return;
+  }
+
+  auto index =
+      ::std::find(trigger_channels_.begin(), trigger_channels_.end(), false);
+  if (index == trigger_channels_.end()) {
+    wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+                            "DMA: No channels left");
+    return;
+  }
+  *index = true;
+
+  const int channel_index = ::std::distance(trigger_channels_.begin(), index);
+
+  const bool is_external_clock =
+      tdma_config_->readConfig_ExternalClock(&status);
+  if (status == 0) {
+    if (!is_external_clock) {
+      tdma_config_->writeConfig_ExternalClock(true, &status);
+      wpi_setErrorWithContext(status, getHALErrorMessage(status));
+      if (status != 0) {
+        return;
+      }
+    }
+  } else {
+    wpi_setErrorWithContext(status, getHALErrorMessage(status));
+    return;
+  }
+
+  nFPGA::nRoboRIO_FPGANamespace::tDMA::tExternalTriggers new_trigger;
+
+  new_trigger.FallingEdge = falling;
+  new_trigger.RisingEdge = rising;
+  new_trigger.ExternalClockSource_AnalogTrigger = false;
+  unsigned char module = 0;
+  unsigned char channel = input->GetChannelForRouting();
+  if (channel >= kNumHeaders) {
+    module = 1;
+    channel -= kNumHeaders;
+  } else {
+    module = 0;
+  }
+
+  new_trigger.ExternalClockSource_Module = module;
+  new_trigger.ExternalClockSource_Channel = channel;
+
+// Configures the trigger to be external, not off the FPGA clock.
+#ifndef WPILIB2015
+  tdma_config_->writeExternalTriggers(channel_index / 4, channel_index % 4,
+                                      new_trigger, &status);
+  if (status != 0) {
+    wpi_setErrorWithContext(status, getHALErrorMessage(status));
+    return;
+  }
+#else
+  uint32_t current_triggers;
+  tRioStatusCode register_status =
+      NiFpga_ReadU32(0x10000, 0x1832c, &current_triggers);
+  if (register_status != 0) {
+    wpi_setErrorWithContext(register_status, getHALErrorMessage(status));
+    return;
+  }
+  current_triggers = (current_triggers & ~(0xff << (channel_index * 8))) |
+                     (new_trigger.value << (channel_index * 8));
+  register_status = NiFpga_WriteU32(0x10000, 0x1832c, current_triggers);
+  if (register_status != 0) {
+    wpi_setErrorWithContext(register_status, getHALErrorMessage(status));
+    return;
+  }
+#endif
+}
+
+DMA::ReadStatus DMA::Read(DMASample *sample, uint32_t timeout_ms,
+                          size_t *remaining_out) {
+  tRioStatusCode status = 0;
+  size_t remainingBytes = 0;
+  *remaining_out = 0;
+
+  if (!manager_.get()) {
+    wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+        "DMA::Read() only works after DMA::Start()");
+    return STATUS_ERROR;
+  }
+
+  sample->dma_ = this;
+  manager_->read(sample->read_buffer_, capture_size_, timeout_ms,
+                 &remainingBytes, &status);
+
+  // TODO(jerry): Do this only if status == 0?
+  *remaining_out = remainingBytes / capture_size_;
+
+  // TODO(austin): Check that *remainingBytes % capture_size_ == 0 and deal
+  // with it if it isn't.  Probably meant that we overflowed?
+  if (status == 0) {
+    return STATUS_OK;
+  } else if (status == NiFpga_Status_FifoTimeout) {
+    return STATUS_TIMEOUT;
+  } else {
+    wpi_setErrorWithContext(status, getHALErrorMessage(status));
+    return STATUS_ERROR;
+  }
+}
+
+const char *DMA::NameOfReadStatus(ReadStatus s) {
+  switch (s) {
+    case STATUS_OK:      return "OK";
+    case STATUS_TIMEOUT: return "TIMEOUT";
+    case STATUS_ERROR:   return "ERROR";
+    default:             return "(bad ReadStatus code)";
+  }
+}
+
+void DMA::Start(size_t queue_depth) {
+  tRioStatusCode status = 0;
+  tconfig_ = tdma_config_->readConfig(&status);
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  if (status != 0) {
+    return;
+  }
+
+  {
+    size_t accum_size = 0;
+#define SET_SIZE(bit)                      \
+  if (tconfig_.bit) {                      \
+    channel_offsets_[k##bit] = accum_size; \
+    accum_size += kChannelSize[k##bit];    \
+  } else {                                 \
+    channel_offsets_[k##bit] = -1;         \
+  }
+
+    SET_SIZE(Enable_AI0_Low);
+    SET_SIZE(Enable_AI0_High);
+    SET_SIZE(Enable_AIAveraged0_Low);
+    SET_SIZE(Enable_AIAveraged0_High);
+    SET_SIZE(Enable_AI1_Low);
+    SET_SIZE(Enable_AI1_High);
+    SET_SIZE(Enable_AIAveraged1_Low);
+    SET_SIZE(Enable_AIAveraged1_High);
+    SET_SIZE(Enable_Accumulator0);
+    SET_SIZE(Enable_Accumulator1);
+    SET_SIZE(Enable_DI);
+    SET_SIZE(Enable_AnalogTriggers);
+    SET_SIZE(Enable_Counters_Low);
+    SET_SIZE(Enable_Counters_High);
+    SET_SIZE(Enable_CounterTimers_Low);
+    SET_SIZE(Enable_CounterTimers_High);
+#ifdef WPILIB2015
+    SET_SIZE(Enable_Encoders);
+    SET_SIZE(Enable_EncoderTimers);
+#else
+    SET_SIZE(Enable_Encoders_Low);
+    SET_SIZE(Enable_Encoders_High);
+    SET_SIZE(Enable_EncoderTimers_Low);
+    SET_SIZE(Enable_EncoderTimers_High);
+#endif
+#undef SET_SIZE
+    capture_size_ = accum_size + 1;
+  }
+
+  manager_.reset(
+      new nFPGA::tDMAManager(0, queue_depth * capture_size_, &status));
+
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  if (status != 0) {
+    return;
+  }
+  // Start, stop, start to clear the buffer.
+  manager_->start(&status);
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  if (status != 0) {
+    return;
+  }
+  manager_->stop(&status);
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  if (status != 0) {
+    return;
+  }
+  manager_->start(&status);
+  wpi_setErrorWithContext(status, getHALErrorMessage(status));
+  if (status != 0) {
+    return;
+  }
+
+}
+
+static_assert(::std::is_pod<DMASample>::value, "DMASample needs to be POD");
+
+ssize_t DMASample::offset(int index) const { return dma_->channel_offsets_[index]; }
+
+uint32_t DMASample::GetTime() const {
+  return read_buffer_[dma_->capture_size_ - 1];
+}
+
+double DMASample::GetTimestamp() const {
+  return static_cast<double>(GetTime()) * 0.000001;
+}
+
+bool DMASample::Get(DigitalSource *input) const {
+  if (offset(kEnable_DI) == -1) {
+    wpi_setStaticErrorWithContext(
+        dma_, NiFpga_Status_ResourceNotFound,
+        getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+    return false;
+  }
+  if (input->GetChannelForRouting() < kNumHeaders) {
+    return (read_buffer_[offset(kEnable_DI)] >> input->GetChannelForRouting()) &
+           0x1;
+  } else {
+    return (read_buffer_[offset(kEnable_DI)] >>
+            (input->GetChannelForRouting() + 6)) &
+           0x1;
+  }
+}
+
+int32_t DMASample::GetRaw(Encoder *input) const {
+  int index = input->GetFPGAIndex();
+  uint32_t dmaWord = 0;
+#ifdef WPILIB2015
+  if (index >= 4 || offset(kEnable_Encoders) == -1) {
+    wpi_setStaticErrorWithContext(
+        dma_, NiFpga_Status_ResourceNotFound,
+        getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+    return -1;
+  }
+  dmaWord = read_buffer_[offset(kEnable_Encoders) + index];
+#else
+  if (index < 4) {
+    if (offset(kEnable_Encoders_Low) == -1) {
+      wpi_setStaticErrorWithContext(
+          dma_, NiFpga_Status_ResourceNotFound,
+          getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+      return -1;
+    }
+    dmaWord = read_buffer_[offset(kEnable_Encoders_Low) + index];
+  } else if (index < 8) {
+    if (offset(kEnable_Encoders_High) == -1) {
+      wpi_setStaticErrorWithContext(
+          dma_, NiFpga_Status_ResourceNotFound,
+          getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+      return -1;
+    }
+    dmaWord = read_buffer_[offset(kEnable_Encoders_High) + (index - 4)];
+  } else {
+    wpi_setStaticErrorWithContext(
+        dma_, NiFpga_Status_ResourceNotFound,
+        getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+    return 0;
+  }
+#endif
+
+  int32_t result = 0;
+
+  // Extract the 31-bit signed tEncoder::tOutput Value using a struct with the
+  // reverse packed field order of tOutput. This gets Value from the high
+  // order 31 bits of output on little-endian ARM using gcc. This works
+  // even though C/C++ doesn't guarantee bitfield order.
+  t1Output output;
+
+  output.value = dmaWord;
+  result = output.Value;
+
+  return result;
+}
+
+int32_t DMASample::Get(Encoder *input) const {
+  int32_t raw = GetRaw(input);
+
+  return raw / input->GetEncodingScale();
+}
+
+uint16_t DMASample::GetValue(AnalogInput *input) const {
+  uint32_t channel = input->GetChannel();
+  uint32_t dmaWord;
+  if (channel < 4) {
+    if (offset(kEnable_AI0_Low) == -1) {
+      wpi_setStaticErrorWithContext(
+          dma_, NiFpga_Status_ResourceNotFound,
+          getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+      return 0xffff;
+    }
+    dmaWord = read_buffer_[offset(kEnable_AI0_Low) + channel / 2];
+  } else if (channel < 8) {
+    if (offset(kEnable_AI0_High) == -1) {
+      wpi_setStaticErrorWithContext(
+          dma_, NiFpga_Status_ResourceNotFound,
+          getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+      return 0xffff;
+    }
+    dmaWord = read_buffer_[offset(kEnable_AI0_High) + (channel - 4) / 2];
+  } else {
+    wpi_setStaticErrorWithContext(
+        dma_, NiFpga_Status_ResourceNotFound,
+        getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+    return 0xffff;
+  }
+  if (channel % 2) {
+    return (dmaWord >> 16) & 0xffff;
+  } else {
+    return dmaWord & 0xffff;
+  }
+  return static_cast<int16_t>(dmaWord);
+}
+
+float DMASample::GetVoltage(AnalogInput *input) const {
+  uint16_t value = GetValue(input);
+  if (value == 0xffff) return 0.0;
+  uint32_t lsb_weight = input->GetLSBWeight();
+  int32_t offset = input->GetOffset();
+  float voltage = lsb_weight * 1.0e-9 * value - offset * 1.0e-9;
+  return voltage;
+}
diff --git a/frc971/wpilib/dma.h b/frc971/wpilib/dma.h
new file mode 100644
index 0000000..970497c
--- /dev/null
+++ b/frc971/wpilib/dma.h
@@ -0,0 +1,135 @@
+#ifndef FRC971_WPILIB_DMA_H_
+#define FRC971_WPILIB_DMA_H_
+
+// Interface to the roboRIO FPGA's DMA features.
+// TODO(Brian): Make this less wpilib-like and more frc971-like.
+
+#include <stdint.h>
+
+#include <array>
+#include <memory>
+
+#include "ChipObject.h"
+#include "ErrorBase.h"
+
+class DMA;
+class DigitalSource;
+class AnalogInput;
+class Encoder;
+
+// A POD class which stores the data from a DMA sample and provides safe ways to
+// access it.
+class DMASample {
+ public:
+  DMASample() = default;
+
+  // Returns the FPGA timestamp of the sample in seconds.
+  double GetTimestamp() const;
+  // Returns the FPGA timestamp of the sample in microseconds.
+  uint32_t GetTime() const;
+
+  // All Get methods either return the requested value, or set the Error.
+
+  // Returns the value of the digital input in the sample.
+  bool Get(DigitalSource *input) const;
+  // Returns the raw value of the encoder in the sample.
+  int32_t GetRaw(Encoder *input) const;
+  // Returns the {1, 2, or 4} X scaled value of the encoder in the sample.
+  int32_t Get(Encoder *input) const;
+  // Returns the raw 12-bit value from the ADC.
+  uint16_t GetValue(AnalogInput *input) const;
+  // Returns the scaled value of an analog input.
+  float GetVoltage(AnalogInput *input) const;
+
+ private:
+  friend DMA;
+
+  // Returns the offset of the sample type in the buffer, or -1 if it isn't in
+  // the sample.
+  ssize_t offset(int index) const;
+
+  // TODO(austin): This should be re-used from WPILib...  Once I merge this back
+  // into WPILib.
+
+  DMA *dma_;
+  uint32_t read_buffer_[64];
+};
+
+// TODO(austin): ErrorBase...
+class DMA : public ErrorBase {
+ public:
+  DMA();
+  virtual ~DMA();
+
+  // Sets whether or not DMA is paused.
+  // If not specified, the default is false.
+  void SetPause(bool pause);
+
+  // Sets the number of triggers that need to occur before a sample is saved.
+  // If not specified, the default is 1.
+  void SetRate(uint32_t cycles);
+
+  // Adds the input signal to the state to snapshot on the trigger event.
+  // It is safe to add the same input multiple times, but there is currently
+  // no way to remove one once it has been added.
+  // Call Add() and SetExternalTrigger() before Start().
+  void Add(Encoder *encoder);
+  void Add(DigitalSource *input);
+  void Add(AnalogInput *input);
+
+  // Configures DMA to trigger on an external trigger.  There can only be 4
+  // external triggers.
+  // Call Add() and SetExternalTrigger() before Start().
+  void SetExternalTrigger(DigitalSource *input, bool rising, bool falling);
+
+  // Starts reading samples into the buffer.  Clears all previous samples before
+  // starting.
+  // Call Start() before Read().
+  void Start(size_t queue_depth);
+
+  enum ReadStatus {
+    STATUS_OK = 0,
+    STATUS_TIMEOUT = 1,
+    STATUS_ERROR = 2,
+  };
+
+  // Reads a sample from the DMA buffer, waiting up to timeout_ms for it.
+  // Returns a status code indicating whether the read worked, timed out, or
+  // failed.
+  // Returns in *remaining_out the number of DMA samples still queued after this
+  // Read().
+  // Call Add() and SetExternalTrigger() then Start() before Read().
+  // The sample is only usable while this DMA object is left started.
+  ReadStatus Read(DMASample *sample, uint32_t timeout_ms,
+                  size_t *remaining_out);
+
+  // Translates a ReadStatus code to a string name.
+  static const char *NameOfReadStatus(ReadStatus s);
+
+ private:
+  ::std::unique_ptr<nFPGA::tDMAManager> manager_;  // set by Start()
+  typedef nFPGA::nRoboRIO_FPGANamespace::tDMA tDMA;
+  friend DMASample;
+
+  // The offsets into the sample structure for each DMA type, or -1 if it isn't
+  // in the set of values.
+#ifdef WPILIB2015
+  ssize_t channel_offsets_[18];
+#else
+  ssize_t channel_offsets_[20];
+#endif
+
+  // The size of the data to read to get a sample.
+  size_t capture_size_ = 0;
+  tDMA::tConfig tconfig_;
+  tDMA *tdma_config_;
+
+#ifndef WPILIB2015
+  ::std::array<bool, 8> trigger_channels_ = {
+      {false, false, false, false, false, false, false, false}};
+#else
+  ::std::array<bool, 4> trigger_channels_ = {{false, false, false, false}};
+#endif
+};
+
+#endif  // FRC971_WPILIB_DMA_H_
diff --git a/frc971/wpilib/dma_edge_counting.h b/frc971/wpilib/dma_edge_counting.h
index 37f5837..cdbff1d 100644
--- a/frc971/wpilib/dma_edge_counting.h
+++ b/frc971/wpilib/dma_edge_counting.h
@@ -6,11 +6,12 @@
 
 #include "aos/common/macros.h"
 
+#include "frc971/wpilib/dma.h"
+
 #include "DigitalInput.h"
 #include "Encoder.h"
 #include "AnalogInput.h"
 #include "Utility.h"
-#include "dma.h"
 #undef ERROR
 
 namespace frc971 {
diff --git a/frc971/wpilib/encoder_and_potentiometer.h b/frc971/wpilib/encoder_and_potentiometer.h
index 2450841..7f784e9 100644
--- a/frc971/wpilib/encoder_and_potentiometer.h
+++ b/frc971/wpilib/encoder_and_potentiometer.h
@@ -10,9 +10,9 @@
 #include "Encoder.h"
 #include "DigitalSource.h"
 #include "AnalogInput.h"
-#include "dma.h"
 
 #include "frc971/wpilib/dma_edge_counting.h"
+#include "frc971/wpilib/dma.h"
 
 namespace frc971 {
 namespace wpilib {