Merge a fix for a Task bug which breaks the RWLock test
diff --git a/aos/atom_code/input/FRCComm.h b/aos/atom_code/input/FRCComm.h
index 14c4602..98619de 100644
--- a/aos/atom_code/input/FRCComm.h
+++ b/aos/atom_code/input/FRCComm.h
@@ -124,7 +124,6 @@
 	UINT32 FPGAChecksum3;
 
 	char versionData[8];
-};
-
+} __attribute__((packed));
 
 #endif
diff --git a/aos/atom_code/output/MotorOutput.cpp b/aos/atom_code/output/MotorOutput.cpp
deleted file mode 100644
index ecbdf8b..0000000
--- a/aos/atom_code/output/MotorOutput.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "aos/atom_code/output/MotorOutput.h"
-
-#include <math.h>
-
-#include "aos/common/Configuration.h"
-#include "aos/aos_core.h"
-#include "aos/common/byteorder.h"
-#include "aos/common/control_loop/Timing.h"
-#include "aos/atom_code/messages/DriverStationDisplay.h"
-
-namespace aos {
-
-MotorOutput::MotorOutput() : sock(NetworkPort::kMotors,
-          configuration::GetIPAddress(configuration::NetworkDevice::kCRIO)) {}
-
-void MotorOutput::Run() {
-  while (true) {
-    time::PhasedLoopXMS(5, 1000);
-    RunIteration();
-
-    // doesn't matter if multiple instances end up running this loop at the same
-    // time because the queue handles the race conditions
-    while (true) {
-      const aos::DriverStationDisplay *line = aos::DriverStationDisplay::GetNext();
-      if (line != NULL) {
-        AddDSLine(line->line, line->data);
-        LOG(DEBUG, "got a line %hhd that said '%s'\n", line->line, line->data);
-        aos::DriverStationDisplay::Free(line);
-      } else {
-        break;
-      }
-    }
-
-    if (sock.SendHoldMsg() == -1) {
-      LOG(WARNING, "sending outputs failed\n");
-      continue;
-    } else {
-      LOG(DEBUG, "sent outputs\n");
-    }
-  }
-}
-
-int MotorOutput::AddMotor(char type, uint8_t num, float value) {
-  if (sock.hold_msg_len_ + 4 > sock.MAX_MSG) {
-    return -1;
-  }
-  sock.hold_msg_[sock.hold_msg_len_ ++] = type;
-  sock.hold_msg_[sock.hold_msg_len_ ++] = num;
-  to_network(&value, &sock.hold_msg_[sock.hold_msg_len_]);
-  sock.hold_msg_len_ += 4;
-  return 0;
-}
-int MotorOutput::AddSolenoid(uint8_t port, bool on) {
-  if (sock.hold_msg_len_ + 3 > sock.MAX_MSG) {
-    return -1;
-  }
-  sock.hold_msg_[sock.hold_msg_len_ ++] = 's';
-  sock.hold_msg_[sock.hold_msg_len_ ++] = port;
-  sock.hold_msg_[sock.hold_msg_len_ ++] = on ? 1 : 0;
-  return 0;
-}
-
-int MotorOutput::AddDSLine(uint8_t line, const char *data) {
-  size_t data_len = strlen(data); // doesn't include terminating NULL
-  if (sock.hold_msg_len_ + 3 + data_len > sock.MAX_MSG) {
-    return -1;
-  }
-
-  sock.hold_msg_[sock.hold_msg_len_ ++] = 'd';
-  sock.hold_msg_[sock.hold_msg_len_ ++] = line;
-  sock.hold_msg_[sock.hold_msg_len_ ++] = data_len;
-  memcpy(&sock.hold_msg_[sock.hold_msg_len_], data, data_len);
-  sock.hold_msg_len_ += data_len;
-  return 0;
-}
-
-} // namespace aos
-
diff --git a/aos/atom_code/output/MotorOutput.h b/aos/atom_code/output/MotorOutput.h
deleted file mode 100644
index 925139a..0000000
--- a/aos/atom_code/output/MotorOutput.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef AOS_ATOM_CODE_OUTPUT_MOTOR_OUTPUT_H_
-#define AOS_ATOM_CODE_OUTPUT_MOTOR_OUTPUT_H_
-
-#include <stdint.h>
-#include <string.h>
-#include <algorithm>
-#include <string>
-
-#include "aos/common/network/SendSocket.h"
-#include "aos/common/byteorder.h"
-#include "aos/common/type_traits.h"
-
-namespace aos {
-
-class MotorOutput {
- public:
-  MotorOutput();
-  void Run();
-
-  // Constants for the first argument of AddMotor.
-  static const char VICTOR = 'v';
-  static const char TALON = 't';
-
- protected:
-  // num is 1-indexed
-  int AddMotor(char type, uint8_t num, float value);
-  int AddSolenoid(uint8_t num, bool on);
-  int AddDSLine(uint8_t line, const char *data);
-  // loop_queuegroup is expected to be a control loop queue group.
-  // This function will fetch the latest goal and serve it.
-  template <class T>
-  int AddControlLoopGoal(T *loop_queuegroup);
-
-  virtual void RunIteration() = 0;
-
- private:
-  SendSocket sock;
-};
-
-template <class T>
-int MotorOutput::AddControlLoopGoal(T *loop_queuegroup) {
-  typedef typename std::remove_reference<
-      decltype(*(loop_queuegroup->goal.MakeMessage().get()))>::type GoalType;
-  // TODO(aschuh): This assumes a static serialized message size.
-  const uint16_t size = GoalType::Size();
-  if (sock.hold_msg_len_ + 4 + 1 + size > sock.MAX_MSG) {
-    return -1;
-  }
-
-  const bool zero = !loop_queuegroup->goal.FetchLatest();
-
-  sock.hold_msg_[sock.hold_msg_len_ ++] = 'g';
-  const uint32_t hash = loop_queuegroup->hash();
-
-  // Place the loop hash at the front.
-  to_network(&hash, &sock.hold_msg_[sock.hold_msg_len_]);
-  sock.hold_msg_len_ += 4;
-
-  if (zero) {
-    GoalType zero_message;
-    zero_message.Zero();
-    zero_message.Serialize(&sock.hold_msg_[sock.hold_msg_len_]);
-    sock.hold_msg_len_ += size;
-    return -1;
-  } else {
-    loop_queuegroup->goal->Serialize(&sock.hold_msg_[sock.hold_msg_len_]);
-    sock.hold_msg_len_ += size;
-    return 0;
-  }
-}
-
-}  // namespace aos
-
-#endif
diff --git a/aos/atom_code/output/motor_output.cc b/aos/atom_code/output/motor_output.cc
new file mode 100644
index 0000000..d7022f5
--- /dev/null
+++ b/aos/atom_code/output/motor_output.cc
@@ -0,0 +1,85 @@
+#include "aos/atom_code/output/motor_output.h"
+
+#include <math.h>
+
+#include "aos/common/Configuration.h"
+#include "aos/aos_core.h"
+#include "aos/common/control_loop/Timing.h"
+
+namespace aos {
+
+// 255 = 2.5ms, 0 = 0.5ms (or something close to that)
+// got 211->210 as first transition that made a speed difference and 53->54 on
+//   the other side
+// going 2 to each side to make sure we get the full range
+const MotorOutput::MotorControllerBounds MotorOutput::kTalonBounds
+    {213, 135, 132, 129, 50};
+
+uint8_t MotorOutput::MotorControllerBounds::Map(double value) const {
+  if (value == 0.0) return kCenter;
+  if (value > 0.0) {
+    return static_cast<uint8_t>(kDeadbandMax + (value * (kMax - kDeadbandMax)) +
+                                0.5);
+  } else {
+    return static_cast<uint8_t>(kDeadbandMin + (value * (kDeadbandMin - kMin)) +
+                                0.5);
+  }
+}
+
+MotorOutput::MotorOutput() : socket_(NetworkPort::kMotors,
+          configuration::GetIPAddress(configuration::NetworkDevice::kCRIO)) {}
+
+void MotorOutput::Run() {
+  while (true) {
+    time::PhasedLoopXMS(5, 1000);
+
+    values_.digital_module = -1;
+    // 0 means output disabled.
+    memset(&values_.pwm_outputs, 0x00, sizeof(values_.pwm_outputs));
+    values_.digital_output_enables = 0;
+    values_.digital_output_values = 0;
+    values_.pressure_switch_channel = 0;
+    values_.compressor_channel = 0;
+    values_.solenoid_module = -1;
+    values_.solenoid_values = 0;
+
+    RunIteration();
+
+    values_.digital_output_enables = hton(values_.digital_output_enables);
+    values_.digital_output_values = hton(values_.digital_output_values);
+
+    values_.FillInHashValue();
+    values_.hash_value = hton(values_.hash_value);
+
+    if (socket_.Send(&values_, sizeof(values_)) != sizeof(values_)) {
+      LOG(WARNING, "sending outputs failed\n");
+    } else {
+      LOG(DEBUG, "sent outputs\n");
+    }
+  }
+}
+
+void MotorOutput::SetSolenoid(uint8_t channel, bool set) {
+  if (set) {
+    values_.solenoid_values |= 1 << (channel - 1);
+  }
+}
+
+void MotorOutput::SetPWMOutput(uint8_t channel, double value,
+                               const MotorControllerBounds &bounds) {
+  values_.pwm_outputs[channel - 1] = bounds.Map(value);
+}
+
+void MotorOutput::DisablePWMOutput(uint8_t channel) {
+  values_.pwm_outputs[channel - 1] = 0;
+}
+
+void MotorOutput::SetDigitalOutput(uint8_t channel, bool value) {
+  const uint8_t shift_amount = 15 - channel;
+  values_.digital_output_enables |= 1 << shift_amount;
+  if (value) {
+    values_.digital_output_values |= 1 << shift_amount;
+  }
+}
+
+}  // namespace aos
diff --git a/aos/atom_code/output/motor_output.h b/aos/atom_code/output/motor_output.h
new file mode 100644
index 0000000..2c1af1d
--- /dev/null
+++ b/aos/atom_code/output/motor_output.h
@@ -0,0 +1,68 @@
+#ifndef AOS_ATOM_CODE_OUTPUT_MOTOR_OUTPUT_H_
+#define AOS_ATOM_CODE_OUTPUT_MOTOR_OUTPUT_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <algorithm>
+#include <string>
+
+#include "aos/common/network/SendSocket.h"
+#include "aos/common/byteorder.h"
+#include "aos/common/type_traits.h"
+#include "aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.h"
+
+namespace aos {
+
+// A class for sending output values to a cRIO.
+// values_ gets completely reset each time through, so RunIteration() needs to
+// set everything each time.
+class MotorOutput {
+ public:
+  MotorOutput();
+  void Run();
+
+  // A container for holding the constants that WPILib uses for each type of
+  // motor controller to map floating point values to 1-byte PWM values to
+  // actually use.
+  struct MotorControllerBounds {
+    // What 1.0 maps to.
+    const uint8_t kMax;
+    // The smallest value to map a positive speed to.
+    const uint8_t kDeadbandMax;
+    // What 0.0 maps to.
+    const uint8_t kCenter;
+    // The biggest value to map a negative speed to.
+    const uint8_t kDeadbandMin;
+    // What -1.0 maps to.
+    const uint8_t kMin;
+
+    // Applies the mapping.
+    uint8_t Map(double value) const;
+  };
+
+ protected:
+  // Brian got the values here by trying values with hardware on 11/23/12.
+  static const MotorControllerBounds kTalonBounds;
+
+  // Helper methods for filling out values_.
+  // All channels are the 1-indexed numbers that usually go into WPILib.
+  void SetSolenoid(uint8_t channel, bool set);
+  void SetPWMOutput(uint8_t channel, double value,
+                    const MotorControllerBounds &bounds);
+  void DisablePWMOutput(uint8_t channel);
+  void SetDigitalOutput(uint8_t channel, bool value);
+
+  // The struct that's going to get sent over.
+  // Gets reset (everything set so that it won't do anything) each time through.
+  NetworkRobotValues values_;
+
+ private:
+  // Subclasses need to actually fill out values_ here.
+  virtual void RunIteration() = 0;
+
+  SendSocket socket_;
+};
+
+}  // namespace aos
+
+#endif
diff --git a/aos/atom_code/output/motor_output_test.cc b/aos/atom_code/output/motor_output_test.cc
new file mode 100644
index 0000000..1e8d5fb
--- /dev/null
+++ b/aos/atom_code/output/motor_output_test.cc
@@ -0,0 +1,21 @@
+#include "gtest/gtest.h"
+
+#include "aos/atom_code/output/motor_output.h"
+
+namespace aos {
+namespace testing {
+
+TEST(MotorControllerBoundsTest, Limits) {
+  MotorOutput::MotorControllerBounds test_bounds
+      {200, 135, 128, 126, 110};
+  EXPECT_EQ(test_bounds.Map(1.0), test_bounds.kMax);
+  EXPECT_EQ(test_bounds.Map(-1.0), test_bounds.kMin);
+  EXPECT_EQ(test_bounds.Map(0.0), test_bounds.kCenter);
+  EXPECT_EQ(test_bounds.Map(0.55), 171);
+  EXPECT_EQ(test_bounds.Map(0.5), 168);
+  EXPECT_EQ(test_bounds.Map(0.45), 164);
+  EXPECT_EQ(test_bounds.Map(-0.5), 118);
+}
+
+}  // namespace testing
+}  // namespace aos
diff --git a/aos/atom_code/output/output.gyp b/aos/atom_code/output/output.gyp
index 65232b6..2dfcd0e 100644
--- a/aos/atom_code/output/output.gyp
+++ b/aos/atom_code/output/output.gyp
@@ -24,14 +24,28 @@
       'target_name': 'motor_output',
       'type': 'static_library',
       'sources': [
-        'MotorOutput.cpp',
+        'motor_output.cc',
       ],
       'dependencies': [
         '<(AOS)/common/network/network.gyp:socket',
-        '<(AOS)/build/aos.gyp:aos_core',
+        '<(AOS)/common/common.gyp:common',
+        '<(AOS)/common/common.gyp:timing',
+        '<(EXTERNALS):WPILib-NetworkRobotValues',
       ],
       'export_dependent_settings': [
         '<(AOS)/common/network/network.gyp:socket',
+        '<(EXTERNALS):WPILib-NetworkRobotValues',
+      ],
+    },
+    {
+      'target_name': 'motor_output_test',
+      'type': 'executable',
+      'sources': [
+        'motor_output_test.cc',
+      ],
+      'dependencies': [
+        'motor_output',
+        '<(EXTERNALS):gtest',
       ],
     },
   ],
diff --git a/aos/atom_code/starter/netconsole.cc b/aos/atom_code/starter/netconsole.cc
new file mode 100644
index 0000000..e2ba2b4
--- /dev/null
+++ b/aos/atom_code/starter/netconsole.cc
@@ -0,0 +1,219 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/Configuration.h"
+
+namespace aos {
+namespace {
+
+struct FDsToCopy {
+  const int input;
+  const int output;
+
+  const struct sockaddr_in *const interface_address;
+};
+
+void *FDCopyThread(void *to_copy_in) {
+  FDsToCopy *to_copy = static_cast<FDsToCopy *>(to_copy_in);
+
+  char buffer[32768];
+  ssize_t position = 0;
+  while (true) {
+    assert(position >= 0);
+    assert(position <= static_cast<ssize_t>(sizeof(buffer)));
+    if (position != sizeof(buffer)) {
+      ssize_t read_bytes;
+      bool good_data = true;
+      if (to_copy->interface_address != NULL) {
+        char control_buffer[0x100];
+        struct msghdr header;
+        memset(static_cast<void *>(&header), 0, sizeof(header));
+        header.msg_control = control_buffer;
+        header.msg_controllen = sizeof(control_buffer);
+        struct iovec iovecs[1];
+        iovecs[0].iov_base = buffer + position;
+        iovecs[0].iov_len = position - sizeof(buffer);
+        header.msg_iov = iovecs;
+        header.msg_iovlen = sizeof(iovecs) / sizeof(iovecs[0]);
+        read_bytes = recvmsg(to_copy->input, &header, 0);
+        if (read_bytes != -1) {
+          for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&header);
+               cmsg != NULL;
+               cmsg = CMSG_NXTHDR(&header, cmsg)) {
+            if (cmsg->cmsg_level == IPPROTO_IP &&
+                cmsg->cmsg_type == IP_PKTINFO) {
+              unsigned char *data = CMSG_DATA(cmsg);
+              struct in_pktinfo *pktinfo;
+              memcpy(&pktinfo, &data, sizeof(void *));
+              good_data = pktinfo->ipi_spec_dst.s_addr ==
+                  to_copy->interface_address->sin_addr.s_addr;
+            }
+          }
+        }
+      } else {
+        read_bytes = read(to_copy->input,
+                          buffer + position, position - sizeof(buffer));
+      }
+      if (read_bytes == -1) {
+        if (errno != EINTR) {
+          LOG(FATAL, "read(%d, %p, %zd) failed with %d: %s\n",
+              to_copy->input, buffer + position, position - sizeof(buffer),
+              errno, strerror(errno));
+        }
+      } else if (read_bytes == 0 && to_copy->interface_address == NULL) {
+        // read(2) says that this means EOF
+        return NULL;
+      }
+      if (good_data) {
+        position += read_bytes;
+      }
+    }
+
+    assert(position >= 0);
+    assert(position <= static_cast<ssize_t>(sizeof(buffer)));
+    if (position > 0) {
+      ssize_t sent_bytes = write(to_copy->output, buffer, position);
+      if (sent_bytes == -1) {
+        if (errno != EINTR) {
+          LOG(FATAL, "write(%d, %p, %zd) failed with %d: %s\n",
+              to_copy->output, buffer, position, errno, strerror(errno));
+        }
+      } else if (sent_bytes != 0) {
+        if (sent_bytes == position) {
+          position = 0;
+        } else {
+          memmove(buffer, buffer + sent_bytes, position - sent_bytes);
+          position -= sent_bytes;
+        }
+      }
+    }
+  }
+}
+
+int NetconsoleMain(int argc, char **argv) {
+  logging::Init();
+
+  int input, output;
+  if (argc > 1) {
+    output = open(argv[1], O_APPEND | O_CREAT | O_WRONLY | O_TRUNC, 0666);
+    if (output == -1) {
+      if (errno == EACCES || errno == ELOOP || errno == ENOSPC ||
+          errno == ENOTDIR || errno == EROFS || errno == ETXTBSY) {
+        fprintf(stderr, "Opening output file '%s' failed because of %s.\n",
+                argv[1], strerror(errno));
+        exit(EXIT_FAILURE);
+      }
+      LOG(FATAL, "open('%s', stuff, 0644) failed with %d: %s\n", argv[1],
+          errno, strerror(errno));
+    }
+    fprintf(stderr, "Writing output to '%s'.\n", argv[1]);
+    input = -1;
+    fprintf(stderr, "Not taking any input.\n");
+  } else {
+    output = STDOUT_FILENO;
+    fprintf(stderr, "Writing output to stdout.\n");
+    input = STDIN_FILENO;
+    fprintf(stderr, "Reading stdin.\n");
+  }
+
+  int on = 1;
+
+  int from_crio = socket(AF_INET, SOCK_DGRAM, 0);
+  if (from_crio == -1) {
+    LOG(FATAL, "socket(AF_INET, SOCK_DGRAM, 0) failed with %d: %s\n",
+        errno, strerror(errno));
+  }
+  if (setsockopt(from_crio, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
+    LOG(FATAL, "SOL_SOCKET::SO_REUSEADDR=%d(%d) failed with %d: %s\n",
+        on, from_crio, errno, strerror(errno));
+  }
+  if (setsockopt(from_crio, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) {
+    LOG(FATAL, "SOL_SOCKET::SO_BROADCAST=%d(%d) failed with %d: %s\n",
+        on, from_crio, errno, strerror(errno));
+  }
+  if (setsockopt(from_crio, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)) == -1) {
+    LOG(FATAL, "IPROTO_IP::IP_PKTINFO=%d(%d) failed with %d: %s\n",
+        on, from_crio, errno, strerror(errno));
+  }
+  union {
+    struct sockaddr_in in;
+    struct sockaddr addr;
+  } address;
+  address.in.sin_family = AF_INET;
+  address.in.sin_port = htons(6666);
+  address.in.sin_addr.s_addr = INADDR_ANY;
+  if (bind(from_crio, &address.addr, sizeof(address)) == -1) {
+    LOG(FATAL, "bind(%d, %p, %zu) failed with %d: %s\n",
+        from_crio, &address.addr, sizeof(address), errno, strerror(errno));
+  }
+
+  pthread_t input_thread, output_thread;
+
+  if (input != -1) {
+    int to_crio = socket(AF_INET, SOCK_DGRAM, 0);
+    if (to_crio == -1) {
+      LOG(FATAL, "socket(AF_INET, SOCK_DGRAM, 0) failed with %d: %s\n",
+          errno, strerror(errno));
+    }
+    if (setsockopt(to_crio, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
+      LOG(FATAL, "SOL_SOCKET::SO_REUSEADDR=%d(%d) failed with %d: %s\n",
+          on, to_crio, errno, strerror(errno));
+    }
+    address.in.sin_port = htons(6668);
+    if (inet_aton(
+            configuration::GetIPAddress(configuration::NetworkDevice::kCRIO),
+            &address.in.sin_addr) == 0) {
+      LOG(FATAL, "inet_aton(%s, %p) failed with %d: %s\n",
+          configuration::GetIPAddress(configuration::NetworkDevice::kCRIO),
+          &address.in.sin_addr, errno, strerror(errno));
+    }
+    if (connect(to_crio, &address.addr, sizeof(address)) == -1) {
+      LOG(FATAL, "connect(%d, %p, %zu) failed with %d: %s\n",
+          to_crio, &address.addr, sizeof(address), errno, strerror(errno));
+    }
+    FDsToCopy input_fds{input, to_crio, NULL};
+    if (pthread_create(&input_thread, NULL, FDCopyThread, &input_fds) == -1) {
+      LOG(FATAL, "pthread_create(%p, NULL, %p, %p) failed with %d: %s\n",
+          &input_thread, FDCopyThread, &input_fds, errno, strerror(errno));
+    }
+  }
+
+  fprintf(stderr, "Using cRIO IP %s.\n",
+          configuration::GetIPAddress(configuration::NetworkDevice::kCRIO));
+
+  if (inet_aton(
+          configuration::GetIPAddress(configuration::NetworkDevice::kSelf),
+          &address.in.sin_addr) == 0) {
+    LOG(FATAL, "inet_aton(%s, %p) failed with %d: %s\n",
+        configuration::GetIPAddress(configuration::NetworkDevice::kSelf),
+        &address.in.sin_addr, errno, strerror(errno));
+  }
+  FDsToCopy output_fds{from_crio, output, &address.in};
+  if (pthread_create(&output_thread, NULL, FDCopyThread, &output_fds) == -1) {
+    LOG(FATAL, "pthread_create(%p, NULL, %p, %p) failed with %d: %s\n",
+        &output_thread, FDCopyThread, &output_fds, errno, strerror(errno));
+  }
+
+  // input_thread will finish when stdin gets an EOF
+  if (pthread_join((input == -1) ? output_thread : input_thread, NULL) == -1) {
+    LOG(FATAL, "pthread_join(a_thread, NULL) failed with %d: %s\n",
+        errno, strerror(errno));
+  }
+  exit(EXIT_SUCCESS);
+}
+
+}  // namespace
+}  // namespace aos
+
+int main(int argc, char **argv) {
+  return ::aos::NetconsoleMain(argc, argv);
+}
diff --git a/aos/atom_code/starter/starter.gyp b/aos/atom_code/starter/starter.gyp
index 91b5870..183ec9e 100644
--- a/aos/atom_code/starter/starter.gyp
+++ b/aos/atom_code/starter/starter.gyp
@@ -1,6 +1,17 @@
 {
   'targets': [
     {
+      'target_name': 'netconsole',
+      'type': 'executable',
+      'sources': [
+        'netconsole.cc',
+      ],
+      'dependencies': [
+        '<(AOS)/build/aos.gyp:logging',
+        '<(AOS)/common/common.gyp:common',
+      ],
+    },
+    {
       'target_name': 'starter_exe',
       'type': 'executable',
       'sources': [
diff --git a/aos/atom_code/starter/starter.sh b/aos/atom_code/starter/starter.sh
index 63ccd42..33906c3 100755
--- a/aos/atom_code/starter/starter.sh
+++ b/aos/atom_code/starter/starter.sh
@@ -17,4 +17,4 @@
 #DUMPCAP_STDOUT_FILE=$(date "/home/driver/tmp/robot_logs/stdout_dumpcap.%F_%H-%M-%S")
 #chrt -o 0 bash -c "dumpcap -i eth0 -w ${DUMPCAP_LOG_FILE} -f 'not port 8080 and not net 10.9.71.13' > ${DUMPCAP_STDOUT_FILE}" &
 
-#chrt -o 0 bash -c 'while true; do socat UDP4-RECV:6666,reuseaddr STDIO > /home/driver/tmp/robot_logs/netconsole-`date +%s`; done' &
+chrt -o 0 bash -c 'while true; /home/driver/robot_code/bin/netconsole /home/driver/tmp/robot_logs/netconsole-`date +%s`; done' &
diff --git a/aos/build/aos.gyp b/aos/build/aos.gyp
index 9e464a5..1996cd8 100644
--- a/aos/build/aos.gyp
+++ b/aos/build/aos.gyp
@@ -8,7 +8,6 @@
     'conditions': [
       ['OS=="crio"', {
           'libaos_source_files': [
-            '<(AOS)/crio/Talon.cpp',
           ],
         }, {
           'libaos_source_files': [
diff --git a/aos/build/aos.gypi b/aos/build/aos.gypi
index 662a419..3f71412 100644
--- a/aos/build/aos.gypi
+++ b/aos/build/aos.gypi
@@ -44,6 +44,8 @@
     'defines': [
       '__STDC_FORMAT_MACROS',
       '_FORTIFY_SOURCE=2',
+      '__STDC_CONSTANT_MACROS',
+      '__STDC_LIMIT_MACROS',
     ],
     'ldflags': [
       '-pipe',
@@ -119,12 +121,14 @@
             ],
           ],
           'ldflags': [
-            '-mcpu=603',
+            '-mcpu=603e',
             '-mstrict-align',
             '-mlongcall',
           ],
           'cflags': [
-            '-mcpu=603',
+            # The Freescale MPC5200B (cRIO-FRC) and MPC5125 (cRIO-FRC II) both
+            # have MPC603e cores according to Freescale docs.
+            '-mcpu=603e',
             '-mstrict-align',
             '-mlongcall',
             '-isystem', '<(aos_abs)/externals/gccdist/WindRiver/gnu/3.4.4-vxworks-6.3/x86-win32/lib/gcc/powerpc-wrs-vxworks/3.4.4/include/',
diff --git a/aos/build/aos_all.gyp b/aos/build/aos_all.gyp
index 1d283a9..b4116f4 100644
--- a/aos/build/aos_all.gyp
+++ b/aos/build/aos_all.gyp
@@ -15,7 +15,8 @@
         '../atom_code/core/core.gyp:*',
         #'../atom_code/async_action:*', # TODO(brians) fix this broken test
         '../atom_code/ipc_lib/ipc_lib.gyp:*',
-        '../atom_code/starter/starter.gyp:*',
+        '../atom_code/starter/starter.gyp:starter_exe',
+        '../atom_code/starter/starter.gyp:netconsole',
         '../crio/crio.gyp:unsafe_queue_test',
         '../common/common.gyp:queue_test',
         #'../common/messages/messages.gyp:*', # TODO(brians) did this test ever exist?
diff --git a/aos/build/build.sh b/aos/build/build.sh
index cc349d0..c1cf166 100755
--- a/aos/build/build.sh
+++ b/aos/build/build.sh
@@ -58,24 +58,32 @@
 if [ "${ACTION}" == "clean" ]; then
   rm -r ${OUTDIR}
 else
-  if [ "${ACTION}" != "deploy" -a "${ACTION}" != "tests" ]; then
-					GYP_ACTION=${ACTION}
-	else
-					GYP_ACTION=
-	fi
+  if [ "${ACTION}" != "deploy" -a "${ACTION}" != "tests" -a "${ACTION}" != "redeploy" ]; then
+    GYP_ACTION=${ACTION}
+  else
+    GYP_ACTION=
+  fi
   ${NINJA} -C ${OUTDIR}/Default ${GYP_ACTION}
-  case ${ACTION} in
-    deploy)
-      [ ${PLATFORM} == atom ] && \
-        rsync --progress -c -r --rsync-path=/home/driver/bin/rsync \
-        ${OUTDIR}/Default/outputs/* \
-        driver@`${AOS}/build/get_ip fitpc`:/home/driver/robot_code/bin
-      [ ${PLATFORM} == crio ] && \
-        ncftpput `${AOS}/build/get_ip robot` / \
-        ${OUTDIR}/Default/lib/FRC_UserProgram.out
-      ;;
-    tests)
-      find ${OUTDIR}/Default/tests -executable -exec {} \;
-      ;;
-  esac
+  if [[ ${ACTION} == deploy || ${ACTION} == redeploy ]]; then
+    [ ${PLATFORM} == atom ] && \
+      rsync --progress -c -r --rsync-path=/home/driver/bin/rsync \
+      ${OUTDIR}/Default/outputs/* \
+      driver@`${AOS}/build/get_ip fitpc`:/home/driver/robot_code/bin
+    [ ${PLATFORM} == crio ] && \
+      ncftpput `${AOS}/build/get_ip robot` / \
+      ${OUTDIR}/Default/lib/FRC_UserProgram.out
+  fi
+  if [[ ${ACTION} == redeploy ]]; then
+    if [[ ${PLATFORM} != crio ]]; then
+      echo "Platform ${PLATFORM} does not support redeploy." 1>&2
+      exit 1
+    fi
+    ${OUTDIR}/../out_atom/Default/outputs/netconsole <<"END"
+unld "FRC_UserProgram.out"
+ld < FRC_UserProgram.out
+END
+  fi
+  if [[ ${ACTION} == tests ]]; then
+    find ${OUTDIR}/Default/tests -executable -exec {} \;
+  fi
 fi
diff --git a/aos/build/download_externals.sh b/aos/build/download_externals.sh
index 5e07d02..4f999b2 100755
--- a/aos/build/download_externals.sh
+++ b/aos/build/download_externals.sh
@@ -89,3 +89,10 @@
   CFLAGS='-m32' CXXFLAGS='-m32' LDFLAGS='-m32' \
   bash -c "cd ${LIBUSB_DIR} && ./configure \
   --prefix=`readlink -f ${LIBUSB_PREFIX}` && make && make install"
+
+# get the LLVM Compiler-RT source
+COMPILER_RT_TAG=RELEASE_32/final
+COMPILER_RT_VERSION=`echo ${COMPILER_RT_TAG} | sed s:/:_:`
+COMPILER_RT_DIR=${EXTERNALS}/compiler-rt-${COMPILER_RT_VERSION}
+COMPILER_RT_URL=http://llvm.org/svn/llvm-project/compiler-rt/tags/${COMPILER_RT_TAG}
+[ -d ${COMPILER_RT_DIR} ] || svn checkout ${COMPILER_RT_URL} ${COMPILER_RT_DIR}
diff --git a/aos/build/externals.gyp b/aos/build/externals.gyp
index 0fa5fed..06bd225 100644
--- a/aos/build/externals.gyp
+++ b/aos/build/externals.gyp
@@ -13,6 +13,7 @@
     'gflags_version': '2.0',
     'libusb_version': '1.0.9',
     'libusb_apiversion': '1.0',
+    'compiler_rt_version': 'RELEASE_32_final',
   },
   'targets': [
     {
@@ -42,6 +43,21 @@
       },
     },
     {
+      'target_name': 'WPILib-NetworkRobotValues',
+      'type': 'static_library',
+      'sources': [
+        '<(externals)/WPILib/WPILib/NetworkRobot/NetworkRobotValues.cpp'
+      ],
+      'include_dirs': [
+        '<(externals)/WPILib',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<(externals)/WPILib',
+        ],
+      },
+    },
+    {
       'target_name': 'onejar',
       'type': 'none',
       'direct_dependent_settings': {
@@ -187,4 +203,7 @@
       },
     },
   ],
+  'includes': [
+    'libgcc-additions/libgcc-additions.gypi',
+  ],
 }
diff --git a/aos/build/libgcc-additions/README b/aos/build/libgcc-additions/README
new file mode 100644
index 0000000..6a9a665
--- /dev/null
+++ b/aos/build/libgcc-additions/README
@@ -0,0 +1,26 @@
+This directory contains the stuff necessary to deal with the fact that the
+  libgcc.a from the old GCC that the cRIO has doesn't have all of the functions
+  that newer GCCs expect from it.
+
+[extra functions necessary for 4.5.2]
+I generated this diff with `powerpc-wrs-vxworks-nm \
+  gccdist/WindRiver/gnu/3.4.4-vxworks-6.3/x86-win32/lib/gcc/powerpc-wrs-vxworks/3.4.4/libgcc.a \
+  | fgrep '000 T' | awk ' { print $NF }'` (and using the same command with the
+  gccdist.a from the 4.5.2 GCC.
+  I then gave the outputs from both of those to diff and edited out by hand the
+  functions that just moved.
+__powisf2
+__powidf2
+__mulsc3
+__muldc3
+__divsc3
+__divdc3
+__bswapsi2
+__bswapdi2
+__floatundisf
+__floatundidf
+__eprintf
+
+eprintf looks like it's not needed.
+Compiler-RT thinks that bswapsi2 and bswapdi2 are only needed on arm, so it
+  only has arm assembly for them.
diff --git a/aos/build/libgcc-additions/_bswapdi2.o b/aos/build/libgcc-additions/_bswapdi2.o
new file mode 100644
index 0000000..8bc6f56
--- /dev/null
+++ b/aos/build/libgcc-additions/_bswapdi2.o
Binary files differ
diff --git a/aos/build/libgcc-additions/_bswapsi2.o b/aos/build/libgcc-additions/_bswapsi2.o
new file mode 100644
index 0000000..4700ad8
--- /dev/null
+++ b/aos/build/libgcc-additions/_bswapsi2.o
Binary files differ
diff --git a/aos/build/libgcc-additions/libgcc-additions.gypi b/aos/build/libgcc-additions/libgcc-additions.gypi
new file mode 100644
index 0000000..afa81f6
--- /dev/null
+++ b/aos/build/libgcc-additions/libgcc-additions.gypi
@@ -0,0 +1,35 @@
+{
+  'targets': [
+    {
+      'target_name': 'libgcc-4.5.2',
+      'type': 'static_library',
+      'variables': {
+        'compiler-rt': '<(externals)/compiler-rt-<(compiler_rt_version)',
+      },
+      'include_dirs': [
+        '<(compiler-rt)/lib',
+      ],
+      'defines': [
+        '_YUGA_BIG_ENDIAN=1',
+        '_YUGA_LITTLE_ENDIAN=0',
+        'UINT64_C(c)=c##ULL',
+      ],
+      'sources': [
+        '<(compiler-rt)/lib/powisf2.c',
+        '<(compiler-rt)/lib/powidf2.c',
+        '<(compiler-rt)/lib/mulsc3.c',
+        '<(compiler-rt)/lib/muldc3.c',
+        '<(compiler-rt)/lib/divsc3.c',
+        '<(compiler-rt)/lib/divdc3.c',
+        #'<(compiler-rt)/lib/bswapsi2.c',
+        '_bswapsi2.o',
+        #'<(compiler-rt)/lib/bswapdi2.c',
+        '_bswapdi2.o',
+        '<(compiler-rt)/lib/floatundisf.c',
+        '<(compiler-rt)/lib/floatundidf.c',
+
+        'libm.c',
+      ],
+    },
+  ],
+}
diff --git a/aos/build/libgcc-additions/libm.c b/aos/build/libgcc-additions/libm.c
new file mode 100644
index 0000000..ce04ce5
--- /dev/null
+++ b/aos/build/libgcc-additions/libm.c
@@ -0,0 +1,47 @@
+// Some of the Compiler-RT implementations we use want to use functions from
+// libm. However, GCC provides builtins for them, so they don't need to show up
+// in <math.h> Unfortunately, those GCC builtins seem to (sometimes, at least)
+// just emit calls to the functions themselves, so we have our own
+// implementations here. They might not be as fast as they could be, but they
+// are correct as far as I can tell.
+
+#include <stdint.h>
+
+double fmax(double a, double b) {
+  if (__builtin_isnan(a)) return b;
+  if (__builtin_isnan(b)) return a;
+  if (a > b) return a;
+  return b;
+}
+float fmaxf(float a, float b) {
+  if (__builtin_isnan(a)) return b;
+  if (__builtin_isnan(b)) return a;
+  if (a > b) return a;
+  return b;
+}
+
+double scalbn(double x, int exp) {
+  return x * (2 << exp);
+}
+float scalbnf(float x, int exp) {
+  return x * (2 << exp);
+}
+
+float logbf(float x) {
+  union {
+    float f;
+    int32_t d;
+  } converter;
+  converter.f = x;
+  int32_t ix = converter.d & 0x7fffffff;
+  int32_t rix;
+
+  if (ix == 0) {
+    return (float)-1.0 / __builtin_fabsf(x);
+  } else if (ix >= 0x7f800000) {
+    return x * x;
+  } else if (__builtin_expect((rix = ix >> 23) == 0, 0)) {
+    rix -= __builtin_clz(ix) - 9;
+  }
+  return (float)(rix - 127);
+}
diff --git a/aos/common/Configuration.cpp b/aos/common/Configuration.cpp
index 90de05d..f379aac 100644
--- a/aos/common/Configuration.cpp
+++ b/aos/common/Configuration.cpp
@@ -15,9 +15,32 @@
 #include <ifaddrs.h>
 #endif
 
-#include "aos/aos_core.h"
 #ifndef __VXWORKS__
+#include "aos/common/logging/logging.h"
 #include "aos/common/unique_malloc_ptr.h"
+#else
+#include <taskLib.h>
+#undef ERROR
+enum LogLevel {
+  DEBUG,
+  INFO,
+  WARNING,
+  ERROR = -1,
+  FATAL,
+};
+#define LOG(level, format, args...) do { \
+  fprintf(stderr, #level ": " format, ##args); \
+  if (level == FATAL) { \
+    printf("I am 0x%x suspending for debugging purposes.\n", taskIdSelf()); \
+    printf("\t`tt 0x%x` will give you a stack trace.\n", taskIdSelf()); \
+    fputs("\t`lkAddr` will reverse lookup a symbol for you.\n", stdout); \
+    fputs("\t`dbgHelp` and `help` have some useful commands in them.\n", stdout); \
+    taskSuspend(0); \
+    printf("You weren't supposed to resume 0x%x!!. Going to really die now.\n", \
+           taskIdSelf()); \
+    abort(); \
+  } \
+} while (0)
 #endif
 #include "aos/common/once.h"
 
@@ -142,6 +165,10 @@
       return RawIPAddress(179);
     case NetworkDevice::kCRIO:
       return RawIPAddress(2);
+    case NetworkDevice::kSelf:
+      char *ret = static_cast<char *>(malloc(kMaxAddrLength));
+      if (!GetOwnIPAddress(ret, kMaxAddrLength)) return NULL;
+      return ret;
   }
   LOG(FATAL, "Unknown network device.");
   return NULL;
diff --git a/aos/common/Configuration.h b/aos/common/Configuration.h
index 2f7e777..e1b7ef5 100644
--- a/aos/common/Configuration.h
+++ b/aos/common/Configuration.h
@@ -31,6 +31,8 @@
   // The computer that the cRIO talks to.
   kAtom,
   kCRIO,
+  // Whatever device this is being run on.
+  kSelf,
 };
 // Returns the IP address to get to the specified machine.
 // The return value should be passed to free(3) if it is no longer needed.
diff --git a/aos/common/common.gyp b/aos/common/common.gyp
index 7ae3f61..2151064 100644
--- a/aos/common/common.gyp
+++ b/aos/common/common.gyp
@@ -47,7 +47,6 @@
         'Configuration.cpp',
       ],
       'dependencies': [
-        '<(AOS)/build/aos.gyp:logging',
         'once',
       ],
       'export_dependent_settings': [
@@ -57,7 +56,12 @@
         ['OS=="crio"', {
           'dependencies': [
             '<(EXTERNALS):WPILib',
-        ]}],
+          ],
+        }, {
+          'dependencies': [
+            '<(AOS)/build/aos.gyp:logging',
+          ],
+        }],
       ],
     },
     {
diff --git a/aos/common/network/SendSocket.cpp b/aos/common/network/SendSocket.cpp
index 78aca79..c30ec32 100644
--- a/aos/common/network/SendSocket.cpp
+++ b/aos/common/network/SendSocket.cpp
@@ -25,18 +25,7 @@
     last_ret_ = 1;
   }
 
-  hold_msg_len_ = 0;
   return last_ret_ = 0;
 }
 
-int SendSocket::SendHoldMsg() {
-  if (hold_msg_len_ > 0) {
-    int val = Send(hold_msg_, hold_msg_len_);
-    hold_msg_len_ = 0;
-    return val;
-  }
-  return -1;
-}
-
-} // namespace aos
-
+}  // namespace aos
diff --git a/aos/common/network/SendSocket.h b/aos/common/network/SendSocket.h
index a0c1fe2..fca904a 100644
--- a/aos/common/network/SendSocket.h
+++ b/aos/common/network/SendSocket.h
@@ -7,7 +7,6 @@
 
 class SendSocket : public Socket {
  public:
-  //inline int Send(const void *buf, int length) { return Socket::Send(buf, length); }
   // Connect must be called before use.
   SendSocket() {}
   // Calls Connect automatically.
@@ -15,11 +14,6 @@
     Connect(port, robot_ip);
   }
   int Connect(NetworkPort port, const char *robot_ip, int type = SOCK_DGRAM);
-
-  static const size_t MAX_MSG = 4096;
-  char hold_msg_[MAX_MSG];
-  size_t hold_msg_len_;
-  int SendHoldMsg();
 };
 
 } // namespace aos
diff --git a/aos/crio/Talon.cpp b/aos/crio/Talon.cpp
deleted file mode 100644
index 54ef459..0000000
--- a/aos/crio/Talon.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "aos/crio/Talon.h"
-#include "WPILib/DigitalModule.h"
-
-Talon::Talon(UINT32 channel) : SafePWM(channel) {
-  // 255 = 2.5ms, 0 = 0.5ms (or something close to that)
-  // these numbers were determined empirically with real hardware by Brian
-  //   on 11/23/12
-  //   got 211->210 as first transition that made a speed difference and
-  //   53->54 on the other side
-  //   going 2 to each side to make sure we get the full range
-  SetBounds(213, 137, 132, 127, 50);
-  // 1X = every 5.05ms, 2X and 4x are multiples of that
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetRaw(m_centerPwm);
-}
-
-void Talon::Set(float speed, UINT8 /*syncGroup*/) { SetSpeed(speed); }
-float Talon::Get() { return GetSpeed(); }
-void Talon::Disable() { SetRaw(kPwmDisabled); }
-
-void Talon::PIDWrite(float output) { Set(output); }
diff --git a/aos/crio/Talon.h b/aos/crio/Talon.h
deleted file mode 100644
index 5942a77..0000000
--- a/aos/crio/Talon.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef AOS_CRIO_TALON_H_
-#define AOS_CRIO_TALON_H_
-
-#include "WPILib/SafePWM.h"
-#include "WPILib/SpeedController.h"
-#include "WPILib/PIDOutput.h"
-
-// Used for controlling a Talon speed controller. Non-standard API and
-// namespace so that the likely WPILib version will be drop-in replaceable.
-class Talon : public SafePWM, public SpeedController {
- public:
-  explicit Talon(UINT32 channel);
-
-  virtual void Set(float value, UINT8 syncGroup=0);
-  virtual float Get();
-  virtual void Disable();
-
-  virtual void PIDWrite(float output);
-};
-
-#endif  // AOS_CRIO_TALON_H_
diff --git a/aos/crio/motor_server/MotorControllerOutput.h b/aos/crio/motor_server/MotorControllerOutput.h
index 41c3546..6caebf7 100644
--- a/aos/crio/motor_server/MotorControllerOutput.h
+++ b/aos/crio/motor_server/MotorControllerOutput.h
@@ -3,8 +3,7 @@
 
 #include "aos/crio/motor_server/OutputDevice.h"
 
-#include "aos/crio/Talon.h"
-
+#include "WPILib/Talon.h"
 #include "WPILib/SpeedController.h"
 #include "WPILib/Jaguar.h"
 #include "WPILib/CANJaguar.h"
diff --git a/aos/externals/.gitignore b/aos/externals/.gitignore
index 7f6c98d..64c3acf 100644
--- a/aos/externals/.gitignore
+++ b/aos/externals/.gitignore
@@ -21,3 +21,4 @@
 /libusb-1.0.9-prefix/
 /libusb-1.0.9.tar.bz2
 /libusb-1.0.9/
+/compiler-rt-RELEASE_32_final/
diff --git a/aos/externals/WPILib/WPILib/ADXL345_I2C.cpp b/aos/externals/WPILib/WPILib/ADXL345_I2C.cpp
index 9880e0e..2a4040e 100644
--- a/aos/externals/WPILib/WPILib/ADXL345_I2C.cpp
+++ b/aos/externals/WPILib/WPILib/ADXL345_I2C.cpp
@@ -73,7 +73,7 @@
  */
 ADXL345_I2C::AllAxes ADXL345_I2C::GetAccelerations()
 {
-	AllAxes data = {0.0};
+	AllAxes data = {0.0, 0.0, 0.0};
 	INT16 rawData[3];
 	if (m_i2c)
 	{
diff --git a/aos/externals/WPILib/WPILib/ADXL345_SPI.cpp b/aos/externals/WPILib/WPILib/ADXL345_SPI.cpp
index 789448d..42d8dea 100644
--- a/aos/externals/WPILib/WPILib/ADXL345_SPI.cpp
+++ b/aos/externals/WPILib/WPILib/ADXL345_SPI.cpp
@@ -157,7 +157,7 @@
  */
 ADXL345_SPI::AllAxes ADXL345_SPI::GetAccelerations()
 {
-	AllAxes data = {0.0};
+	AllAxes data = {0.0, 0.0, 0.0};
 	INT16 rawData[3];
 	if (m_spi)
 	{
diff --git a/aos/externals/WPILib/WPILib/AnalogChannel.cpp b/aos/externals/WPILib/WPILib/AnalogChannel.cpp
index 834837f..26708c1 100644
--- a/aos/externals/WPILib/WPILib/AnalogChannel.cpp
+++ b/aos/externals/WPILib/WPILib/AnalogChannel.cpp
@@ -38,9 +38,9 @@
 	}
 
 	snprintf(buf, 64, "Analog Input %d (Module: %d)", channel, moduleNumber);
-	if (channels->Allocate((moduleNumber - 1) * kAnalogChannels + channel - 1, buf) == ~0ul)
+	if (channels->Allocate((moduleNumber - 1) * kAnalogChannels +
+                         channel - 1, buf, this) == ~0ul)
 	{
-		CloneError(channels);
 		return;
 	}
 	m_channel = channel;
@@ -86,7 +86,8 @@
  */
 AnalogChannel::~AnalogChannel()
 {
-	channels->Free((m_module->GetNumber() - 1) * kAnalogChannels + m_channel - 1);
+	channels->Free((m_module->GetNumber() - 1) * kAnalogChannels + m_channel - 1,
+                 this);
 }
 
 /**
diff --git a/aos/externals/WPILib/WPILib/AnalogModule.cpp b/aos/externals/WPILib/WPILib/AnalogModule.cpp
index d685c13..68e7940 100644
--- a/aos/externals/WPILib/WPILib/AnalogModule.cpp
+++ b/aos/externals/WPILib/WPILib/AnalogModule.cpp
@@ -14,7 +14,8 @@
 const long AnalogModule::kDefaultOversampleBits;
 const long AnalogModule::kDefaultAverageBits;
 const float AnalogModule::kDefaultSampleRate;
-SEM_ID AnalogModule::m_registerWindowSemaphore = NULL;
+// Needs to be global since the protected resource spans both module singletons.
+ReentrantSemaphore AnalogModule::m_registerWindowSemaphore;
 
 /**
  * Get an instance of an Analog Module.
@@ -71,12 +72,6 @@
 		SetAverageBits(i + 1, kDefaultAverageBits);
 		SetOversampleBits(i + 1, kDefaultOversampleBits);
 	}
-
-	if (m_registerWindowSemaphore == NULL)
-	{
-		// Needs to be global since the protected resource spans both module singletons.
-		m_registerWindowSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
-	}
 }
 
 /**
diff --git a/aos/externals/WPILib/WPILib/AnalogModule.h b/aos/externals/WPILib/WPILib/AnalogModule.h
index 3f69685..3ed44ca 100644
--- a/aos/externals/WPILib/WPILib/AnalogModule.h
+++ b/aos/externals/WPILib/WPILib/AnalogModule.h
@@ -48,7 +48,7 @@
 	virtual ~AnalogModule();
 
 private:
-	static SEM_ID m_registerWindowSemaphore;
+	static ReentrantSemaphore m_registerWindowSemaphore;
 
 	UINT32 GetNumActiveChannels();
 	UINT32 GetNumChannelsToActivate();
diff --git a/aos/externals/WPILib/WPILib/AnalogTrigger.cpp b/aos/externals/WPILib/WPILib/AnalogTrigger.cpp
index ffe45c1..2deeb54 100644
--- a/aos/externals/WPILib/WPILib/AnalogTrigger.cpp
+++ b/aos/externals/WPILib/WPILib/AnalogTrigger.cpp
@@ -21,10 +21,9 @@
 void AnalogTrigger::InitTrigger(UINT8 moduleNumber, UINT32 channel)
 {
 	Resource::CreateResourceObject(&triggers, tAnalogTrigger::kNumSystems);
-	UINT32 index = triggers->Allocate("Analog Trigger");
+	UINT32 index = triggers->Allocate("Analog Trigger", this);
 	if (index == ~0ul)
 	{
-		CloneError(triggers);
 		return;
 	}
 	m_index = (UINT8)index;
@@ -74,7 +73,7 @@
 
 AnalogTrigger::~AnalogTrigger()
 {
-	triggers->Free(m_index);
+	triggers->Free(m_index, this);
 	delete m_trigger;
 }
 
diff --git a/aos/externals/WPILib/WPILib/CAN/JaguarCANDriver.h b/aos/externals/WPILib/WPILib/CAN/JaguarCANDriver.h
index 35c82a5..0f8afe4 100644
--- a/aos/externals/WPILib/WPILib/CAN/JaguarCANDriver.h
+++ b/aos/externals/WPILib/WPILib/CAN/JaguarCANDriver.h
@@ -14,8 +14,6 @@
 #ifndef __JaguarCANDriver_h__
 #define __JaguarCANDriver_h__
 
-#include <VxWorks.h>
-
 #ifdef __cplusplus
 extern "C"
 {
diff --git a/aos/externals/WPILib/WPILib/CANJaguar.cpp b/aos/externals/WPILib/WPILib/CANJaguar.cpp
index cf03370..ec55460 100644
--- a/aos/externals/WPILib/WPILib/CANJaguar.cpp
+++ b/aos/externals/WPILib/WPILib/CANJaguar.cpp
@@ -1223,7 +1223,7 @@
 
 void CANJaguar::GetDescription(char *desc)
 {
-	sprintf(desc, "CANJaguar ID %d", m_deviceNumber);
+	snprintf(desc, 64, "CANJaguar ID %d", m_deviceNumber);
 }
 
 /**
diff --git a/aos/externals/WPILib/WPILib/ChipObject/tSystemInterface.h b/aos/externals/WPILib/WPILib/ChipObject/tSystemInterface.h
index 46786e8..d5008e1 100644
--- a/aos/externals/WPILib/WPILib/ChipObject/tSystemInterface.h
+++ b/aos/externals/WPILib/WPILib/ChipObject/tSystemInterface.h
@@ -12,9 +12,9 @@
    tSystemInterface(){}
    virtual ~tSystemInterface(){}
 
-   virtual const uint16_t getExpectedFPGAVersion()=0;
-   virtual const uint32_t getExpectedFPGARevision()=0;
-   virtual const uint32_t * const getExpectedFPGASignature()=0;
+   virtual uint16_t getExpectedFPGAVersion()=0;
+   virtual uint32_t getExpectedFPGARevision()=0;
+   virtual uint32_t * getExpectedFPGASignature()=0;
    virtual void getHardwareFpgaSignature(uint32_t *guid_ptr, tRioStatusCode *status)=0;
    virtual uint32_t getLVHandle(tRioStatusCode *status)=0;
    virtual uint32_t getHandle()=0;
diff --git a/aos/externals/WPILib/WPILib/Counter.cpp b/aos/externals/WPILib/WPILib/Counter.cpp
index a11c268..e7c17ad 100644
--- a/aos/externals/WPILib/WPILib/Counter.cpp
+++ b/aos/externals/WPILib/WPILib/Counter.cpp
@@ -20,10 +20,9 @@
 void Counter::InitCounter(Mode mode)
 {
 	Resource::CreateResourceObject(&counters, tCounter::kNumSystems);
-	UINT32 index = counters->Allocate("Counter");
+	UINT32 index = counters->Allocate("Counter", this);
 	if (index == ~0ul)
 	{
-		CloneError(counters);
 		return;
 	}
 	m_index = index;
@@ -183,7 +182,7 @@
 	}
 	delete m_counter;
 	m_counter = NULL;
-	counters->Free(m_index);
+	counters->Free(m_index, this);
 }
 
 /**
diff --git a/aos/externals/WPILib/WPILib/Dashboard.cpp b/aos/externals/WPILib/WPILib/Dashboard.cpp
index c513baf..2235b99 100644
--- a/aos/externals/WPILib/WPILib/Dashboard.cpp
+++ b/aos/externals/WPILib/WPILib/Dashboard.cpp
@@ -29,7 +29,7 @@
 {
 	m_userStatusData = new char[kMaxDashboardDataSize];
 	m_localBuffer = new char[kMaxDashboardDataSize];
-	m_localPrintBuffer = new char[kMaxDashboardDataSize * 2];
+	m_localPrintBuffer = new char[kLocalPrintBufferSize];
 	m_localPrintBuffer[0] = 0;
 	m_packPtr = m_localBuffer;
 	m_printSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
@@ -275,7 +275,7 @@
 	va_start (args, writeFmt);
 	{
 		Synchronized sync(m_printSemaphore);
-		vsprintf(m_localPrintBuffer + strlen(m_localPrintBuffer), writeFmt, args);
+		vsnprintf(m_localPrintBuffer + strlen(m_localPrintBuffer), kLocalPrintBufferSize - strlen(m_localPrintBuffer), writeFmt, args);
 		size = strlen(m_localPrintBuffer);
 	}
 	if (size > kMaxDashboardDataSize)
diff --git a/aos/externals/WPILib/WPILib/Dashboard.h b/aos/externals/WPILib/WPILib/Dashboard.h
index dfcf989..1856660 100644
--- a/aos/externals/WPILib/WPILib/Dashboard.h
+++ b/aos/externals/WPILib/WPILib/Dashboard.h
@@ -50,6 +50,7 @@
 	void Flush() {}
 private:
 	static const INT32 kMaxDashboardDataSize = USER_STATUS_DATA_SIZE - sizeof(UINT32) * 3 - sizeof(UINT8); // 13 bytes needed for 3 size parameters and the sequence number
+  static const size_t kLocalPrintBufferSize = kMaxDashboardDataSize * 2;
 
 	// Usage Guidelines...
 	DISALLOW_COPY_AND_ASSIGN(Dashboard);
diff --git a/aos/externals/WPILib/WPILib/DigitalModule.cpp b/aos/externals/WPILib/WPILib/DigitalModule.cpp
index 3e7f728..d715c65 100644
--- a/aos/externals/WPILib/WPILib/DigitalModule.cpp
+++ b/aos/externals/WPILib/WPILib/DigitalModule.cpp
@@ -63,7 +63,7 @@
 	if (m_fpgaDIO->readLoopTiming(&localStatus) != kExpectedLoopTiming)
 	{
 		char err[128];
-		sprintf(err, "DIO LoopTiming: %d, expecting: %d\n", m_fpgaDIO->readLoopTiming(&localStatus), kExpectedLoopTiming);
+		snprintf(err, sizeof(err), "DIO LoopTiming: %d, expecting: %d\n", m_fpgaDIO->readLoopTiming(&localStatus), kExpectedLoopTiming);
 		wpi_setWPIErrorWithContext(LoopTimingError, err);
 	}
 	m_fpgaDIO->writePWMConfig_Period(PWM::kDefaultPwmPeriod, &localStatus);
@@ -154,18 +154,7 @@
  */
 void DigitalModule::SetRelayForward(UINT32 channel, bool on)
 {
-	tRioStatusCode localStatus = NiFpga_Status_Success;
-	CheckRelayChannel(channel);
-	{
-		Synchronized sync(m_relaySemaphore);
-		UINT8 forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus);
-		if (on)
-			forwardRelays |= 1 << (channel - 1);
-		else
-			forwardRelays &= ~(1 << (channel - 1));
-		m_fpgaDIO->writeSlowValue_RelayFwd(forwardRelays, &localStatus);
-	}
-	wpi_setError(localStatus);
+  SetRelaysForward(1 << (channel - 1), on ? 0xFF : 0x00);
 }
 
 /**
@@ -175,18 +164,47 @@
  */
 void DigitalModule::SetRelayReverse(UINT32 channel, bool on)
 {
-	tRioStatusCode localStatus = NiFpga_Status_Success;
-	CheckRelayChannel(channel);
-	{
-		Synchronized sync(m_relaySemaphore);
-		UINT8 reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus);
-		if (on)
-			reverseRelays |= 1 << (channel - 1);
-		else
-			reverseRelays &= ~(1 << (channel - 1));
-		m_fpgaDIO->writeSlowValue_RelayRev(reverseRelays, &localStatus);
-	}
-	wpi_setError(localStatus);
+  SetRelaysReverse(1 << (channel - 1), on ? 0xFF : 0x00);
+}
+
+/**
+ * Set the state of multiple relays at the same time.
+ * For both parameters, 0b100000000 is channel 1 and 0b00000001 is channel 8.
+ * @param mask which relays to set
+ * @param values what to set the relays to
+ */
+void DigitalModule::SetRelaysForward(UINT8 mask, UINT8 values) {
+  tRioStatusCode localStatus = NiFpga_Status_Success;
+  {
+    Synchronized sync(m_relaySemaphore);
+    UINT8 current = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus);
+    // Clearr all of the bits that we're messing with first.
+    current &= ~mask;
+    // Then set only the ones that are supposed to be set.
+    current |= (mask & values);
+    m_fpgaDIO->writeSlowValue_RelayFwd(current, &localStatus);
+  }
+  wpi_setError(localStatus);
+}
+
+/**
+ * Set the state of multiple relays at the same time.
+ * For both parameters, 0b100000000 is channel 1 and 0b00000001 is channel 8.
+ * @param mask which relays to set
+ * @param values what to set the relays to
+ */
+void DigitalModule::SetRelaysReverse(UINT8 mask, UINT8 values) {
+  tRioStatusCode localStatus = NiFpga_Status_Success;
+  {
+    Synchronized sync(m_relaySemaphore);
+    UINT8 current = m_fpgaDIO->readSlowValue_RelayRev(&localStatus);
+    // Clearr all of the bits that we're messing with first.
+    current &= ~mask;
+    // Then set only the ones that are supposed to be set.
+    current |= (mask & values);
+    m_fpgaDIO->writeSlowValue_RelayRev(current, &localStatus);
+  }
+  wpi_setError(localStatus);
 }
 
 /**
@@ -248,7 +266,8 @@
 {
 	char buf[64];
 	snprintf(buf, 64, "DIO %d (Module %d)", channel, m_moduleNumber);
-	if (DIOChannels->Allocate(kDigitalChannels * (m_moduleNumber - 1) + channel - 1, buf) == ~0ul) return false;
+	if (DIOChannels->Allocate(kDigitalChannels * (m_moduleNumber - 1) +
+                            channel - 1, buf, this) == ~0ul) return false;
 	tRioStatusCode localStatus = NiFpga_Status_Success;
 	{
 		Synchronized sync(m_digitalSemaphore);
@@ -276,7 +295,29 @@
  */
 void DigitalModule::FreeDIO(UINT32 channel)
 {
-	DIOChannels->Free(kDigitalChannels * (m_moduleNumber - 1) + channel - 1);
+	DIOChannels->Free(kDigitalChannels * (m_moduleNumber - 1) + channel - 1,
+                    this);
+}
+
+/**
+ * Write multiple digital I/O bits to the FPGA at the same time.
+ * For both parameters, 0x0001 is channel 16 and 0x8000 is channel 1.
+ *
+ * @param mask Which bits to modify.
+ * @param values What to set all of the bits in mask to.
+ */
+void DigitalModule::SetDIOs(UINT16 mask, UINT16 values) {
+  tRioStatusCode localStatus = NiFpga_Status_Success;
+  {
+    Synchronized sync(m_digitalSemaphore);
+    UINT16 current = m_fpgaDIO->readDO(&localStatus);
+    // Clear all of the bits that we're messing with first.
+    current &= ~mask;
+    // Then set only the ones that are supposed to be set.
+    current |= (mask & values);
+    m_fpgaDIO->writeDO(current, &localStatus);
+  }
+  wpi_setError(localStatus);
 }
 
 /**
@@ -286,29 +327,9 @@
  * @param channel The Digital I/O channel
  * @param value The state to set the digital channel (if it is configured as an output)
  */
-void DigitalModule::SetDIO(UINT32 channel, short value)
+void DigitalModule::SetDIO(UINT32 channel, bool value)
 {
-	if (value != 0 && value != 1)
-	{
-		wpi_setWPIError(NonBinaryDigitalValue);
-		if (value != 0)
-			value = 1;
-	}
-	tRioStatusCode localStatus = NiFpga_Status_Success;
-	{
-		Synchronized sync(m_digitalSemaphore);
-		UINT16 currentDIO = m_fpgaDIO->readDO(&localStatus);
-		if(value == 0)
-		{
-			currentDIO = currentDIO & ~(1 << RemapDigitalChannel(channel - 1));
-		}
-		else if (value == 1)
-		{
-			currentDIO = currentDIO | (1 << RemapDigitalChannel(channel - 1));
-		} 
-		m_fpgaDIO->writeDO(currentDIO, &localStatus);
-	}
-	wpi_setError(localStatus);
+  SetDIOs(1 << RemapDigitalChannel(channel - 1), value ? 0xFFFF : 0x0000);
 }
 
 /**
@@ -421,7 +442,7 @@
 
 /**
  * Allocate a DO PWM Generator.
- * Allocate PWM generators so that they are not accidently reused.
+ * Allocate PWM generators so that they are not accidentally reused.
  * 
  * @return PWM Generator refnum
  */
@@ -429,7 +450,7 @@
 {
 	char buf[64];
 	snprintf(buf, 64, "DO_PWM (Module: %d)", m_moduleNumber);
-	return DO_PWMGenerators[(m_moduleNumber - 1)]->Allocate(buf);
+	return DO_PWMGenerators[(m_moduleNumber - 1)]->Allocate(buf, this);
 }
 
 /**
@@ -440,7 +461,7 @@
 void DigitalModule::FreeDO_PWM(UINT32 pwmGenerator)
 {
 	if (pwmGenerator == ~0ul) return;
-	DO_PWMGenerators[(m_moduleNumber - 1)]->Free(pwmGenerator);
+	DO_PWMGenerators[(m_moduleNumber - 1)]->Free(pwmGenerator, this);
 }
 
 /**
@@ -461,7 +482,7 @@
 }
 
 /**
- * Configure which DO channel the PWM siganl is output on
+ * Configure which DO channel the PWM signal is output on
  * 
  * @param pwmGenerator The generator index reserved by AllocateDO_PWM()
  * @param channel The Digital Output channel to output on
diff --git a/aos/externals/WPILib/WPILib/DigitalModule.h b/aos/externals/WPILib/WPILib/DigitalModule.h
index e80beb6..44f617f 100644
--- a/aos/externals/WPILib/WPILib/DigitalModule.h
+++ b/aos/externals/WPILib/WPILib/DigitalModule.h
@@ -29,13 +29,16 @@
 	void SetPWMPeriodScale(UINT32 channel, UINT32 squelchMask);
 	void SetRelayForward(UINT32 channel, bool on);
 	void SetRelayReverse(UINT32 channel, bool on);
+  void SetRelaysForward(UINT8 mask, UINT8 values);
+  void SetRelaysReverse(UINT8 mask, UINT8 values);
 	bool GetRelayForward(UINT32 channel);
 	UINT8 GetRelayForward();
 	bool GetRelayReverse(UINT32 channel);
 	UINT8 GetRelayReverse();
 	bool AllocateDIO(UINT32 channel, bool input);
 	void FreeDIO(UINT32 channel);
-	void SetDIO(UINT32 channel, short value);
+  void SetDIOs(UINT16 mask, UINT16 values);
+	void SetDIO(UINT32 channel, bool value);
 	bool GetDIO(UINT32 channel);
 	UINT16 GetDIO();
 	bool GetDIODirection(UINT32 channel);
diff --git a/aos/externals/WPILib/WPILib/DigitalOutput.cpp b/aos/externals/WPILib/WPILib/DigitalOutput.cpp
index a571066..4f45ee7 100644
--- a/aos/externals/WPILib/WPILib/DigitalOutput.cpp
+++ b/aos/externals/WPILib/WPILib/DigitalOutput.cpp
@@ -214,10 +214,9 @@
 void DigitalOutput::RequestInterrupts(tInterruptHandler handler, void *param)
 {
 	if (StatusIsFatal()) return;
-	UINT32 index = interruptsResource->Allocate("Sync Interrupt");
+	UINT32 index = interruptsResource->Allocate("Sync Interrupt", this);
 	if (index == ~0ul)
 	{
-		CloneError(interruptsResource);
 		return;
 	}
 	m_interruptIndex = index;
@@ -245,10 +244,9 @@
 void DigitalOutput::RequestInterrupts()
 {
 	if (StatusIsFatal()) return;
-	UINT32 index = interruptsResource->Allocate("Sync Interrupt");
+	UINT32 index = interruptsResource->Allocate("Sync Interrupt", this);
 	if (index == ~0ul)
 	{
-		CloneError(interruptsResource);
 		return;
 	}
 	m_interruptIndex = index;
diff --git a/aos/externals/WPILib/WPILib/DigitalSource.cpp b/aos/externals/WPILib/WPILib/DigitalSource.cpp
index 3c1e3a8..862b439 100644
--- a/aos/externals/WPILib/WPILib/DigitalSource.cpp
+++ b/aos/externals/WPILib/WPILib/DigitalSource.cpp
@@ -24,7 +24,7 @@
 	{
 		delete m_manager;
 		delete m_interrupt;
-		interruptsResource->Free(m_interruptIndex);
+		interruptsResource->Free(m_interruptIndex, this);
 	}
 }
 
@@ -39,10 +39,9 @@
 void DigitalSource::RequestInterrupts(tInterruptHandler handler, void *param)
 {
 	if (StatusIsFatal()) return;
-	UINT32 index = interruptsResource->Allocate("Async Interrupt");
+	UINT32 index = interruptsResource->Allocate("Async Interrupt", this);
 	if (index == ~0ul)
 	{
-		CloneError(interruptsResource);
 		return;
 	}
 	m_interruptIndex = index;
@@ -70,10 +69,9 @@
 void DigitalSource::RequestInterrupts()
 {
 	if (StatusIsFatal()) return;
-	UINT32 index = interruptsResource->Allocate("Sync Interrupt");
+	UINT32 index = interruptsResource->Allocate("Sync Interrupt", this);
 	if (index == ~0ul)
 	{
-		CloneError(interruptsResource);
 		return;
 	}
 	m_interruptIndex = index;
diff --git a/aos/externals/WPILib/WPILib/DoubleSolenoid.cpp b/aos/externals/WPILib/WPILib/DoubleSolenoid.cpp
index d05c7ff..4b3e473 100644
--- a/aos/externals/WPILib/WPILib/DoubleSolenoid.cpp
+++ b/aos/externals/WPILib/WPILib/DoubleSolenoid.cpp
@@ -37,15 +37,15 @@
 	Resource::CreateResourceObject(&m_allocated, tSolenoid::kNumDO7_0Elements * kSolenoidChannels);
 
 	snprintf(buf, 64, "Solenoid %d (Module %d)", m_forwardChannel, m_moduleNumber);
-	if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels + m_forwardChannel - 1, buf) == ~0ul)
+	if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels +
+                            m_forwardChannel - 1, buf, this) == ~0ul)
 	{
-		CloneError(m_allocated);
 		return;
 	}
 	snprintf(buf, 64, "Solenoid %d (Module %d)", m_reverseChannel, m_moduleNumber);
-	if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels + m_reverseChannel - 1, buf) == ~0ul)
+	if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels +
+                            m_reverseChannel - 1, buf, this) == ~0ul)
 	{
-		CloneError(m_allocated);
 		return;
 	}
 	m_forwardMask = 1 << (m_forwardChannel - 1);
@@ -90,11 +90,10 @@
  */
 DoubleSolenoid::~DoubleSolenoid()
 {
-	if (CheckSolenoidModule(m_moduleNumber))
-	{
-		m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels + m_forwardChannel - 1);
-		m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels + m_reverseChannel - 1);
-	}
+	m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels +
+                    m_forwardChannel - 1, this);
+	m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels +
+                    m_reverseChannel - 1, this);
 }
 
 /**
diff --git a/aos/externals/WPILib/WPILib/DoubleSolenoid.h b/aos/externals/WPILib/WPILib/DoubleSolenoid.h
index f9920a1..a4cd03a 100644
--- a/aos/externals/WPILib/WPILib/DoubleSolenoid.h
+++ b/aos/externals/WPILib/WPILib/DoubleSolenoid.h
@@ -38,7 +38,7 @@
 	ITable * GetTable();
 
 private:
-	virtual void InitSolenoid();
+	void InitSolenoid();
 
 	UINT32 m_forwardChannel; ///< The forward channel on the module to control.
 	UINT32 m_reverseChannel; ///< The reverse channel on the module to control.
diff --git a/aos/externals/WPILib/WPILib/DriverStation.cpp b/aos/externals/WPILib/WPILib/DriverStation.cpp
index ffe0e66..9c2cb91 100644
--- a/aos/externals/WPILib/WPILib/DriverStation.cpp
+++ b/aos/externals/WPILib/WPILib/DriverStation.cpp
@@ -19,8 +19,8 @@
 const UINT32 DriverStation::kBatteryChannel;
 const UINT32 DriverStation::kJoystickPorts;
 const UINT32 DriverStation::kJoystickAxes;
-const float DriverStation::kUpdatePeriod;
 DriverStation* DriverStation::m_instance = NULL;
+ReentrantSemaphore DriverStation::m_instanceSemaphore;
 UINT8 DriverStation::m_updateNumber = 0;
 
 /**
@@ -58,32 +58,7 @@
 
 	m_waitForDataSem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
 
-	m_controlData = new FRCCommonControlData;
-
-	// initialize packet number and control words to zero;
-	m_controlData->packetIndex = 0;
-	m_controlData->control = 0;
-
-	// set all joystick axis values to neutral; buttons to OFF
-	m_controlData->stick0Axis1 = m_controlData->stick0Axis2 = m_controlData->stick0Axis3 = 0;
-	m_controlData->stick1Axis1 = m_controlData->stick1Axis2 = m_controlData->stick1Axis3 = 0;
-	m_controlData->stick2Axis1 = m_controlData->stick2Axis2 = m_controlData->stick2Axis3 = 0;
-	m_controlData->stick3Axis1 = m_controlData->stick3Axis2 = m_controlData->stick3Axis3 = 0;
-	m_controlData->stick0Axis4 = m_controlData->stick0Axis5 = m_controlData->stick0Axis6 = 0;
-	m_controlData->stick1Axis4 = m_controlData->stick1Axis5 = m_controlData->stick1Axis6 = 0;
-	m_controlData->stick2Axis4 = m_controlData->stick2Axis5 = m_controlData->stick2Axis6 = 0;
-	m_controlData->stick3Axis4 = m_controlData->stick3Axis5 = m_controlData->stick3Axis6 = 0;
-	m_controlData->stick0Buttons = 0;
-	m_controlData->stick1Buttons = 0;
-	m_controlData->stick2Buttons = 0;
-	m_controlData->stick3Buttons = 0;
-
-	// initialize the analog and digital data.
-	m_controlData->analog1 = 0;
-	m_controlData->analog2 = 0;
-	m_controlData->analog3 = 0;
-	m_controlData->analog4 = 0;
-	m_controlData->dsDigitalIn = 0;
+	m_controlData = new FRCCommonControlData();
 
 	m_batteryChannel = new AnalogChannel(kBatteryModuleNumber, kBatteryChannel);
 
@@ -97,6 +72,7 @@
 
 DriverStation::~DriverStation()
 {
+  Synchronized sync(m_instanceSemaphore);
 	m_task.Stop();
 	semDelete(m_statusDataSemaphore);
 	delete m_batteryChannel;
@@ -106,6 +82,15 @@
 	// Unregister our semaphore.
 	setNewDataSem(0);
 	semDelete(m_packetDataAvailableSem);
+  semDelete(m_newControlData);
+}
+
+/**
+ * Gets a read lock on all of the data. Be careful with this; holding one of
+ * these locks prevents any new data from being read.
+ */
+RWLock::Locker DriverStation::GetDataReadLock() {
+  return RWLock::Locker(&m_dataLock, false);
 }
 
 void DriverStation::InitTask(DriverStation *ds)
@@ -113,6 +98,9 @@
 	ds->Run();
 }
 
+/**
+ * Gets called in a separate task to deal with actually reading any new data.
+ */
 void DriverStation::Run()
 {
 	int period = 0;
@@ -128,14 +116,17 @@
 			MotorSafetyHelper::CheckMotors();
 			period = 0;
 		}
-		if (m_userInDisabled)
-			FRC_NetworkCommunication_observeUserProgramDisabled();
-		if (m_userInAutonomous)
-			FRC_NetworkCommunication_observeUserProgramAutonomous();
-        if (m_userInTeleop)
-            FRC_NetworkCommunication_observeUserProgramTeleop();
-        if (m_userInTest)
-            FRC_NetworkCommunication_observeUserProgramTest();
+    {
+      RWLock::Locker userStateLocker(&m_userStateLock, false);
+		  if (m_userInDisabled)
+			  FRC_NetworkCommunication_observeUserProgramDisabled();
+		  if (m_userInAutonomous)
+			  FRC_NetworkCommunication_observeUserProgramAutonomous();
+      if (m_userInTeleop)
+        FRC_NetworkCommunication_observeUserProgramTeleop();
+      if (m_userInTest)
+        FRC_NetworkCommunication_observeUserProgramTest();
+    }
 	}
 }
 
@@ -144,6 +135,7 @@
  */
 DriverStation* DriverStation::GetInstance()
 {
+  Synchronized sync(m_instanceSemaphore);
 	if (m_instance == NULL)
 	{
 		m_instance = new DriverStation();
@@ -152,14 +144,18 @@
 }
 
 /**
- * Copy data from the DS task for the user.
- * If no new data exists, it will just be returned, otherwise
- * the data will be copied from the DS polling loop.
+ * Copy data from the DS task for the rest of the code to use.
  */
 void DriverStation::GetData()
 {
 	static bool lastEnabled = false;
-	getCommonControlData(m_controlData, WAIT_FOREVER);
+  {
+    // Only have to lock right around reading the data because we're the only
+    // task that ever modifies it.
+    RWLock::Locker write_lock(&m_dataLock, true);
+	  // Have to const_cast away the volatile and the const.
+	  getCommonControlData(const_cast<FRCCommonControlData *>(m_controlData), WAIT_FOREVER);
+  }
 	if (!lastEnabled && IsEnabled()) 
 	{
 		// If starting teleop, assume that autonomous just took up 15 seconds
@@ -177,7 +173,7 @@
 }
 
 /**
- * Copy status data from the DS task for the user.
+ * Copy status data to the DS task from the user.
  */
 void DriverStation::SetData()
 {
@@ -308,6 +304,7 @@
 	if (channel < 1 || channel > 4)
 		wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 4");
 
+	// TODO: Fix the lack of thread safety here (for reported_mask).
 	static UINT8 reported_mask = 0;
 	if (!(reported_mask & (1 >> channel)))
 	{
@@ -340,6 +337,7 @@
 	if (channel < 1 || channel > 8)
 		wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 8");
 
+	// TODO: Fix the lack of thread safety here (for reported_mask).
 	static UINT8 reported_mask = 0;
 	if (!(reported_mask & (1 >> channel)))
 	{
@@ -364,6 +362,8 @@
 	if (channel < 1 || channel > 8)
 		wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 8");
 
+	// TODO: Fix the lack of thread safety here (for both reported_mask and
+  // m_digitalOut).
 	static UINT8 reported_mask = 0;
 	if (!(reported_mask & (1 >> channel)))
 	{
@@ -388,35 +388,76 @@
 	return ((m_digitalOut >> (channel-1)) & 0x1) ? true : false;;
 }
 
+/**
+ * @return Whether or not the robot is currently enabled by the field controls.
+ */
 bool DriverStation::IsEnabled()
 {
 	return m_controlData->enabled;
 }
 
+/**
+ * @return Whether or not the robot is currently disabled by the field controls.
+ */
 bool DriverStation::IsDisabled()
 {
 	return !m_controlData->enabled;
 }
 
+/**
+ * Determines if the robot is currently in autonomous mode. Does not check
+ * whether the robot is enabled.
+ * @return Whether or not the robot is currently in autonomous mode.
+ */
 bool DriverStation::IsAutonomous()
 {
 	return m_controlData->autonomous;
 }
 
+/**
+ * Determines if the robot is currently in teleoperated mode. Does not check
+ * whether the robot is enabled.
+ * @return Whether or not the robot is currently in teleoperated mode.
+ */
 bool DriverStation::IsOperatorControl()
 {
+  RWLock::Locker data_locker(GetDataReadLock());
 	return !(m_controlData->autonomous || m_controlData->test);
 }
 
+/**
+ * Determines if the robot is currently in test mode. Does not check
+ * whether the robot is enabled.
+ * @return Whether or not the robot is currently in test mode.
+ */
 bool DriverStation::IsTest()
 {
 	return m_controlData->test;
 }
 
 /**
+ * @return What state the robot is currently in.
+ */
+DriverStation::FMSState DriverStation::GetCurrentState() {
+  RWLock::Locker data_locker(GetDataReadLock());
+  if (IsDisabled()) {
+    return FMSState::kDisabled;
+    // Or else it must be enabled (for all of the other ones).
+  } else if (IsAutonomous()) {
+    return FMSState::kAutonomous;
+  } else if (IsTest()) {
+    return FMSState::kTestMode;
+  } else {  // IsOperatorControl() has to return true now
+    return FMSState::kTeleop;
+  }
+}
+
+/**
  * Has a new control packet from the driver station arrived since the last time this function was called?
  * Warning: If you call this function from more than one place at the same time,
- * you will not get the get the intended behavior
+ * you will not get the get the intended behavior unless that behavior is
+ * exactly 1 of the places that you call it from getting a true after a packet
+ * arrives.
  * @return True if the control data has been updated since the last call.
  */
 bool DriverStation::IsNewControlData()
@@ -437,7 +478,6 @@
 /**
  * Return the DS packet number.
  * The packet number is the index of this set of data returned by the driver station.
- * Each time new data is received, the packet number (included with the sent data) is returned.
  * @return The driver station packet number
  */
 UINT32 DriverStation::GetPacketNumber()
@@ -447,13 +487,14 @@
 
 /**
  * Return the alliance that the driver station says it is on.
- * This could return kRed or kBlue
  * @return The Alliance enum
  */
 DriverStation::Alliance DriverStation::GetAlliance()
 {
-	if (m_controlData->dsID_Alliance == 'R') return kRed;
-	if (m_controlData->dsID_Alliance == 'B') return kBlue;
+  // Read it first to make sure that it doesn't change in between the checks.
+  char alliance = m_controlData->dsID_Alliance;
+	if (alliance == 'R') return kRed;
+	if (alliance == 'B') return kBlue;
 	wpi_assert(false);
 	return kInvalid;
 }
@@ -465,8 +506,9 @@
  */
 UINT32 DriverStation::GetLocation()
 {
-	wpi_assert ((m_controlData->dsID_Position >= '1') && (m_controlData->dsID_Position <= '3'));
-	return m_controlData->dsID_Position - '0';
+  char position = m_controlData->dsID_Position;
+	wpi_assert ((position >= '1') && (position <= '3'));
+	return position - '0';
 }
 
 /**
diff --git a/aos/externals/WPILib/WPILib/DriverStation.h b/aos/externals/WPILib/WPILib/DriverStation.h
index acd9b16..1fcf1e8 100644
--- a/aos/externals/WPILib/WPILib/DriverStation.h
+++ b/aos/externals/WPILib/WPILib/DriverStation.h
@@ -11,6 +11,9 @@
 #include "DriverStationEnhancedIO.h"
 #include "SensorBase.h"
 #include "Task.h"
+#include "Synchronized.h"
+#include "RWLock.h"
+#include "Base.h"
 
 struct FRCCommonControlData;
 class AnalogChannel;
@@ -23,14 +26,35 @@
 public:
 	enum Alliance {kRed, kBlue, kInvalid};
 
-	virtual ~DriverStation();
+  // Represents all of the states that FMS thinks of a robot as being in.
+  // NOTE: All of the ones except kDisabled mean that the robot is enabled too.
+  enum FMSState {
+    kDisabled,
+    kAutonomous,
+    kTeleop,
+    kTestMode,
+  };
+
 	static DriverStation *GetInstance();
 
+  RWLock::Locker GetDataReadLock();
+
 	static const UINT32 kBatteryModuleNumber = 1;
 	static const UINT32 kBatteryChannel = 8;
 	static const UINT32 kJoystickPorts = 4;
 	static const UINT32 kJoystickAxes = 6;
 
+  /**
+   * Returns the pointer to all of the data. This pointer will never change, but
+   * its contents will, so make sure to GetDataReadLock() if you want to make
+   * sure that it doesn't change while you're using it.
+   *
+   * You may NOT modify the contents!
+   */
+  const volatile struct FRCCommonControlData *GetControlData() {
+    return m_controlData;
+  }
+
 	float GetStickAxis(UINT32 stick, UINT32 axis);
 	short GetStickButtons(UINT32 stick);
 
@@ -41,9 +65,10 @@
 
 	bool IsEnabled();
 	bool IsDisabled();
-    bool IsAutonomous();
+  bool IsAutonomous();
 	bool IsOperatorControl();
-    bool IsTest();
+  bool IsTest();
+  FMSState GetCurrentState();
 	bool IsNewControlData();
 	bool IsFMSAttached();
 
@@ -93,23 +118,47 @@
      *   for diagnostic purposes only
      * @param entering If true, starting test code; if false, leaving test code */
     void InTest(bool entering) {m_userInTest=entering;}
+  /**
+   * Get a pointer to the lock used for the data set by the In* methods.
+   * Creating write locks on this is useful if you want to atomically modify the
+   * information about what code you claim to be executing.
+   * @return A pointer to the lock. Be aware that the code that looks at this
+   * state (using a read lock) runs after the code that reads new packets and
+   * must finish before a new one can be read.
+   * @see #InDisabled(bool)
+   * @see #InAutonomous(bool)
+   * @see #InOperatorControl(bool)
+   * @see #InTest(bool)
+   */
+  RWLock *GetUserStateLock() { return &m_userStateLock; }
 
 protected:
 	DriverStation();
+	virtual ~DriverStation();
 
 	void GetData();
 	void SetData();
 
 private:
-	static void InitTask(DriverStation *ds);
 	static DriverStation *m_instance;
+  static ReentrantSemaphore m_instanceSemaphore;
 	static UINT8 m_updateNumber;
-	///< TODO: Get rid of this and use the semaphore signaling
-	static const float kUpdatePeriod = 0.02;
 
+	static void InitTask(DriverStation *ds);
 	void Run();
 
-	struct FRCCommonControlData *m_controlData;
+  // Volatile because it gets modified by GetData() in a separate task. Be
+  // careful using values out of here (2-byte and 4-byte accesses are safe as
+  // long as they're aligned, which all of the ones in here should be). If you
+  // need consistent data, use m_dataLock.
+  // Const because it should never be modifed except by getCommonControlData,
+  // and that call has to const_cast away the volatile anyways.
+  const volatile struct FRCCommonControlData *m_controlData;
+  // A lock for *m_controlData.
+  // Read (not write) RWLock::Lockers for this get given out to users so that
+  // they can prevent updates to the data while they are doing stuff with it.
+  RWLock m_dataLock;
+
 	UINT8 m_digitalOut;
 	AnalogChannel *m_batteryChannel;
 	SEM_ID m_statusDataSemaphore;
@@ -118,15 +167,24 @@
 	Dashboard m_dashboardLow;
 	DashboardBase* m_dashboardInUseHigh;  // the current dashboard packers in use
 	DashboardBase* m_dashboardInUseLow;
+  // Used to indicate when there is new control data available for
+  // IsNewControlData(). A semaphore instead of just a bool to avoid race
+  // conditions resulting in missed packets.
 	SEM_ID m_newControlData;
 	SEM_ID m_packetDataAvailableSem;
 	DriverStationEnhancedIO m_enhancedIO;
+  // Always empty. Gets semFlushed when there is new data available so that
+  // multiple tasks waiting for it can be woken at the same time.
 	SEM_ID m_waitForDataSem;
 	double m_approxMatchTimeOffset;
+
+  RWLock m_userStateLock;
 	bool m_userInDisabled;
 	bool m_userInAutonomous;
     bool m_userInTeleop;
     bool m_userInTest;
+
+  DISALLOW_COPY_AND_ASSIGN(DriverStation);
 };
 
 #endif
diff --git a/aos/externals/WPILib/WPILib/DriverStationLCD.h b/aos/externals/WPILib/WPILib/DriverStationLCD.h
index 3ea90a5..1a3731f 100644
--- a/aos/externals/WPILib/WPILib/DriverStationLCD.h
+++ b/aos/externals/WPILib/WPILib/DriverStationLCD.h
@@ -25,7 +25,6 @@
 	static const INT32 kNumLines = 6;
 	enum Line {kMain_Line6=0, kUser_Line1=0, kUser_Line2=1, kUser_Line3=2, kUser_Line4=3, kUser_Line5=4, kUser_Line6=5};
 
-	virtual ~DriverStationLCD();
 	static DriverStationLCD *GetInstance();
 
 	void UpdateLCD();
@@ -38,6 +37,7 @@
 
 protected:
 	DriverStationLCD();
+	virtual ~DriverStationLCD();
 
 private:
 	static void InitTask(DriverStationLCD *ds);
diff --git a/aos/externals/WPILib/WPILib/Encoder.cpp b/aos/externals/WPILib/WPILib/Encoder.cpp
index cdf1dba..cfb96c9 100644
--- a/aos/externals/WPILib/WPILib/Encoder.cpp
+++ b/aos/externals/WPILib/WPILib/Encoder.cpp
@@ -27,14 +27,11 @@
 {
 	m_encodingType = encodingType;
 	tRioStatusCode localStatus = NiFpga_Status_Success;
-	switch (encodingType)
-	{
-	case k4X:
+  if (encodingType == k4X) {
 		Resource::CreateResourceObject(&quadEncoders, tEncoder::kNumSystems);
-		UINT32 index = quadEncoders->Allocate("4X Encoder");
+		UINT32 index = quadEncoders->Allocate("4X Encoder", this);
 		if (index == ~0ul)
 		{
-			CloneError(quadEncoders);
 			return;
 		}
 		if (m_aSource->StatusIsFatal())
@@ -59,12 +56,9 @@
 		m_encoder->writeConfig_Reverse(reverseDirection, &localStatus);
 		m_encoder->writeTimerConfig_AverageSize(4, &localStatus);
 		m_counter = NULL;
-		break;
-	case k1X:
-	case k2X:
+  } else {
 		m_counter = new Counter(m_encodingType, m_aSource, m_bSource, reverseDirection);
 		m_index = m_counter->GetIndex();
-		break;
 	}
 	m_distancePerPulse = 1.0;
 	m_pidSource = kDistance;
@@ -195,7 +189,7 @@
 	}
 	else
 	{
-		quadEncoders->Free(m_index);
+		quadEncoders->Free(m_index, this);
 		delete m_encoder;
 	}
 }
diff --git a/aos/externals/WPILib/WPILib/Error.cpp b/aos/externals/WPILib/WPILib/Error.cpp
index 30f0cbc..9e83de7 100644
--- a/aos/externals/WPILib/WPILib/Error.cpp
+++ b/aos/externals/WPILib/WPILib/Error.cpp
@@ -25,8 +25,32 @@
 Error::~Error()
 {}
 
-void Error::Clone(Error &error)
+/**
+ * Clones another error into this if this is currently clear. If not, does
+ * nothing.
+ * This is necessary because just using "if (!IsClear()) Clone(error)" has a
+ * race condition which this method does not.
+ * Cloning 2 errors into each other at the same time can lead to deadlocks!
+ */
+void Error::CloneIfClear(const Error &error) {
+  Synchronized sync(m_semaphore);
+  if (IsClear()) {
+    DoClone(error);
+  }
+}
+
+/**
+ * Clones another error into this object.
+ * Cloning 2 errors into each other at the same time can lead to deadlocks!
+ */
+void Error::Clone(const Error &error) {
+  Synchronized sync(m_semaphore);
+  DoClone(error);
+}
+
+void Error::DoClone(const Error &error)
 {
+  Synchronized sync(error.m_semaphore);
 	m_code = error.m_code;
 	m_message = error.m_message;
 	m_filename = error.m_filename;
@@ -36,17 +60,19 @@
 	m_timestamp = error.m_timestamp;
 }
 
+bool Error::IsClear() const { return GetCode() == 0; }
+
 Error::Code Error::GetCode() const
 {	return m_code;  }
 
-const char * Error::GetMessage() const
-{	return m_message.c_str();  }
+std::string Error::GetMessage() const
+{	return m_message;  }
 
-const char * Error::GetFilename() const
-{	return m_filename.c_str();  }
+std::string Error::GetFilename() const
+{	return m_filename;  }
 
-const char * Error::GetFunction() const
-{	return m_function.c_str();  }
+std::string Error::GetFunction() const
+{	return m_function;  }
 
 UINT32 Error::GetLineNumber() const
 {	return m_lineNumber;  }
@@ -57,8 +83,10 @@
 double Error::GetTime() const
 {	return m_timestamp;  }
 
-void Error::Set(Code code, const char* contextMessage, const char* filename, const char* function, UINT32 lineNumber, const ErrorBase* originatingObject)
-{
+void Error::Set(Code code, const char* contextMessage, const char* filename,
+                const char* function, UINT32 lineNumber,
+                const ErrorBase* originatingObject)  {
+  Synchronized sync(m_semaphore);
 	m_code = code;
 	m_message = contextMessage;
 	m_filename = filename;
@@ -69,39 +97,40 @@
 
 	Report();
 
-	if (m_suspendOnErrorEnabled) taskSuspend(0);
+	if (m_suspendOnErrorEnabled) taskSuspend(0 /*self*/);
 }
 
-void Error::Report()
+void Error::Report() const
 {
 	// Error string buffers
-	char *error = new char[256];
-	char *error_with_code = new char[256];
+	char error[256];
+	char error_with_code[256];
 
 	// Build error strings
-	if (m_code != -1)
+	if (m_code != -1 && m_code != 1)
 	{
-		snprintf(error, 256, "%s: status = %d (0x%08X) %s ...in %s() in %s at line %d\n",
-				m_code < 0 ? "ERROR" : "WARNING", (INT32)m_code, (UINT32)m_code, m_message.c_str(),
-				m_function.c_str(), m_filename.c_str(), m_lineNumber);
-		sprintf(error_with_code,"<Code>%d %s", (INT32)m_code, error);
+		snprintf(error, sizeof(error),
+             "%s: status = %d (0x%08X) %s ...in %s() in %s at line %d\n",
+				     m_code < 0 ? "ERROR" : "WARNING", (INT32)m_code,
+             (UINT32)m_code, m_message.c_str(),
+				     m_function.c_str(), m_filename.c_str(), m_lineNumber);
+		snprintf(error_with_code, sizeof(error_with_code),
+             "<Code>%d %s", (INT32)m_code, error);
 	} else {
-		snprintf(error, 256, "ERROR: %s ...in %s() in %s at line %d\n", m_message.c_str(),
-				m_function.c_str(), m_filename.c_str(), m_lineNumber);
-		strcpy(error_with_code, error);
+		snprintf(error, sizeof(error),
+             "%s: %s ...in %s() in %s at line %d\n",
+             m_code < 0 ? "ERROR" : "WARNING", m_message.c_str(),
+				     m_function.c_str(), m_filename.c_str(), m_lineNumber);
+		strncpy(error_with_code, error, sizeof(error_with_code));
 	}
 	// TODO: Add logging to disk
 
 	// Send to the DriverStation
 	setErrorData(error_with_code, strlen(error_with_code), 100);
 
-	delete [] error_with_code;
-
 	// Print to console
 	printf("\n\n>>>>%s", error);
 
-	delete [] error;
-
 	if (m_stackTraceEnabled)
 	{
 		printf("-----------<Stack Trace>----------------\n");
@@ -111,6 +140,7 @@
 
 void Error::Clear()
 {
+  Synchronized sync(m_semaphore);
 	m_code = 0;
 	m_message = "";
 	m_filename = "";
diff --git a/aos/externals/WPILib/WPILib/Error.h b/aos/externals/WPILib/WPILib/Error.h
index a80fe06..9636262 100644
--- a/aos/externals/WPILib/WPILib/Error.h
+++ b/aos/externals/WPILib/WPILib/Error.h
@@ -10,37 +10,55 @@
 #include "Base.h"
 #include "ChipObject/NiRio.h"
 #include <string>
-#include <vxWorks.h>
+#include "Synchronized.h"
 
 //  Forward declarations
 class ErrorBase;
 
 /**
- * Error object represents a library error.
+ * Represents an error or warning.
+ *
+ * All methods that can change instance data are protected by a lock so
+ * that it is safe to call any methods from multiple tasks at the same time.
  */
 class Error
 {
 public:
+  // -1 is other error, 1 is other warning.
 	typedef tRioStatusCode Code;
 
 	Error();
 	~Error();
-	void Clone(Error &error);
-	Code GetCode() const;
-	const char *GetMessage() const;
-	const char *GetFilename() const;
-	const char *GetFunction() const;
-	UINT32 GetLineNumber() const;
-	const ErrorBase* GetOriginatingObject() const;
-	double GetTime() const;
+
+	void CloneIfClear(const Error &error);
+  void Clone(const Error &error);
 	void Clear();
 	void Set(Code code, const char* contextMessage, const char* filename,
 		const char *function, UINT32 lineNumber, const ErrorBase* originatingObject);
+
+  bool IsClear() const;
+	Code GetCode() const;
+  // Have to return by value to avoid race conditions using the result for all
+  // of these methods.
+  std::string GetMessage() const;
+  std::string GetFilename() const;
+  std::string GetFunction() const;
+	UINT32 GetLineNumber() const;
+	const ErrorBase* GetOriginatingObject() const;
+	double GetTime() const;
+
+  // Enable or disable printing out a stack trace on the console whenever there
+  // is an error.
 	static void EnableStackTrace(bool enable) { m_stackTraceEnabled=enable; }
+  // Enable or disable having any task that gets an error suspend itself.
 	static void EnableSuspendOnError(bool enable) { m_suspendOnErrorEnabled=enable; }
 
 private:
-	void Report();
+  // Deals with notifying other code of this error.
+	void Report() const;
+  // Actually implements cloning.
+  // Does not lock m_semaphore, so callers must.
+  void DoClone(const Error &error);
 
 	Code m_code;
 	std::string m_message;
@@ -49,6 +67,10 @@
 	UINT32 m_lineNumber;
 	const ErrorBase* m_originatingObject;
 	double m_timestamp;
+  // Used for protecting all modifications to instance data.
+  // This means that all non-const methods should lock this for (at least most)
+  // of their implementations!
+  ReentrantSemaphore m_semaphore;
 
 	static bool m_stackTraceEnabled;
 	static bool m_suspendOnErrorEnabled;
diff --git a/aos/externals/WPILib/WPILib/ErrorBase.cpp b/aos/externals/WPILib/WPILib/ErrorBase.cpp
index 2722963..5f766c7 100644
--- a/aos/externals/WPILib/WPILib/ErrorBase.cpp
+++ b/aos/externals/WPILib/WPILib/ErrorBase.cpp
@@ -14,8 +14,8 @@
 #include <symLib.h>
 #include <sysSymTbl.h>
 
-SEM_ID ErrorBase::_globalErrorMutex = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
 Error ErrorBase::_globalError;
+
 /**
  * @brief Initialize the instance status to 0 for now.
  */
@@ -26,15 +26,10 @@
 {}
 
 /**
- * @brief Retrieve the current error.
- * Get the current error information associated with this sensor.
+ * @brief Retrieve the error associated this object.
+ * Get the error information associated with this sensor.
  */
-Error& ErrorBase::GetError()
-{
-	return m_error;
-}
-
-const Error& ErrorBase::GetError() const
+Error& ErrorBase::GetError() const
 {
 	return m_error;
 }
@@ -48,7 +43,10 @@
 }
 
 /**
- * @brief Set error information associated with a C library call that set an error to the "errno" global variable.
+ * @brief Set error information associated with a C library call that set an
+ * error to the "errno" "global variable" (it's really a macro that calls a
+ * function under VxWorks so that it's thread safe).
+ * Will still set an error even if errno is 0.
  * 
  * @param contextMessage A custom message from the code that set the error.
  * @param filename Filename of the error source
@@ -62,7 +60,7 @@
 	int errNo = errnoGet();
 	if (errNo == 0)
 	{
-		sprintf(err, "OK: %s", contextMessage);
+		snprintf(err, sizeof(err), "OK: %s", contextMessage);
 	}
 	else
 	{
@@ -71,24 +69,21 @@
 		SYM_TYPE ptype;
 		symFindByValue(statSymTbl, errNo, statName, &pval, &ptype);
 		if (pval != errNo)
-			snprintf(err, 256, "Unknown errno 0x%08X: %s", errNo, contextMessage);
+			snprintf(err, sizeof(err), "Unknown errno 0x%08X: %s", errNo, contextMessage);
 		else
-			snprintf(err, 256, "%s (0x%08X): %s", statName, errNo, contextMessage);
+			snprintf(err, sizeof(err), "%s (0x%08X): %s", statName, errNo, contextMessage);
 		delete [] statName;
 	}
 
 	//  Set the current error information for this object.
 	m_error.Set(-1, err, filename, function, lineNumber, this);
 
-	// Update the global error if there is not one already set.
-	Synchronized mutex(_globalErrorMutex);
-	if (_globalError.GetCode() == 0) {
-		_globalError.Clone(m_error);
-	}
+  _globalError.CloneIfClear(m_error);
 }
 
 /**
  * @brief Set the current error information associated from the nivision Imaq API.
+ * Does nothing of success is > 0.
  * 
  * @param success The return from the function
  * @param contextMessage A custom message from the code that set the error.
@@ -101,21 +96,18 @@
 	//  If there was an error
 	if (success <= 0) {
 		char err[256];
-		sprintf(err, "%s: %s", contextMessage, imaqGetErrorText(imaqGetLastError()));
+		snprintf(err, sizeof(err), "%s: %s", contextMessage, imaqGetErrorText(imaqGetLastError()));
 
 		//  Set the current error information for this object.
 		m_error.Set(imaqGetLastError(), err, filename, function, lineNumber, this);
 
-		// Update the global error if there is not one already set.
-		Synchronized mutex(_globalErrorMutex);
-		if (_globalError.GetCode() == 0) {
-			_globalError.Clone(m_error);
-		}
+    _globalError.CloneIfClear(m_error);
 	}
 }
 
 /**
- * @brief Set the current error information associated with this sensor.
+ * @brief Set the current error information associated with this object.
+ * Does nothing if code is 0.
  * 
  * @param code The error code
  * @param contextMessage A custom message from the code that set the error.
@@ -131,16 +123,12 @@
 		//  Set the current error information for this object.
 		m_error.Set(code, contextMessage, filename, function, lineNumber, this);
 
-		// Update the global error if there is not one already set.
-		Synchronized mutex(_globalErrorMutex);
-		if (_globalError.GetCode() == 0) {
-			_globalError.Clone(m_error);
-		}
+    _globalError.CloneIfClear(m_error);
 	}
 }
 
 /**
- * @brief Set the current error information associated with this sensor.
+ * @brief Set the current error information associated with this object.
  * 
  * @param errorMessage The error message from WPIErrors.h
  * @param contextMessage A custom message from the code that set the error.
@@ -152,19 +140,15 @@
 		const char* filename, const char* function, UINT32 lineNumber) const
 {
 	char err[256];
-	sprintf(err, "%s: %s", errorMessage, contextMessage);
+	snprintf(err, sizeof(err), "%s: %s", errorMessage, contextMessage);
 
 	//  Set the current error information for this object.
 	m_error.Set(-1, err, filename, function, lineNumber, this);
 
-	// Update the global error if there is not one already set.
-	Synchronized mutex(_globalErrorMutex);
-	if (_globalError.GetCode() == 0) {
-		_globalError.Clone(m_error);
-	}
+  _globalError.CloneIfClear(m_error);
 }
 
-void ErrorBase::CloneError(ErrorBase *rhs) const
+void ErrorBase::CloneError(const ErrorBase *rhs) const
 {
 	m_error.Clone(rhs->GetError());
 }
@@ -179,37 +163,49 @@
 	return m_error.GetCode() < 0;
 }
 
+/**
+ * @brief Set the current global error information.
+ * Does nothing if code is 0.
+ * TODO: think about getting rid of this because nothing uses it any more
+ *
+ * @param code The error code
+ * @param contextMessage A custom message from the code that set the error.
+ * @param filename Filename of the error source
+ * @param function Function of the error source
+ * @param lineNumber Line number of the error source
+ */
 void ErrorBase::SetGlobalError(Error::Code code, const char *contextMessage,
 		const char* filename, const char* function, UINT32 lineNumber)
 {
-	//  If there was an error
-	if (code != 0) {
-		Synchronized mutex(_globalErrorMutex);
-
-		//  Set the current error information for this object.
-		_globalError.Set(code, contextMessage, filename, function, lineNumber, NULL);
-	}
+  if (code != 0) {
+	  //  Set the current error information for this object.
+	  _globalError.Set(code, contextMessage, filename, function, lineNumber, NULL);
+  }
 }
 
+/**
+ * @brief Set the current global error information.
+ *
+ * @param errorMessage The error message from WPIErrors.h
+ * @param contextMessage A custom message from the code that set the error.
+ * @param filename Filename of the error source
+ * @param function Function of the error source
+ * @param lineNumber Line number of the error source
+ */
 void ErrorBase::SetGlobalWPIError(const char *errorMessage, const char *contextMessage,
         const char* filename, const char* function, UINT32 lineNumber)
 {
 	char err[256];
-	sprintf(err, "%s: %s", errorMessage, contextMessage);
+	snprintf(err, sizeof(err), "%s: %s", errorMessage, contextMessage);
 
-	Synchronized mutex(_globalErrorMutex);
-	if (_globalError.GetCode() != 0) {
-		_globalError.Clear();
-	}
 	_globalError.Set(-1, err, filename, function, lineNumber, NULL);
 }
 
 /**
-  * Retrieve the current global error.    
+  * Retrieve the global error.
 */
-Error& ErrorBase::GetGlobalError()
+const Error& ErrorBase::GetGlobalError()
 {
-	Synchronized mutex(_globalErrorMutex);
 	return _globalError;
 }
 
diff --git a/aos/externals/WPILib/WPILib/ErrorBase.h b/aos/externals/WPILib/WPILib/ErrorBase.h
index f967562..9d032cb 100644
--- a/aos/externals/WPILib/WPILib/ErrorBase.h
+++ b/aos/externals/WPILib/WPILib/ErrorBase.h
@@ -10,59 +10,86 @@
 #include "Base.h"
 #include "ChipObject/NiRio.h"
 #include "Error.h"
-#include <semLib.h>
-#include <vxWorks.h>
 
-#define wpi_setErrnoErrorWithContext(context)   (this->SetErrnoError((context), __FILE__, __FUNCTION__, __LINE__))
-#define wpi_setErrnoError()   (wpi_setErrnoErrorWithContext(""))
-#define wpi_setImaqErrorWithContext(code, context)   (this->SetImaqError((code), (context), __FILE__, __FUNCTION__, __LINE__))
-#define wpi_setErrorWithContext(code, context)   (this->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+// Helper macros to fill in the context information for you. See the
+// documentation for the methods that they call for details.
+#define wpi_setErrnoErrorWithContext(context) \
+    (this->SetErrnoError((context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setErrnoError() \
+    (wpi_setErrnoErrorWithContext(""))
+#define wpi_setImaqErrorWithContext(code, context) \
+    (this->SetImaqError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setErrorWithContext(code, context) \
+        (this->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__))
 #define wpi_setError(code)   (wpi_setErrorWithContext(code, ""))
-#define wpi_setStaticErrorWithContext(object, code, context)   (object->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__))
-#define wpi_setStaticError(object, code)   (wpi_setStaticErrorWithContext(object, code, ""))
-#define wpi_setGlobalErrorWithContext(code, context)   (ErrorBase::SetGlobalError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setStaticErrorWithContext(object, code, context) \
+        (object->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setStaticError(object, code) \
+    (wpi_setStaticErrorWithContext(object, code, ""))
+#define wpi_setGlobalErrorWithContext(code, context) \
+        (ErrorBase::SetGlobalError((code), (context), \
+                                   __FILE__, __FUNCTION__, __LINE__))
 #define wpi_setGlobalError(code)   (wpi_setGlobalErrorWithContext(code, ""))
-#define wpi_setWPIErrorWithContext(error, context)   (this->SetWPIError((wpi_error_s_##error), (context), __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setWPIErrorWithContext(error, context) \
+        (this->SetWPIError((wpi_error_s_##error), (context), \
+                           __FILE__, __FUNCTION__, __LINE__))
 #define wpi_setWPIError(error)   (wpi_setWPIErrorWithContext(error, ""))
-#define wpi_setStaticWPIErrorWithContext(object, error, context)   (object->SetWPIError((wpi_error_s_##error), (context), __FILE__, __FUNCTION__, __LINE__))
-#define wpi_setStaticWPIError(object, error)   (wpi_setStaticWPIErrorWithContext(object, error, ""))
-#define wpi_setGlobalWPIErrorWithContext(error, context)   (ErrorBase::SetGlobalWPIError((wpi_error_s_##error), (context), __FILE__, __FUNCTION__, __LINE__))
-#define wpi_setGlobalWPIError(error)   (wpi_setGlobalWPIErrorWithContext(error, ""))
+#define wpi_setStaticWPIErrorWithContext(object, error, context) \
+        (object->SetWPIError((wpi_error_s_##error), (context), \
+                             __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setStaticWPIError(object, error) \
+    (wpi_setStaticWPIErrorWithContext(object, error, ""))
+#define wpi_setGlobalWPIErrorWithContext(error, context) \
+        (ErrorBase::SetGlobalWPIError((wpi_error_s_##error), (context), \
+                                      __FILE__, __FUNCTION__, __LINE__))
+#define wpi_setGlobalWPIError(error) \
+    (wpi_setGlobalWPIErrorWithContext(error, ""))
 
 /**
  * Base class for most objects.
  * ErrorBase is the base class for most objects since it holds the generated error
  * for that object. In addition, there is a single instance of a global error object
+ *
+ * BE AWARE: This does include a mutable instance variable! This means that even
+ * if you make an object const it's not really. However, all modification to
+ * that instance variable is protected by a semaphore, so it does not create any
+ * thread safety issues.
+ *
+ * All of the Set*Error methods will update the global error if there is nothing
+ * there already.
  */
 class ErrorBase
 {
-//TODO: Consider initializing instance variables and cleanup in destructor
 public:
+	ErrorBase();
 	virtual ~ErrorBase();
-	virtual Error& GetError();
-	virtual const Error& GetError() const;
-	virtual void SetErrnoError(const char *contextMessage,
+
+	Error& GetError() const;
+	void SetErrnoError(const char *contextMessage,
 		const char* filename, const char* function, UINT32 lineNumber) const;
-	virtual void SetImaqError(int success, const char *contextMessage,
+	void SetImaqError(int success, const char *contextMessage,
         const char* filename, const char* function, UINT32 lineNumber) const;
-	virtual void SetError(Error::Code code, const char *contextMessage,
+	void SetError(Error::Code code, const char *contextMessage,
 		const char* filename, const char* function, UINT32 lineNumber) const;
-	virtual void SetWPIError(const char *errorMessage, const char *contextMessage,
+	void SetWPIError(const char *errorMessage, const char *contextMessage,
 		const char* filename, const char* function, UINT32 lineNumber) const;
-	virtual void CloneError(ErrorBase *rhs) const;
-	virtual void ClearError() const;
-	virtual bool StatusIsFatal() const;
+	void CloneError(const ErrorBase *rhs) const;
+	void ClearError() const;
+	bool StatusIsFatal() const;
 	static void SetGlobalError(Error::Code code, const char *contextMessage,
 		const char* filename, const char* function, UINT32 lineNumber);
 	static void SetGlobalWPIError(const char *errorMessage, const char *contextMessage,
 		const char* filename, const char* function, UINT32 lineNumber);
-	static Error& GetGlobalError();
+	static const Error& GetGlobalError();
+
 protected:
+  // This mutable is safe because Error guarantees that all modifications are
+  // protected with an internal lock.
 	mutable Error m_error;
-	// TODO: Replace globalError with a global list of all errors.
-	static SEM_ID _globalErrorMutex;
+	// TODO: Replace globalError with a global list of all errors, but make sure
+  // that it's thread safe.
 	static Error _globalError;
-	ErrorBase();
+
 private:
 	DISALLOW_COPY_AND_ASSIGN(ErrorBase);
 };
diff --git a/aos/externals/WPILib/WPILib/Global.cpp b/aos/externals/WPILib/WPILib/Global.cpp
new file mode 100644
index 0000000..0d48129
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/Global.cpp
@@ -0,0 +1,155 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "Global.h"
+#include "Utility.h"
+
+Global *Global::instance;
+ReentrantSemaphore Global::instance_lock;
+
+Global *Global::GetInstance() {
+  Synchronized sync(instance_lock);
+  if (instance == NULL) {
+    instance = new Global();
+  }
+  return instance;
+}
+
+Global::Global() {
+	tRioStatusCode status = NiFpga_Status_Success;
+	global_.reset(tGlobal::create(&status));
+  wpi_setError(status);
+
+  AddToSingletonList();
+}
+
+Global::~Global() {
+  Synchronized sync(instance_lock);
+  instance = NULL;
+}
+
+/**
+ * Return the FPGA Version number.
+ * For now, expect this to be competition year.
+ * @return FPGA Version number.
+ */
+UINT16 Global::GetFPGAVersion()
+{
+	tRioStatusCode status = NiFpga_Status_Success;
+	UINT16 version = global_->readVersion(&status);
+	wpi_setError(status);
+	return version;
+}
+
+/**
+ * Return the FPGA Revision number.
+ * The format of the revision is 3 numbers.
+ * The 12 most significant bits are the Major Revision.
+ * the next 8 bits are the Minor Revision.
+ * The 12 least significant bits are the Build Number.
+ * @return FPGA Revision number.
+ */
+UINT32 Global::GetFPGARevision()
+{
+	tRioStatusCode status = NiFpga_Status_Success;
+	UINT32 revision = global_->readRevision(&status);
+	wpi_setError(status);
+	return revision;
+}
+
+/**
+ * Read the microsecond-resolution timer on the FPGA.
+ *
+ * @return The current time in microseconds according to the FPGA (since FPGA reset).
+ */
+UINT32 Global::GetFPGATime()
+{
+	tRioStatusCode status = NiFpga_Status_Success;
+	UINT32 time = global_->readLocalTime(&status);
+	wpi_setError(status);
+	return time;
+}
+
+// RT hardware access functions exported from ni_emb.out
+extern "C"
+{
+	INT32 UserSwitchInput(INT32 nSwitch);
+	INT32 LedInput(INT32 led);
+	INT32 LedOutput(INT32 led, INT32 value);
+}
+
+/**
+ * Read the value of the USER1 DIP switch on the cRIO.
+ */
+INT32 Global::GetRIOUserSwitch()
+{
+	INT32 switchValue = UserSwitchInput(0);
+	wpi_assert(switchValue >= 0);
+	return switchValue > 0;
+}
+
+/**
+ * Set the state of the USER1 status LED on the cRIO.
+ */
+void Global::SetRIOUserLED(UINT32 state)
+{
+	LedOutput(0, state > 0);
+}
+
+/**
+ * Get the current state of the USER1 status LED on the cRIO.
+ * @return The curent state of the USER1 LED.
+ */
+INT32 Global::GetRIOUserLED()
+{
+	return LedInput(0);
+}
+
+/**
+ * Toggle the state of the USER1 status LED on the cRIO.
+ * @return The new state of the USER1 LED.
+ */
+INT32 Global::ToggleRIOUserLED()
+{
+  Synchronized sync(led_toggle_lock_);
+	INT32 ledState = !GetRIOUserLED();
+	SetRIOUserLED(ledState);
+	return ledState;
+}
+
+/**
+ * Set the state of the FPGA status LED on the cRIO.
+ */
+void Global::SetRIO_FPGA_LED(UINT32 state)
+{
+	tRioStatusCode status = NiFpga_Status_Success;
+	global_->writeFPGA_LED(state, &status);
+	wpi_setError(status);
+}
+
+/**
+ * Get the current state of the FPGA status LED on the cRIO.
+ * @return The curent state of the FPGA LED.
+ */
+INT32 Global::GetRIO_FPGA_LED()
+{
+	tRioStatusCode status = NiFpga_Status_Success;
+	bool ledValue = global_->readFPGA_LED(&status);
+	wpi_setError(status);
+	return ledValue;
+}
+
+/**
+ * Toggle the state of the FPGA status LED on the cRIO.
+ * @return The new state of the FPGA LED.
+ */
+INT32 Global::ToggleRIO_FPGA_LED()
+{
+  Synchronized sync(led_toggle_lock_);
+	INT32 ledState = !GetRIO_FPGA_LED();
+	SetRIO_FPGA_LED(ledState);
+	return ledState;
+}
diff --git a/aos/externals/WPILib/WPILib/Global.h b/aos/externals/WPILib/WPILib/Global.h
new file mode 100644
index 0000000..4857e09
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/Global.h
@@ -0,0 +1,42 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include <memory>
+
+#include "SensorBase.h"
+#include "Synchronized.h"
+#include "ChipObject.h"
+
+#ifndef WPILIB_GLOBAL_H_
+#define WPILIB_GLOBAL_H_
+
+class Global : public SensorBase {
+ public:
+  static Global *GetInstance();
+
+  UINT16 GetFPGAVersion();
+  UINT32 GetFPGARevision();
+  UINT32 GetFPGATime();
+  INT32 GetRIOUserSwitch();
+  void SetRIOUserLED(UINT32 state);
+  INT32 GetRIOUserLED();
+  INT32 ToggleRIOUserLED();
+  void SetRIO_FPGA_LED(UINT32 state);
+  INT32 GetRIO_FPGA_LED();
+  INT32 ToggleRIO_FPGA_LED();
+
+ private:
+  Global();
+  ~Global();
+
+  static Global *instance;
+  static ReentrantSemaphore instance_lock;
+
+  ::std::auto_ptr<tGlobal> global_;
+  ReentrantSemaphore led_toggle_lock_;
+};
+
+#endif  // WPILIB_GLOBAL_H_
diff --git a/aos/externals/WPILib/WPILib/IterativeRobot.cpp b/aos/externals/WPILib/WPILib/IterativeRobot.cpp
index 96e9b71..579ea2b 100644
--- a/aos/externals/WPILib/WPILib/IterativeRobot.cpp
+++ b/aos/externals/WPILib/WPILib/IterativeRobot.cpp
@@ -61,7 +61,7 @@
 
 /**
  * Get the period for the periodic functions.
- * Returns 0.0 if configured to syncronize with DS control data packets.
+ * Returns 0.0 if configured to synchronize with DS control data packets.
  * @return Period of the periodic function calls
  */
 double IterativeRobot::GetPeriod()
@@ -87,8 +87,7 @@
  * 
  * This specific StartCompetition() implements "main loop" behavior like that of the FRC
  * control system in 2008 and earlier, with a primary (slow) loop that is
- * called periodically, and a "fast loop" (a.k.a. "spin loop") that is 
- * called as fast as possible with no delay between calls. 
+ * called periodically.
  */
 void IterativeRobot::StartCompetition()
 {
diff --git a/aos/externals/WPILib/WPILib/LiveWindow/LiveWindow.h b/aos/externals/WPILib/WPILib/LiveWindow/LiveWindow.h
index 7237afa..b3e620e 100644
--- a/aos/externals/WPILib/WPILib/LiveWindow/LiveWindow.h
+++ b/aos/externals/WPILib/WPILib/LiveWindow/LiveWindow.h
@@ -24,6 +24,10 @@
 		this->subsystem = subsystem;
 		this->name = name;
 		this->isSensor = isSensor;
+#else
+    (void)subsystem;
+    (void)name;
+    (void)isSensor;
 #endif
 	}
 };
diff --git a/aos/externals/WPILib/WPILib/Module.cpp b/aos/externals/WPILib/WPILib/Module.cpp
index 99e26ea..2d01ea5 100644
--- a/aos/externals/WPILib/WPILib/Module.cpp
+++ b/aos/externals/WPILib/WPILib/Module.cpp
@@ -8,6 +8,9 @@
 #include "AnalogModule.h"
 #include "DigitalModule.h"
 //#include "SolenoidModule.h"
+#include "Utility.h"
+
+ReentrantSemaphore Module::m_semaphore;
 
 Module* Module::m_modules[kMaxModules] = {NULL};
 
@@ -21,6 +24,7 @@
 	: m_moduleType (type)
 	, m_moduleNumber (number)
 {
+  Synchronized sync(m_semaphore);
 	m_modules[ToIndex(type, number)] = this;
 }
 
@@ -29,6 +33,7 @@
  */
 Module::~Module()
 {
+  m_modules[ToIndex(m_moduleType, m_moduleNumber)] = NULL;
 }
 
 /**
@@ -39,6 +44,7 @@
  */
 Module* Module::GetModule(nLoadOut::tModuleType type, UINT8 number)
 {
+  Synchronized sync(m_semaphore);
 	if (m_modules[ToIndex(type, number)] == NULL)
 	{
 		switch(type)
@@ -70,7 +76,20 @@
  */
 UINT8 Module::ToIndex(nLoadOut::tModuleType type, UINT8 number)
 {
-	if (number == 0 || number > kMaxModuleNumber) return 0;
-	if (type < nLoadOut::kModuleType_Analog || type > nLoadOut::kModuleType_Solenoid) return 0;
+	if (number == 0 || number > kMaxModuleNumber) {
+    char buf[64];
+    snprintf(buf, sizeof(buf), "Trying to get index for invalid module %d",
+             static_cast<int>(number));
+    wpi_assertWithMessage(false, buf);
+    return 0;
+  }
+	if (type < nLoadOut::kModuleType_Analog ||
+      type > nLoadOut::kModuleType_Solenoid) {
+    char buf[64];
+    snprintf(buf, sizeof(buf), "Trying to get index for invalid module type %d",
+             static_cast<int>(type));
+    wpi_assertWithMessage(false, buf);
+    return 0;
+  }
 	return (type * kMaxModuleNumber) + (number - 1);
 }
diff --git a/aos/externals/WPILib/WPILib/Module.h b/aos/externals/WPILib/WPILib/Module.h
index 1c9f976..45b10ff 100644
--- a/aos/externals/WPILib/WPILib/Module.h
+++ b/aos/externals/WPILib/WPILib/Module.h
@@ -9,6 +9,7 @@
 
 #include "SensorBase.h"
 #include "NetworkCommunication/LoadOut.h"
+#include "Synchronized.h"
 
 #define kMaxModules	(nLoadOut::kModuleType_Solenoid * kMaxModuleNumber + (kMaxModuleNumber - 1))
 
@@ -20,7 +21,7 @@
 	static Module *GetModule(nLoadOut::tModuleType type, UINT8 number);
 
 protected:
-	explicit Module(nLoadOut::tModuleType type, UINT8 number);
+	Module(nLoadOut::tModuleType type, UINT8 number);
 	virtual ~Module();
 
 	nLoadOut::tModuleType m_moduleType; ///< The type of module represented.
@@ -29,6 +30,7 @@
 private:
 	static UINT8 ToIndex(nLoadOut::tModuleType type, UINT8 number);
 	static Module* m_modules[kMaxModules];
+  static ReentrantSemaphore m_semaphore;
 };
 
 #endif
diff --git a/aos/externals/WPILib/WPILib/MotorSafety.h b/aos/externals/WPILib/WPILib/MotorSafety.h
index 15481d8..a13ac38 100644
--- a/aos/externals/WPILib/WPILib/MotorSafety.h
+++ b/aos/externals/WPILib/WPILib/MotorSafety.h
@@ -16,6 +16,7 @@
 	virtual void StopMotor() = 0;
 	virtual void SetSafetyEnabled(bool enabled) = 0;
 	virtual bool IsSafetyEnabled() = 0;
+  // May write to the first 64 bytes of desc.
 	virtual void GetDescription(char *desc) = 0;
 };
 
diff --git a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.cpp b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.cpp
new file mode 100644
index 0000000..51dc68a
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.cpp
@@ -0,0 +1,410 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "WPILib/NetworkRobot/NetworkRobot.h"
+
+#include <sockLib.h>
+#include <stdint.h>
+#include <selectLib.h>
+#include <assert.h>
+
+#include "WPILib/Utility.h"
+#include "WPILib/WPIErrors.h"
+#include "WPILib/SensorBase.h"
+#include "WPILib/DriverStation.h"
+#include "WPILib/Timer.h"
+
+const double NetworkRobot::kDisableTime = 0.15;
+
+NetworkRobot::NetworkRobot(UINT16 receive_port, const char *sender_address,
+                           UINT16 send_port, const char *receiver_address)
+    : receive_port_(receive_port), sender_address_(sender_address),
+      send_port_(send_port), receiver_address_(receiver_address),
+      receive_socket_(-1), send_socket_(-1),
+      joystick_values_(),
+      send_task_("DS_Send", reinterpret_cast<FUNCPTR>(StaticSendLoop)),
+      last_received_timestamp_(0.0),
+      digital_modules_(), solenoid_bases_(),
+      allocated_digital_outputs_() {
+}
+
+NetworkRobot::~NetworkRobot() {
+  // Nothing we can really do about any errors for either of these.
+  if (receive_socket_ != -1) {
+    close(receive_socket_);
+  }
+  if (send_socket_ != -1) {
+    close(send_socket_);
+  }
+
+  for (size_t i = 0;
+       i < sizeof(solenoid_bases_) / sizeof(solenoid_bases_[0]);
+       ++i) {
+    delete solenoid_bases_[i];
+  }
+  for (size_t module = 0;
+       module < sizeof(digital_modules_) / sizeof(digital_modules_[0]);
+       ++module) {
+    for (int i = 0; i < 16; ++i) {
+      if (allocated_digital_outputs_[module] & (1 << i)) {
+        digital_modules_[module]->FreeDIO(15 - i);
+      }
+    }
+  }
+}
+
+bool NetworkRobot::FillinInAddr(const char *const_ip, in_addr *inet_address) {
+  // A copy of the passed in address string because vxworks has the function
+  // signature without the const and I don't really trust it not to do something
+  // weird and change it.
+  // The size is the maximum length of an IP address (including the terminating
+  // NUL) (ie "123.456.789.123").
+  char non_const_ip[3 + 1 + 3 + 1 + 3 + 1 + 3 + 1];
+  size_t ip_length = strlen(const_ip);
+  if (ip_length >= sizeof(non_const_ip)) {
+    char buf[128];
+    snprintf(buf, sizeof(buf), "IP address '%s' is %zd bytes long"
+             " but should only be %zd", const_ip,
+             ip_length, sizeof(non_const_ip) - 1);
+    wpi_setErrorWithContext(-1, buf);
+    return false;
+  }
+  memcpy(non_const_ip, const_ip, ip_length + 1);
+  errno = 0;
+  if (inet_aton(non_const_ip, inet_address) != 0) {
+    char buf[64];
+    snprintf(buf, sizeof(buf), "inet_aton(%s)", const_ip);
+    wpi_setErrnoErrorWithContext(buf);
+    return false;
+  }
+  return true;
+}
+
+void NetworkRobot::StartCompetition() {
+  // Multiplied by 2 to give ourselves plenty of time to get around to feeding
+  // it before it completely cuts out everything.
+  m_watchdog.SetExpiration(kDisableTime * 2);
+
+  CreateReceiveSocket();
+  if (StatusIsFatal()) return;
+  CreateSendSocket();
+  if (StatusIsFatal()) return;
+
+  send_task_.Start(reinterpret_cast<uintptr_t>(this));
+
+  if (!FillinInAddr(sender_address_, &expected_sender_address_)) return;
+
+  while (!StatusIsFatal()) {
+    if ((Timer::GetPPCTimestamp() - last_received_timestamp_) > kDisableTime) {
+      StopOutputs();
+    }
+    ReceivePacket();
+  }
+  StopOutputs();
+}
+
+void NetworkRobot::StopOutputs() {
+  for (size_t module = 0;
+       module < sizeof(digital_modules_) / sizeof(digital_modules_[0]);
+       ++module) {
+    DigitalModule *digital_module = digital_modules_[module];
+
+    if (digital_module != NULL) {
+      for (int i = 0; i < 10; ++i) {
+        // 0 means stop sending anything.
+        digital_module->SetPWM(i + 1, 0);
+      }
+
+      // Turn off all of the ones that we're responsible for.
+      digital_module->SetDIOs(allocated_digital_outputs_[module], 0);
+
+      // Turn off all of the relays (both directions).
+      digital_module->SetRelaysForward(0xFF, 0);
+      digital_module->SetRelaysReverse(0xFF, 0);
+    }
+  }
+
+  // Can't do anything intelligent with solenoids. Turning them off can be just
+  // as dangerous as leaving them on.
+
+  // We took care of it, so we don't want the watchdog to permanently disable
+  // us.
+  m_watchdog.Feed();
+}
+
+bool NetworkRobot::WaitForData() {
+  assert(kDisableTime < 1.0);
+
+  struct timeval timeout;
+  timeout.tv_sec = 0;
+  timeout.tv_usec = kDisableTime *
+      1000.0 /*seconds to mseconds*/ *
+      1000.0 /*mseconds to useconds*/;
+
+  fd_set fds;
+  FD_ZERO(&fds);
+  FD_SET(receive_socket_, &fds);
+
+  int ret = select(receive_socket_ + 1,
+                   &fds, // read fds
+                   NULL, // write fds
+                   NULL, // exception fds (not supported)
+                   &timeout);
+  if (ret == 0) {
+    // timeout
+    return false;
+  } else if (ret == 1) {
+    return true;
+  }
+  wpi_setErrnoErrorWithContext("waiting until the socket has data");
+  return false;
+}
+
+void NetworkRobot::ReceivePacket() {
+  if (!WaitForData()) return;
+
+  union {
+    sockaddr addr;
+    sockaddr_in in;
+  } sender_address;
+  int sender_address_length = sizeof(sender_address);
+  int received = recvfrom(receive_socket_,
+                          reinterpret_cast<char *>(&values_),
+                          sizeof(values_),
+                          0,
+                          &sender_address.addr,
+                          &sender_address_length);
+  if (received == -1) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ||
+        errno == ETIMEDOUT) {
+      // All of these are various kinds of timing out.
+      return;
+    }
+    wpi_setErrnoErrorWithContext("recvfrom on motor value socket");
+    return;
+  }
+  wpi_assert(static_cast<size_t>(sender_address_length) >=
+             sizeof(sender_address.in));
+  if (sender_address.in.sin_addr.s_addr !=
+      expected_sender_address_.s_addr) {
+    char address[INET_ADDR_LEN];
+    inet_ntoa_b(sender_address.in.sin_addr, address);
+    char buf[64];
+    snprintf(buf, sizeof(buf), "Received packet from wrong IP %s", address);
+    wpi_setErrorWithContext(1, buf);
+    return;
+  }
+
+  if (received != sizeof(values_)) {
+    char buf[64];
+    snprintf(buf, sizeof(buf),
+             "Got packet with %zd bytes but expected %zd\n",
+             received, sizeof(values_));
+    wpi_setErrorWithContext(1, buf);
+    return;
+  }
+  if (values_.CheckHashValue()) {
+    ProcessPacket();
+  } else {
+    char buf[64];
+    snprintf(buf, sizeof(buf), "Hash Value Is %x", values_.hash_value);
+    wpi_setErrorWithContext(1, buf);
+    return;
+  }
+}
+
+void NetworkRobot::ProcessPacket() {
+  int8_t digital_number = values_.digital_module;
+  if (digital_number != -1) {
+    if (digital_number == 0) {
+      digital_number = SensorBase::GetDefaultDigitalModule();
+    }
+    if (digital_number < 1 || digital_number > 2) {
+      char buf[64];
+      snprintf(buf, sizeof(buf), "Got Digital Module %d",
+               static_cast<int>(digital_number));
+      wpi_setWPIErrorWithContext(ModuleIndexOutOfRange, buf);
+      return;
+    }
+    DigitalModule *digital_module = digital_modules_[digital_number - 1];
+    if (digital_module == NULL) {
+      digital_module = digital_modules_[digital_number - 1] =
+          DigitalModule::GetInstance(digital_number);
+      for (int i = 0; i < 10; ++i) {
+        digital_module->SetPWMPeriodScale(i + 1, 0);
+      }
+    }
+
+    for (int i = 0; i < 10; ++i) {
+      digital_module->SetPWM(i + 1, values_.pwm_outputs[i]);
+    }
+
+    uint16_t old_allocated = allocated_digital_outputs_[digital_number - 1];
+    // Have to keep track of which ones we've (de)allocated as we go through in
+    // case we have trouble allocating one of them in the middle.
+    for (int i = 0; i < 16; ++i) {
+      // If we have it allocated and this packet says we shouldn't.
+      if ((old_allocated & (1 << i)) &&
+          !(values_.digital_output_enables & (1 << i))) {
+        digital_module->FreeDIO(15 - i);
+        allocated_digital_outputs_[digital_number - 1] &= ~(1 << i);
+      // If this packet says we should have it allocated and we don't.
+      } else if ((values_.digital_output_enables & (1 << i)) &&
+                 !(old_allocated & (1 << i))) {
+        if (!digital_module->AllocateDIO(15 - i, false /*input*/)) return;
+        allocated_digital_outputs_[digital_number - 1] |= 1 << i;
+      }
+    }
+    wpi_assertEqual(allocated_digital_outputs_[digital_number - 1],
+                    values_.digital_output_enables);
+    digital_module->SetDIOs(values_.digital_output_enables,
+                            values_.digital_output_values);
+
+    if (values_.pressure_switch_channel != 0 &&
+        values_.compressor_channel != 0) {
+      digital_module->SetRelayForward(values_.compressor_channel,
+                                      !digital_module->GetDIO(
+                                          values_.pressure_switch_channel));
+    }
+  }
+
+  int8_t solenoid_number = values_.solenoid_module;
+  if (solenoid_number != -1) {
+    if (solenoid_number == 0) {
+      solenoid_number = SensorBase::GetDefaultSolenoidModule();
+    }
+    if (solenoid_number < 1 || solenoid_number > 2) {
+      char buf[64];
+      snprintf(buf, sizeof(buf), "Got Solenoid Module %d",
+               static_cast<int>(solenoid_number));
+      wpi_setWPIErrorWithContext(ModuleIndexOutOfRange, buf);
+      return;
+    }
+    SolenoidBase *solenoid_base = solenoid_bases_[solenoid_number - 1];
+    if (solenoid_base == NULL) {
+      solenoid_base = solenoid_bases_[solenoid_number - 1] =
+          new SolenoidBase(solenoid_number);
+    }
+
+    solenoid_base->SetAll(values_.solenoid_values);
+  }
+
+  // This code can only assume that whatever is sending it values knows what
+  // state it should be in.
+  DriverStation::FMSState state = m_ds->GetCurrentState();
+  {
+    // Don't have to synchronize around getting the current state too because
+    // it's ok if we're 1 cycle off. It would just be bad if we reported not
+    // being in any state or in 2 states at once.
+    RWLock::Locker state_locker(m_ds->GetUserStateLock(), true);
+    m_ds->InDisabled(state == DriverStation::FMSState::kDisabled);
+    m_ds->InAutonomous(state == DriverStation::FMSState::kAutonomous);
+    m_ds->InOperatorControl(state == DriverStation::FMSState::kTeleop);
+    m_ds->InTest(state == DriverStation::FMSState::kTestMode);
+  }
+
+  m_watchdog.Feed();
+  last_received_timestamp_ = Timer::GetPPCTimestamp();
+}
+
+void NetworkRobot::SendLoop() {
+  while (!StatusIsFatal()) {
+    m_ds->WaitForData();
+
+    {
+      RWLock::Locker data_locker(m_ds->GetDataReadLock());
+      // Get a pointer to the data and then cast away the volatile because it's
+      // annoying to propagate it all over and it's unnecessary here because
+      // we have a lock on the data so it's not going to change.
+      const FRCCommonControlData *data =
+          const_cast<const FRCCommonControlData *>(m_ds->GetControlData());
+      CopyStickValues(0, data->stick0Axes, data->stick0Buttons);
+      CopyStickValues(1, data->stick1Axes, data->stick1Buttons);
+      CopyStickValues(2, data->stick2Axes, data->stick2Buttons);
+      CopyStickValues(3, data->stick3Axes, data->stick3Buttons);
+
+      joystick_values_.control_packet_index = data->packetIndex;
+
+      joystick_values_.team_number = data->teamID;
+      joystick_values_.alliance = data->dsID_Alliance;
+      joystick_values_.position = data->dsID_Position;
+
+      joystick_values_.control.set_test_mode(data->test);
+      joystick_values_.control.set_fms_attached(data->fmsAttached);
+      joystick_values_.control.set_autonomous(data->autonomous);
+      joystick_values_.control.set_enabled(data->enabled);
+    }
+
+    ++joystick_values_.index;
+
+    joystick_values_.FillInHashValue();
+  }
+}
+
+void NetworkRobot::CopyStickValues(int number,
+                                   const INT8 (&axes)[6],
+                                   UINT16 buttons) {
+  for (int i = 0; i < 6; ++i) {
+    joystick_values_.joysticks[number].axes[i] = axes[i];
+  }
+  joystick_values_.joysticks[number].buttons = buttons;
+}
+
+void NetworkRobot::CreateReceiveSocket() {
+  wpi_assertEqual(receive_socket_, -1);
+
+  receive_socket_ = socket(AF_INET, SOCK_DGRAM, 0);
+  if (receive_socket_ < 0) {
+    wpi_setErrnoErrorWithContext("Creating UDP Socket");
+    receive_socket_ = -1;
+    return;
+  }
+
+  union {
+    sockaddr_in in;
+    sockaddr addr;
+  } address;
+  memset(&address, 0, sizeof(address));
+  address.in.sin_family = AF_INET;
+  address.in.sin_port = receive_port_;
+  address.in.sin_addr.s_addr = INADDR_ANY;
+  if (bind(receive_socket_, &address.addr, sizeof(address.addr)) == ERROR) {
+    wpi_setErrnoErrorWithContext("Binding Socket");
+    return;
+  }
+
+  int on = 1;
+  errno = 0;
+  if (ioctl(receive_socket_, FIONBIO, reinterpret_cast<int>(&on)) < 0) {
+    wpi_setErrnoErrorWithContext("Setting Socket to Non-Blocking Mode");
+    return;
+  }
+}
+
+void NetworkRobot::CreateSendSocket() {
+  wpi_assertEqual(send_socket_, -1);
+
+  send_socket_ = socket(AF_INET, SOCK_DGRAM, 0);
+  if (send_socket_ < 0) {
+    wpi_setErrnoErrorWithContext("Creating UDP Socket");
+    send_socket_ = -1;
+    return;
+  }
+
+  union {
+    sockaddr_in in;
+    sockaddr addr;
+  } address;
+  memset(&address, 0, sizeof(address));
+  address.in.sin_family = AF_INET;
+  address.in.sin_port = send_port_;
+  if (!FillinInAddr(receiver_address_, &address.in.sin_addr)) return;
+
+  if (bind(send_socket_, &address.addr, sizeof(address.addr)) == ERROR) {
+    wpi_setErrnoErrorWithContext("Binding Socket");
+    return;
+  }
+}
diff --git a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.h b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.h
new file mode 100644
index 0000000..b17bef2
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobot.h
@@ -0,0 +1,124 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#ifndef WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_H_
+#define WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_H_
+
+#include <inetLib.h>
+
+#include "WPILib/NetworkRobot/NetworkRobotValues.h"
+#include "WPILib/RobotBase.h"
+#include "WPILib/Base.h"
+#include "WPILib/DigitalModule.h"
+#include "WPILib/ErrorBase.h"
+#include "WPILib/SolenoidBase.h"
+#include "WPILib/Task.h"
+
+// A simple implementation of receiving motor values over UDP and sending
+// joystick data back out.
+// Deals with turning a compressor on and off at the same time.
+// You should not try to change any of the outputs on any of the modules that
+// you have this class control outside of this class.
+// This class takes care of disabling outputs when no packets are received in
+// kDisableTime and feeding the Watchdog correctly.
+//
+// The receiving interface consists of receiving NetworkRobotValues structs on
+// a given UDP port from a given sender address. Each time a set of motor values
+// is received, this class will update all output values.
+//
+// The sending interface consists of sending NetworkRobotJoysticks structs on a
+// given UDP port to a given sender address. Each time a new Driver's Station
+// packet is received from the FMS code this class will send out another packet
+// with the new values.
+class NetworkRobot : public RobotBase, public ErrorBase {
+ protected:
+  // Does not take ownership of *sender_address or *receiver_address.
+  NetworkRobot(UINT16 receive_port, const char *sender_address,
+               UINT16 send_port, const char *receiver_address);
+  virtual ~NetworkRobot();
+
+  virtual void StartCompetition();
+
+  // Called when a valid packet has been received into values_.
+  // Subclasses can override if they want to do more, however they still need to
+  // call this implementation.
+  virtual void ProcessPacket();
+
+  // Called when no packet has been received for too long and it's time to stop
+  // everything.
+  // Subclasses can override if they want to do more, however they still need to
+  // call this implementation.
+  virtual void StopOutputs();
+
+ private:
+  // How long between packets we wait until we disable all of the outputs (in
+  // seconds).
+  // Must stay less than 1 second with the current implementation.
+  static const double kDisableTime;
+
+  // Deals with calling inet_aton and passing any errors on to the logging
+  // system. A helper is necessary here because inet_aton is normally just kind
+  // of annoying to deal with, but under vxworks, you have to make a copy of the
+  // string before doing anything with etc etc so it's really complicated.
+  // Returns whether or not it was successful. If it returns false, then an
+  // error will have already been recorded.
+  bool FillinInAddr(const char *const_ip, in_addr *inet_address);
+
+  // Waits for receive_socket_ to become readable for up to kDisableTime.
+  // Returns whether or not there is readable data available.
+  bool WaitForData();
+
+  // Receives a packet and calls ProcessPacket() if it's a good one.
+  void ReceivePacket();
+
+  // Gets run in a separate task to take DS data and send it out.
+  void SendLoop();
+  static void StaticSendLoop(void *self) {
+    static_cast<NetworkRobot *>(self)->SendLoop();
+  }
+
+  // Sets receive_socket_ to an opened socket listening on receive_port_ to UDP
+  // packets from sender_address_.
+  void CreateReceiveSocket();
+  // Sets send_socket_ to an opened socket sending UDP packets on send_port_ to
+  // receiver_address_.
+  void CreateSendSocket();
+
+  const UINT16 receive_port_;
+  const char *const sender_address_;
+  struct in_addr expected_sender_address_;
+
+  const UINT16 send_port_;
+  const char *const receiver_address_;
+
+  int receive_socket_;
+  NetworkRobotValues values_;
+
+  int send_socket_;
+  NetworkRobotJoysticks joystick_values_;
+  Task send_task_;
+
+  // Helper function to copy all of the data for a single joystick into
+  // joystick_values_.
+  // axes and buttons get copied into joystick_values_.joysticks[number].
+  void CopyStickValues(int number, const INT8 (&axes)[6], UINT16 buttons);
+
+  // Using Timer::GetPPCTimestamp() values.
+  double last_received_timestamp_;
+
+  // Ownership of the pointers stored here stays in DigitalModule.
+  DigitalModule *digital_modules_[2];
+  // This object owns pointers stored here.
+  SolenoidBase *solenoid_bases_[2];
+
+  // A bitmask of all of the digital outputs that we have currently allocated.
+  // In hardware order.
+  UINT16 allocated_digital_outputs_[2];
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkRobot);
+};
+
+#endif  // WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_H_
diff --git a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.cpp b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.cpp
new file mode 100644
index 0000000..1d2e805
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.cpp
@@ -0,0 +1,45 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "WPILib/NetworkRobot/NetworkRobotValues.h"
+
+#include <string.h>
+
+namespace {
+
+uint32_t CalculateHashValue(const char *data, size_t size) {
+  uint32_t hash_value = 0;
+  for (size_t i = 0; i < size; ++i) {
+    hash_value = (((hash_value << 8) & 0xFF00) + (data[i] & 0xFF)) % 0x04C11DB7;
+  }
+  return hash_value;
+}
+
+}  // namespace
+
+void NetworkRobotValues::FillInHashValue() {
+  hash_value = CalculateHashValue(reinterpret_cast<const char *>(this) +
+                                  sizeof(hash_value),
+                                  sizeof(*this) - sizeof(hash_value));
+}
+
+bool NetworkRobotValues::CheckHashValue() const {
+  return hash_value == CalculateHashValue(reinterpret_cast<const char *>(this) +
+                                          sizeof(hash_value),
+                                          sizeof(*this) - sizeof(hash_value));
+}
+
+void NetworkRobotJoysticks::FillInHashValue() {
+  hash_value = CalculateHashValue(reinterpret_cast<const char *>(this) +
+                                  sizeof(hash_value),
+                                  sizeof(*this) - sizeof(hash_value));
+}
+
+bool NetworkRobotJoysticks::CheckHashValue() const {
+  return hash_value == CalculateHashValue(reinterpret_cast<const char *>(this) +
+                                          sizeof(hash_value),
+                                          sizeof(*this) - sizeof(hash_value));
+}
diff --git a/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.h b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.h
new file mode 100644
index 0000000..b66ad7c
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/NetworkRobot/NetworkRobotValues.h
@@ -0,0 +1,128 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#ifndef WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_VALUES_H_
+#define WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_VALUES_H_
+
+#include <stdint.h>
+
+// This file needs to not have any dependencies on any other parts of WPILib so
+// that it can be #included by other code (like that which sends these values).
+// All multi-byte values are sent over the network in big endian (network)
+// byte order.
+
+// The structure that actually gets sent over the network to the cRIO with motor
+// values.
+// All channel and module numbers are 1-based like usual.
+struct NetworkRobotValues {
+  // A hash value to make sure that corrupted packets don't get used.
+  // IMPORTANT: Must stay at the beginning.
+  uint32_t hash_value;
+
+  // Which digital module this packet has values for (1 or 2).
+  // 0 means the default one and -1 means to not update any one.
+  int8_t digital_module;
+  // Raw values for all 10 PWM outputs.
+  uint8_t pwm_outputs[10];
+
+  // Bitmasks for enabling digital outputs and what values to set them to.
+  // See DigitalModule::SetDIOs for which bit is which.
+  uint16_t digital_output_enables;
+  uint16_t digital_output_values;
+
+  // Channels for a presssure switch and compressor to turn on and off based on
+  // the value from it.
+  // Whatever compressor channel is sent will be updated with the value from the
+  // corresponding pressure switch channel when the packet is received.
+  // 0 means to not do anything.
+  uint8_t pressure_switch_channel, compressor_channel;
+
+  // Which solenoid module this packet has values for (1 or 2).
+  // 0 means the default one and -1 means to not update any one.
+  int8_t solenoid_module;
+  // 1 bit for each solenoid output.
+  uint8_t solenoid_values;
+
+  // Sets hash_value to the correct value for the rest of the data.
+  void FillInHashValue();
+  // Returns whether or not hash_value matches the rest of the data. Any byte
+  // order conversion must be performed ONLY on the hash_value field before
+  // calling this.
+  bool CheckHashValue() const;
+} __attribute__((packed));
+
+// The structure that the cRIO sends out with joystick positions etc.
+struct NetworkRobotJoysticks {
+  // A hash value to make sure that corrupted packets don't get used.
+  // IMPORTANT: Must stay at the beginning.
+  uint32_t hash_value;
+
+  // A structure that stores the information about a joystick and instances for
+  // each of the 4 joysticks.
+  struct Joystick {
+    // Bitmask of the button values.
+    // The LSB is button 1 and only a maximum of 12 are supported.
+    uint16_t buttons;
+    // Raw values for each of the 6 joystick axes.
+    // The range of values depends on the joystick.
+    int8_t axes[6];
+  } __attribute__((packed)) joysticks[4];
+
+  // The index number from the DS.
+  uint16_t control_packet_index;
+  // An index for this structure. Gets incremented by 1 for each one of these
+  // structures that is sent.
+  uint16_t index;
+
+  // The team number that the DS is configured for.
+  uint16_t team_number;
+  // Which alliance the robot is on. Should be 'R' or 'B'.
+  char alliance;
+  // Which position the DS is in on the field. Should be '1', '2', or '3'.
+  char position;
+
+  // A structure that efficiently stores the control data bits from the DS and
+  // has methods for safely and easily getting and setting them and an instance
+  // of it for actually sending the information.
+  // Not just a C bitfield because those are a mess for portability.
+  struct ControlInformation {
+    bool test_mode() { return GetBit(kTestMode); }
+    void set_test_mode(bool value) { SetBit(kTestMode, value); }
+    bool fms_attached() { return GetBit(kFmsAttached); }
+    void set_fms_attached(bool value) { SetBit(kFmsAttached, value); }
+    bool autonomous() { return GetBit(kAutonomous); }
+    void set_autonomous(bool value) { SetBit(kAutonomous, value); }
+    bool enabled() { return GetBit(kEnabled); }
+    void set_enabled(bool value) { SetBit(kEnabled, value); }
+
+   private:
+    // Constants for which bit is which.
+    static const int kTestMode = 0;
+    static const int kFmsAttached = 1;
+    static const int kAutonomous = 2;
+    static const int kEnabled = 3;
+
+    bool GetBit(int bit) {
+      return bits & (1 << bit);
+    }
+    void SetBit(int bit, bool value) {
+      uint8_t mask = 1 << bit;
+      bits &= ~mask;
+      bits |= (mask & (value ? 0xFF : 0x00));
+    }
+
+    uint8_t bits;
+  } __attribute__((packed)) control;
+
+  // Sets hash_value to the correct value for the rest of the data.
+  void FillInHashValue();
+  // Returns whether or not hash_value matches the rest of the data. Any byte
+  // order conversion must be performed ONLY on the hash_value field before
+  // calling this.
+  bool CheckHashValue() const;
+} __attribute__((packed));
+
+#endif  // WPILIB_NETWORK_ROBOT_NETWORK_ROBOT_VALUES_H_
diff --git a/aos/externals/WPILib/WPILib/PWM.cpp b/aos/externals/WPILib/WPILib/PWM.cpp
index 05bf16d..7ae5370 100644
--- a/aos/externals/WPILib/WPILib/PWM.cpp
+++ b/aos/externals/WPILib/WPILib/PWM.cpp
@@ -42,9 +42,9 @@
 	}
 
 	snprintf(buf, 64, "PWM %d (Module: %d)", channel, moduleNumber);
-	if (allocated->Allocate((moduleNumber - 1) * kPwmChannels + channel - 1, buf) == ~0ul)
+	if (allocated->Allocate((moduleNumber - 1) * kPwmChannels + channel - 1,
+                          buf, this) == ~0ul)
 	{
-		CloneError(allocated);
 		return;
 	}
 	m_channel = channel;
@@ -92,7 +92,8 @@
 	if (m_module)
 	{
 		m_module->SetPWM(m_channel, kPwmDisabled);
-		allocated->Free((m_module->GetNumber() - 1) * kPwmChannels + m_channel - 1);
+		allocated->Free((m_module->GetNumber() - 1) * kPwmChannels + m_channel - 1,
+                    this);
 	}
 }
 
diff --git a/aos/externals/WPILib/WPILib/RWLock.cpp b/aos/externals/WPILib/WPILib/RWLock.cpp
new file mode 100644
index 0000000..9918af0
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/RWLock.cpp
@@ -0,0 +1,202 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include <taskLib.h>
+#include <intLib.h>
+#include <assert.h>
+
+#include "RWLock.h"
+
+// A wrapper for assert that allows it to be easily turned off just in this
+// file. That configuration is recommended for normal use because it means less
+// code that gets executed with the scheduler locked.
+#if 1
+#define rwlock_assert(expression) assert(expression)
+// A macro to easily assert that some expression (possibly with side effects)
+// is 0.
+#define rwlock_assert_success(expression) do { \
+  int ret = (expression); \
+  assert(ret == 0); \
+} while (false)
+#else
+#define rwlock_assert(expression) ((void)0)
+#define rwlock_assert_success(expression) ((void)(expression))
+#endif
+
+/**
+ * Class that locks the scheduler and then unlocks it in the destructor.
+ */
+class TaskSchedulerLocker {
+ public:
+  TaskSchedulerLocker() {
+    rwlock_assert_success(taskLock());
+  }
+  ~TaskSchedulerLocker() {
+    rwlock_assert_success(taskUnlock());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TaskSchedulerLocker);
+};
+
+RWLock::Locker::Locker(RWLock *lock, bool write)
+  : lock_(lock), num_(lock_->Lock(write)) {
+}
+
+RWLock::Locker::Locker(const Locker &other)
+  : lock_(other.lock_), num_(lock_->AddLock()) {
+}
+
+RWLock::Locker::~Locker() {
+  lock_->Unlock(num_);
+}
+
+// RWLock is implemented by just locking the scheduler while doing anything
+// because that is the only way under vxworks to do much of anything atomically.
+
+RWLock::RWLock()
+  : number_of_write_locks_(0),
+    number_of_writers_pending_(0),
+    number_of_readers_(0),
+    reader_tasks_(),
+    read_ready_(semBCreate(SEM_Q_PRIORITY, SEM_EMPTY)),
+    write_ready_(semBCreate(SEM_Q_PRIORITY, SEM_EMPTY)) {
+  rwlock_assert(read_ready_ != NULL);
+  rwlock_assert(write_ready_ != NULL);
+}
+
+RWLock::~RWLock() {
+  // Make sure that nobody else currently has a lock or will ever be able to.
+  Lock(true);
+
+  rwlock_assert_success(semDelete(read_ready_));
+  rwlock_assert_success(semDelete(write_ready_));
+}
+
+int RWLock::Lock(bool write) {
+  assert(!intContext());
+
+  int current_task = taskIdSelf();
+  // It's safe to do this check up here (outside of locking the scheduler)
+  // because we only care whether the current task is in there or not and that
+  // can't be changed because it's the task doing the checking.
+  bool current_task_holds_already = TaskOwns(current_task);
+
+  TaskSchedulerLocker scheduler_locker;
+
+  // We can't be reading and writing at the same time.
+  rwlock_assert(!((number_of_write_locks_ > 0) && (number_of_readers_ > 0)));
+
+  if (write) {
+    assert(!current_task_holds_already);
+    // If somebody else already has it locked.
+    // Don't have to worry about another task getting scheduled after
+    // write_ready_ gets given because nobody else (except another writer, which
+    // would just block on it) will do anything while there are pending
+    // writer(s).
+    if ((number_of_readers_ > 0) || (number_of_write_locks_ > 0)) {
+      ++number_of_writers_pending_;
+      // Wait for it to be our turn.
+      rwlock_assert_success(semTake(write_ready_, WAIT_FOREVER));
+      --number_of_writers_pending_;
+    } else {
+      rwlock_assert(number_of_writers_pending_ == 0);
+    }
+    rwlock_assert((number_of_write_locks_ == 0) && (number_of_readers_ == 0));
+    number_of_write_locks_ = 1;
+    return 0;
+  } else {  // read
+    // While there are one or more writers active or waiting.
+    // Has to be a loop in case a writer gets scheduled between the time
+    // read_ready_ gets flushed and we run.
+    while ((number_of_write_locks_ > 0) || (number_of_writers_pending_ > 0)) {
+      // Wait for the writer(s) to finish.
+      rwlock_assert_success(semTake(read_ready_, WAIT_FOREVER));
+    }
+
+    int num = number_of_readers_;
+    number_of_readers_ = num + 1;
+    assert(num < kMaxReaders);
+    rwlock_assert(reader_tasks_[num] == 0);
+    reader_tasks_[num] = current_task;
+    rwlock_assert((number_of_write_locks_ == 0) && (number_of_readers_ > 0));
+    return num;
+  }
+}
+
+void RWLock::Unlock(int num) {
+  assert(!intContext());
+  TaskSchedulerLocker scheduler_locker;
+
+  // We have to be reading or writing right now, but not both.
+  rwlock_assert((number_of_write_locks_ > 0) != (number_of_readers_ > 0));
+
+  if (number_of_write_locks_ > 0) {  // we're currently writing
+    rwlock_assert(num == 0);
+    --number_of_write_locks_;
+    rwlock_assert((number_of_write_locks_ >= 0) &&
+                  (number_of_writers_pending_ >= 0));
+    // If we were the last one.
+    if (number_of_write_locks_ == 0) {
+      // If there are no other tasks waiting to write (because otherwise they
+      // need to get priority over any readers).
+      if (number_of_writers_pending_ == 0) {
+        // Wake up any waiting readers.
+        rwlock_assert_success(semFlush(read_ready_));
+      } else {
+        // Wake up a waiting writer.
+        // Not a problem if somebody else already did this before the waiting
+        // writer got a chance to take it because it'll do nothing and return
+        // success.
+        rwlock_assert_success(semGive(write_ready_));
+      }
+    }
+  } else {  // we're curently reading
+    rwlock_assert(reader_tasks_[num] == taskIdSelf());
+    reader_tasks_[num] = 0;
+    --number_of_readers_;
+    rwlock_assert(number_of_readers_ >= 0 &&
+                  (number_of_writers_pending_ >= 0));
+    // If we were the last one.
+    if (number_of_readers_ == 0) {
+      // If there are any writers waiting for a chance to go.
+      if (number_of_writers_pending_ > 0) {
+        // Wake a waiting writer.
+        // Not a problem if somebody else already did this before the waiting
+        // writer got a chance to take it because it'll still return success.
+        rwlock_assert_success(semGive(write_ready_));
+      }
+    }
+  }
+}
+
+int RWLock::AddLock() {
+  assert(!intContext());
+  // TODO: Replace this with just atomically incrementing the right number once
+  // we start using a GCC new enough to have the nice atomic builtins.
+  // That will be safe because whether we're currently reading or writing can't
+  // change in the middle of this.
+  TaskSchedulerLocker scheduler_locker;
+
+  // We have to be reading or writing right now, but not both.
+  rwlock_assert((number_of_write_locks_ > 0) != (number_of_readers_ > 0));
+
+  if (number_of_write_locks_ > 0) {  // we're currently writing
+    ++number_of_write_locks_;
+    return 0;
+  } else {  // we're currently reading
+    return number_of_readers_++;
+  }
+}
+
+bool RWLock::TaskOwns(int task_id) {
+  for (size_t i = 0;
+       i < sizeof(reader_tasks_) / sizeof(reader_tasks_[0]);
+       ++i) {
+    if (reader_tasks_[i] == task_id) return true;
+  }
+  return false;
+}
diff --git a/aos/externals/WPILib/WPILib/RWLock.h b/aos/externals/WPILib/WPILib/RWLock.h
new file mode 100644
index 0000000..13d7b50
--- /dev/null
+++ b/aos/externals/WPILib/WPILib/RWLock.h
@@ -0,0 +1,125 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2008. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#ifndef WPILIB_DATA_LOCK_H_
+#define WPILIB_DATA_LOCK_H_
+
+#include <semLib.h>
+
+#include "Base.h"
+
+/**
+ * Represents a read/write lock on using some shared data so that it will not
+ * be modified by any other tasks while code is using it.
+ *
+ * See <http://en.wikipedia.org/wiki/Readers-writer_lock> for an overview of how
+ * this can be used.
+ *
+ * In this implementation, if there are any writers pending, then any new
+ * attempts to acquire read locks will wait until all writers are done unless a
+ * read lock is already held by that same task.
+ */
+class RWLock {
+ public:
+  /**
+   * Represents an actual lock on the shared data. The destructor will free it.
+   *
+   * Intended to be used as an automatic (or local) variable so that the
+   * compiler will ensure that the destructor gets called no matter how the
+   * scope is exited.
+   *
+   * While it is possible to use new/delete to dynamically allocate an instance,
+   * the constructor and destructor still MUST still be called from the same
+   * task.
+   *
+   * Has a copy constructor which allows "copying" the lock that is held. Does
+   * not have an assignment operator because assigning a lock doesn't make much
+   * sense.
+   */
+  class Locker {
+   public:
+    /**
+     * @param write Whether to create a writer lock (creates a reader lock
+     * otherwise).
+     */
+    Locker(RWLock *lock, bool write);
+
+    /**
+     * Creates another lock of the same type. They can both be released
+     * (destructed) independently.
+     * NOTE: This does allow creating multiple write locks that are held at the
+     * same time.
+     */
+    Locker(const Locker &other);
+
+    /**
+     * Unlocks the lock.
+     */
+    ~Locker();
+
+   private:
+    RWLock *const lock_;
+    const int num_;
+
+    void operator=(const Locker &);
+  };
+
+  /**
+   * The maximum number of read locks that can be held at the same time.
+   */
+  static const int kMaxReaders = 64;
+
+  RWLock();
+  /**
+   * Waits until there are no more read or write locks held.
+   */
+  ~RWLock();
+
+ private:
+  // The number of write locks that are currently held.
+  int number_of_write_locks_;
+  // How many tasks are currently waiting to get a write lock.
+  // Each count in here corresponds to a task that is blocked on write_ready_.
+  int number_of_writers_pending_;
+
+  // How many read locks are currently held.
+  int number_of_readers_;
+
+  // The task ID of the task holding each read lock.
+  int reader_tasks_[kMaxReaders];
+
+  // Always locked. Gets semFlushed when readers are allowed to take the lock
+  // (after all writers are done).
+  SEM_ID read_ready_;
+  // Locked almost all of the time. Pending writers (who have to update
+  // number_of_writers_pending_) block locking this and it gets unlocked when it
+  // is time for one of them to go.
+  SEM_ID write_ready_;
+
+  // Acquires the appropriate kind of lock.
+  // Returns a value that must be passed to the corresponding Unlock call.
+  int Lock(bool write);
+  // Unlocks 1 lock.
+  // Does not need to know whether it is read or write because only one type of
+  // lock can be held at a time.
+  // num must be the return value from the corresponding Lock/AddLock call.
+  void Unlock(int num);
+  // Increments the lock count by 1.
+  // There must be at least 1 lock held during the execution of this method.
+  // Use the regular Unlock() to unlock a lock acquired this way.
+  // This is not the same as Lock(current_type) because this is the only way to
+  // acquire multiple write locks at the same time.
+  // Returns a value that must be passed to the corresponding Unlock call.
+  int AddLock();
+  // Checks whether task_id is in reader_tasks_.
+  bool TaskOwns(int task_id);
+
+  friend class Locker;
+
+  DISALLOW_COPY_AND_ASSIGN(RWLock);
+};
+
+#endif  // WPILIB_DATA_LOCK_H_
diff --git a/aos/externals/WPILib/WPILib/Relay.cpp b/aos/externals/WPILib/WPILib/Relay.cpp
index dd59d52..9fb3a2d 100644
--- a/aos/externals/WPILib/WPILib/Relay.cpp
+++ b/aos/externals/WPILib/WPILib/Relay.cpp
@@ -43,9 +43,9 @@
 	if (m_direction == kBothDirections || m_direction == kForwardOnly)
 	{
 		snprintf(buf, 64, "Forward Relay %d (Module: %d)", m_channel, moduleNumber);
-		if (relayChannels->Allocate(((moduleNumber - 1) * kRelayChannels + m_channel - 1) * 2, buf) == ~0ul)
+		if (relayChannels->Allocate(((moduleNumber - 1) * kRelayChannels +
+                                 m_channel - 1) * 2, buf, this) == ~0ul)
 		{
-			CloneError(relayChannels);
 			return;
 		}
 
@@ -54,9 +54,9 @@
 	if (m_direction == kBothDirections || m_direction == kReverseOnly)
 	{
 		snprintf(buf, 64, "Reverse Relay %d (Module: %d)", m_channel, moduleNumber);
-		if (relayChannels->Allocate(((moduleNumber - 1) * kRelayChannels + m_channel - 1) * 2 + 1, buf) == ~0ul)
+		if (relayChannels->Allocate(((moduleNumber - 1) * kRelayChannels
+                                 + m_channel - 1) * 2 + 1, buf, this) == ~0ul)
 		{
-			CloneError(relayChannels);
 			return;
 		}
 
@@ -105,11 +105,13 @@
 
 	if (m_direction == kBothDirections || m_direction == kForwardOnly)
 	{
-		relayChannels->Free(((m_module->GetNumber() - 1) * kRelayChannels + m_channel - 1) * 2);
+		relayChannels->Free(((m_module->GetNumber() - 1) *
+                         kRelayChannels + m_channel - 1) * 2, this);
 	}
 	if (m_direction == kBothDirections || m_direction == kReverseOnly)
 	{
-		relayChannels->Free(((m_module->GetNumber() - 1) * kRelayChannels + m_channel - 1) * 2 + 1);
+		relayChannels->Free(((m_module->GetNumber() - 1) *
+                         kRelayChannels + m_channel - 1) * 2 + 1, this);
 	}
 }
 
diff --git a/aos/externals/WPILib/WPILib/Resource.cpp b/aos/externals/WPILib/WPILib/Resource.cpp
index ec35eb3..ef7d15d 100644
--- a/aos/externals/WPILib/WPILib/Resource.cpp
+++ b/aos/externals/WPILib/WPILib/Resource.cpp
@@ -6,7 +6,6 @@
 
 #include "Resource.h"
 #include "WPIErrors.h"
-#include "ErrorBase.h"
 
 ReentrantSemaphore Resource::m_createLock;
 
@@ -28,6 +27,7 @@
 
 /**
  * Factory method to create a Resource allocation-tracker *if* needed.
+ * Handles the necessary synchronization internally.
  *
  * @param r -- address of the caller's Resource pointer. If *r == NULL, this
  *    will construct a Resource and make *r point to it. If *r != NULL, i.e.
@@ -59,7 +59,7 @@
  * When a resource is requested, mark it allocated. In this case, a free resource value
  * within the range is located and returned after it is marked allocated.
  */
-UINT32 Resource::Allocate(const char *resourceDesc)
+UINT32 Resource::Allocate(const char *resourceDesc, const ErrorBase *error)
 {
 	Synchronized sync(m_allocateLock);
 	for (UINT32 i=0; i < m_size; i++)
@@ -70,7 +70,7 @@
 			return i;
 		}
 	}
-	wpi_setWPIErrorWithContext(NoAvailableResources, resourceDesc);
+	wpi_setStaticWPIErrorWithContext(error, NoAvailableResources, resourceDesc);
 	return ~0ul;
 }
 
@@ -79,17 +79,18 @@
  * The user requests a specific resource value, i.e. channel number and it is verified
  * unallocated, then returned.
  */
-UINT32 Resource::Allocate(UINT32 index, const char *resourceDesc)
+UINT32 Resource::Allocate(UINT32 index, const char *resourceDesc,
+                          const ErrorBase *error)
 {
 	Synchronized sync(m_allocateLock);
 	if (index >= m_size)
 	{
-		wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, resourceDesc);
+		wpi_setStaticWPIErrorWithContext(error, ChannelIndexOutOfRange, resourceDesc);
 		return ~0ul;
 	}
 	if ( m_isAllocated[index] )
 	{
-		wpi_setWPIErrorWithContext(ResourceAlreadyAllocated, resourceDesc);
+		wpi_setStaticWPIErrorWithContext(error, ResourceAlreadyAllocated, resourceDesc);
 		return ~0ul;
 	}
 	m_isAllocated[index] = true;
@@ -102,18 +103,18 @@
  * After a resource is no longer needed, for example a destructor is called for a channel assignment
  * class, Free will release the resource value so it can be reused somewhere else in the program.
  */
-void Resource::Free(UINT32 index)
+void Resource::Free(UINT32 index, const ErrorBase *error)
 {
 	Synchronized sync(m_allocateLock);
 	if (index == ~0ul) return;
 	if (index >= m_size)
 	{
-		wpi_setWPIError(NotAllocated);
+		wpi_setStaticWPIError(error, NotAllocated);
 		return;
 	}
 	if (!m_isAllocated[index])
 	{
-		wpi_setWPIError(NotAllocated);
+		wpi_setStaticWPIError(error, NotAllocated);
 		return;
 	}
 	m_isAllocated[index] = false;
diff --git a/aos/externals/WPILib/WPILib/Resource.h b/aos/externals/WPILib/WPILib/Resource.h
index dbe1936..11faaec 100644
--- a/aos/externals/WPILib/WPILib/Resource.h
+++ b/aos/externals/WPILib/WPILib/Resource.h
@@ -19,15 +19,18 @@
  * The Resource class does not allocate the hardware channels or other
  * resources; it just tracks which indices were marked in use by
  * Allocate and not yet freed by Free.
+ *
+ * Several methods require an ErrorBase* so that they can report any errors.
  */
-class Resource : public ErrorBase
+class Resource
 {
 public:
 	virtual ~Resource();
 	static void CreateResourceObject(Resource **r, UINT32 elements);
-	UINT32 Allocate(const char *resourceDesc);
-	UINT32 Allocate(UINT32 index, const char *resourceDesc);
-	void Free(UINT32 index);
+	UINT32 Allocate(const char *resourceDesc, const ErrorBase *error);
+	UINT32 Allocate(UINT32 index, const char *resourceDesc,
+                  const ErrorBase *error);
+	void Free(UINT32 index, const ErrorBase *error);
 
 private:
 	explicit Resource(UINT32 size);
diff --git a/aos/externals/WPILib/WPILib/RobotBase.cpp b/aos/externals/WPILib/WPILib/RobotBase.cpp
index 0bebf09..17c7682 100644
--- a/aos/externals/WPILib/WPILib/RobotBase.cpp
+++ b/aos/externals/WPILib/WPILib/RobotBase.cpp
@@ -19,6 +19,8 @@
 
 void RobotBase::setInstance(RobotBase* robot)
 {
+  // No point in synchronization here because it's private and it only gets
+  // called from robotTask.
 	wpi_assert(m_instance == NULL);
 	m_instance = robot;
 }
@@ -30,8 +32,9 @@
 
 /**
  * Constructor for a generic robot program.
- * User code should be placed in the constuctor that runs before the Autonomous or Operator
- * Control period starts. The constructor will run to completion before Autonomous is entered.
+ * User code that should run before the Autonomous or Operator Control period
+ * starts should be placed in the subclass constructor.
+ * The constructor must finish before Autonomous can be entered.
  * 
  * This must be used to ensure that the communications code starts. In the future it would be
  * nice to put this code into it's own task that loads on boot so ensure that it runs.
@@ -45,7 +48,7 @@
 
 /**
  * Free the resources for a RobotBase class.
- * This includes deleting all classes that might have been allocated as Singletons to they
+ * This includes deleting all classes that might have been allocated as Singletons so they
  * would never be deleted except here.
  */
 RobotBase::~RobotBase()
@@ -95,7 +98,7 @@
 }
 
 /**
- * Determine if the robot is currently in Autnomous mode.
+ * Determine if the robot is currently in Autonomous mode.
  * @return True if the robot is currently operating Autonomously as determined by the field controls.
  */
 bool RobotBase::IsAutonomous()
@@ -135,18 +138,21 @@
  */
 void RobotBase::robotTask(FUNCPTR factory, Task *task)
 {
-	RobotBase::setInstance((RobotBase*)factory());
-	RobotBase::getInstance().m_task = task;
-	RobotBase::getInstance().StartCompetition();
+  RobotBase *instance = (RobotBase*)factory();
+  instance->m_task = task;
+  RobotBase::setInstance(instance);
+  instance->StartCompetition();
 }
 
 /**
  * 
  * Start the robot code.
- * This function starts the robot code running by spawning a task. Currently tasks seemed to be
- * started by LVRT without setting the VX_FP_TASK flag so floating point context is not saved on
- * interrupts. Therefore the program experiences hard to debug and unpredictable results. So the
- * LVRT code starts this function, and it, in turn, starts the actual user program.
+ * This function starts the robot code running by spawning a task. Currently
+ * tasks seem to be started by LVRT without setting the VX_FP_TASK flag which
+ * means that the floating point registers are not saved on interrupts and task
+ * switches. That causes the program to experience hard to debug and
+ * unpredictable results, so the LVRT code starts this function, so that it, in
+ * turn, can start the actual user program.
  */
 void RobotBase::startRobotTask(FUNCPTR factory)
 {
@@ -199,8 +205,7 @@
 /**
  * This class exists for the sole purpose of getting its destructor called when the module unloads.
  * Before the module is done unloading, we need to delete the RobotBase derived singleton.  This should delete
- * the other remaining singletons that were registered.  This should also stop all tasks that are using
- * the Task class.
+ * the other remaining singletons that were registered.
  */
 class RobotDeleter
 {
diff --git a/aos/externals/WPILib/WPILib/RobotBase.h b/aos/externals/WPILib/WPILib/RobotBase.h
index ac15250..45c24dd 100644
--- a/aos/externals/WPILib/WPILib/RobotBase.h
+++ b/aos/externals/WPILib/WPILib/RobotBase.h
@@ -13,6 +13,11 @@
 
 class DriverStation;
 
+/**
+ * This macro will set up the given class (which must be a (direct or indirect)
+ * RobotBase subclass) so that when the user code is loaded, it will be
+ * instantiated and StartCompetition() will be called on the instance.
+ */
 #define START_ROBOT_CLASS(_ClassName_) \
 	RobotBase *FRC_userClassFactory() \
 	{ \
@@ -28,17 +33,14 @@
 
 /**
  * Implement a Robot Program framework.
- * The RobotBase class is intended to be subclassed by a user creating a robot program.
- * Overridden Autonomous() and OperatorControl() methods are called at the appropriate time
- * as the match proceeds. In the current implementation, the Autonomous code will run to
- * completion before the OperatorControl code could start. In the future the Autonomous code
- * might be spawned as a task, then killed at the end of the Autonomous period.
+ * The RobotBase class is intended to be subclassed by a user creating a robot
+ * program, possibly indirectly through one of the subclasses included in this
+ * library.
  */
 class RobotBase {
 	friend class RobotDeleter;
 public:
 	static RobotBase &getInstance();
-	static void setInstance(RobotBase* robot);
 
 	bool IsEnabled();
 	bool IsDisabled();
@@ -49,13 +51,15 @@
 	bool IsNewDataAvailable();
 	Watchdog &GetWatchdog();
 	static void startRobotTask(FUNCPTR factory);
-	static void robotTask(FUNCPTR factory, Task *task);
 
 protected:
 	virtual ~RobotBase();
 	virtual void StartCompetition() = 0;
 	RobotBase();
 
+	static void setInstance(RobotBase* robot);
+	static void robotTask(FUNCPTR factory, Task *task);
+
 	Task *m_task;
 	Watchdog m_watchdog;
 	DriverStation *m_ds;
diff --git a/aos/externals/WPILib/WPILib/RobotDrive.cpp b/aos/externals/WPILib/WPILib/RobotDrive.cpp
index 14e28f5..1d538c5 100644
--- a/aos/externals/WPILib/WPILib/RobotDrive.cpp
+++ b/aos/externals/WPILib/WPILib/RobotDrive.cpp
@@ -725,7 +725,7 @@
 
 void RobotDrive::GetDescription(char *desc)
 {
-	sprintf(desc, "RobotDrive");
+	snprintf(desc, 64, "RobotDrive");
 }
 
 void RobotDrive::StopMotor()
diff --git a/aos/externals/WPILib/WPILib/SafePWM.cpp b/aos/externals/WPILib/WPILib/SafePWM.cpp
index 484fbe6..989d782 100644
--- a/aos/externals/WPILib/WPILib/SafePWM.cpp
+++ b/aos/externals/WPILib/WPILib/SafePWM.cpp
@@ -100,7 +100,7 @@
 
 void SafePWM::GetDescription(char *desc)
 {
-	sprintf(desc, "PWM %d on module %d", GetChannel(), GetModuleNumber());
+	snprintf(desc, 64, "PWM %d on module %d", GetChannel(), GetModuleNumber());
 }
 
 /**
diff --git a/aos/externals/WPILib/WPILib/SensorBase.cpp b/aos/externals/WPILib/WPILib/SensorBase.cpp
index a68d260..5574a35 100644
--- a/aos/externals/WPILib/WPILib/SensorBase.cpp
+++ b/aos/externals/WPILib/WPILib/SensorBase.cpp
@@ -7,7 +7,6 @@
 #include "SensorBase.h"
 
 #include "NetworkCommunication/LoadOut.h"
-#include "WPIErrors.h"
 
 const UINT32 SensorBase::kSystemClockTicksPerMicrosecond;
 const UINT32 SensorBase::kDigitalChannels;
@@ -18,11 +17,11 @@
 const UINT32 SensorBase::kSolenoidModules;
 const UINT32 SensorBase::kPwmChannels;
 const UINT32 SensorBase::kRelayChannels;
-const UINT32 SensorBase::kChassisSlots;
 SensorBase *SensorBase::m_singletonList = NULL;
+ReentrantSemaphore SensorBase::m_singletonListSemaphore;
 
 /**
- * Creates an instance of the sensor base and gets an FPGA handle
+ * Creates an instance of SensorBase.
  */
 SensorBase::SensorBase()
 {
@@ -36,27 +35,31 @@
 }
 
 /**
- * Add sensor to the singleton list.
- * Add this sensor to the list of singletons that need to be deleted when
- * the robot program exits. Each of the sensors on this list are singletons,
- * that is they aren't allocated directly with new, but instead are allocated
- * by the static GetInstance method. As a result, they are never deleted when
- * the program exits. Consequently these sensors may still be holding onto
- * resources and need to have their destructors called at the end of the program.
+ * @brief Add sensor to the singleton list.
+ * Add this object to the list of singletons that need to be deleted when
+ * the robot program exits. Each of the objects on this list are singletons,
+ * that is they aren't allocated directly by user code, but instead are
+ * allocated by (for example) a static GetInstance method. Because of this, they
+ * need some way to be freed when the module is unloaded so that they can free
+ * any resources that they are holding on to.
+ * @see #DeleteSingletons()
  */
 void SensorBase::AddToSingletonList()
 {
+  Synchronized sync(m_singletonListSemaphore);
 	m_nextSingleton = m_singletonList;
 	m_singletonList = this;
 }
 
 /**
- * Delete all the singleton classes on the list.
- * All the classes that were allocated as singletons need to be deleted so
+ * @brief Delete all the singleton objects on the list.
+ * All the objects that were allocated as singletons need to be deleted so
  * their resources can be freed.
+ * @see #AddToSingletonList()
  */
 void SensorBase::DeleteSingletons()
 {
+  Synchronized sync(m_singletonListSemaphore);
 	for (SensorBase *next = m_singletonList; next != NULL;)
 	{
 		SensorBase *tmp = next;
diff --git a/aos/externals/WPILib/WPILib/SensorBase.h b/aos/externals/WPILib/WPILib/SensorBase.h
index 1b149b1..3f12360 100644
--- a/aos/externals/WPILib/WPILib/SensorBase.h
+++ b/aos/externals/WPILib/WPILib/SensorBase.h
@@ -7,20 +7,19 @@
 #ifndef SENSORBASE_H_
 #define SENSORBASE_H_
 
-#include "ChipObject/NiRio.h"
 #include "ErrorBase.h"
 #include <stdio.h>
 #include "Base.h"
+#include "Synchronized.h"
 
 /**
  * Base class for all sensors.
  * Stores most recent status information as well as containing utility functions for checking
- * channels and error processing.
+ * channels.
  */
 class SensorBase: public ErrorBase {
 public:
 	SensorBase();
-	virtual ~SensorBase();
 	static void DeleteSingletons();
 	static UINT32 GetDefaultAnalogModule() { return 1; }
 	static UINT32 GetDefaultDigitalModule() { return 1; }
@@ -36,6 +35,9 @@
 	static bool CheckAnalogChannel(UINT32 channel);
 	static bool CheckSolenoidChannel(UINT32 channel);
 
+  // NOT vxworks system clock ticks (returned by sysClkRateGet() from sysLib).
+  // TODO: Document what this actually is (has something to do with FPGA times).
+  // 40kHz clock?
 	static const UINT32 kSystemClockTicksPerMicrosecond = 40;
 	static const UINT32 kDigitalChannels = 14;
 	static const UINT32 kAnalogChannels = 8;
@@ -45,14 +47,19 @@
 	static const UINT32 kSolenoidModules = 2;
 	static const UINT32 kPwmChannels = 10;
 	static const UINT32 kRelayChannels = 8;
-	static const UINT32 kChassisSlots = 8;
+
 protected:
 	void AddToSingletonList();
+  // Subclasses that don't use the singleton list mechanism should make this
+  // public, but ones that do should keep it protected so that users can not
+  // delete the singleton instance(s).
+	virtual ~SensorBase();
 
 private:
 	DISALLOW_COPY_AND_ASSIGN(SensorBase);
 	static SensorBase *m_singletonList;
 	SensorBase *m_nextSingleton;
+  static ReentrantSemaphore m_singletonListSemaphore;
 };
 
 
diff --git a/aos/externals/WPILib/WPILib/SimpleRobot.h b/aos/externals/WPILib/WPILib/SimpleRobot.h
index d963805..88143a6 100644
--- a/aos/externals/WPILib/WPILib/SimpleRobot.h
+++ b/aos/externals/WPILib/WPILib/SimpleRobot.h
@@ -11,6 +11,10 @@
 
 /**
  * @todo If this is going to last until release, it needs a better name.
+ * Overridden Autonomous() and OperatorControl() methods are called at the appropriate time
+ * as the match proceeds. In the current implementation, the Autonomous code will run to
+ * completion before the OperatorControl code could start. In the future the Autonomous code
+ * might be spawned as a task, then killed at the end of the Autonomous period.
  */
 class SimpleRobot: public RobotBase
 {
diff --git a/aos/externals/WPILib/WPILib/Solenoid.cpp b/aos/externals/WPILib/WPILib/Solenoid.cpp
index 35813af..7a56a09 100644
--- a/aos/externals/WPILib/WPILib/Solenoid.cpp
+++ b/aos/externals/WPILib/WPILib/Solenoid.cpp
@@ -30,9 +30,10 @@
 	Resource::CreateResourceObject(&m_allocated, tSolenoid::kNumDO7_0Elements * kSolenoidChannels);
 
 	snprintf(buf, 64, "Solenoid %d (Module: %d)", m_channel, m_moduleNumber);
-	if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels + m_channel - 1, buf) == ~0ul)
+	if (m_allocated->Allocate((m_moduleNumber - 1) * kSolenoidChannels
+                            + m_channel - 1,
+                            buf, this) == ~0ul)
 	{
-		CloneError(m_allocated);
 		return;
 	}
 
@@ -72,7 +73,8 @@
 {
 	if (CheckSolenoidModule(m_moduleNumber))
 	{
-		m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels + m_channel - 1);
+		m_allocated->Free((m_moduleNumber - 1) * kSolenoidChannels + m_channel - 1,
+                      this);
 	}
 }
 
diff --git a/aos/externals/WPILib/WPILib/SolenoidBase.cpp b/aos/externals/WPILib/WPILib/SolenoidBase.cpp
index cc1a2c4..1f38869 100644
--- a/aos/externals/WPILib/WPILib/SolenoidBase.cpp
+++ b/aos/externals/WPILib/WPILib/SolenoidBase.cpp
@@ -50,7 +50,7 @@
 }
 
 /**
- * Set the value of a solenoid.
+ * Set the value of 1 or more solenoids at the same time.
  * 
  * @param value The value you want to set on the module.
  * @param mask The channels you want to be affected.
@@ -63,8 +63,9 @@
 		Synchronized sync(m_semaphore);
 		UINT8 currentValue = m_fpgaSolenoidModule->readDO7_0(m_moduleNumber - 1, &localStatus);
 		// Zero out the values to change
-		currentValue = currentValue & ~mask;
-		currentValue = currentValue | (value & mask);
+		currentValue &= ~mask;
+    // Actually set the values.
+		currentValue |= value & mask;
 		m_fpgaSolenoidModule->writeDO7_0(m_moduleNumber - 1, currentValue, &localStatus);
 	}
 	wpi_setError(localStatus);
diff --git a/aos/externals/WPILib/WPILib/SolenoidBase.h b/aos/externals/WPILib/WPILib/SolenoidBase.h
index a7a67a6..a9fdf61 100644
--- a/aos/externals/WPILib/WPILib/SolenoidBase.h
+++ b/aos/externals/WPILib/WPILib/SolenoidBase.h
@@ -15,17 +15,25 @@
 /**
  * SolenoidBase class is the common base class for the Solenoid and
  * DoubleSolenoid classes.
+ * It also supports getting and setting the values of all solenoids on a given
+ * module at the same time directly.
  */
 class SolenoidBase : public SensorBase {
 public:
+	explicit SolenoidBase(UINT8 moduleNumber);
 	virtual ~SolenoidBase();
+
+	void Set(UINT8 value, UINT8 mask);
+
 	UINT8 GetAll();
+  /**
+   * Set the value of all of the solenoids at the same time.
+   *
+   * @param value The values you want to set all of the solenoids to.
+   */
+  void SetAll(UINT8 value) { Set(value, 0xFF); }
 
 protected:
-	explicit SolenoidBase(UINT8 moduleNumber);
-	void Set(UINT8 value, UINT8 mask);
-	virtual void InitSolenoid() = 0;
-
 	UINT32 m_moduleNumber; ///< Slot number where the module is plugged into the chassis.
 	static Resource *m_allocated;
 
diff --git a/aos/externals/WPILib/WPILib/Synchronized.cpp b/aos/externals/WPILib/WPILib/Synchronized.cpp
index eb17318..fd133b5 100644
--- a/aos/externals/WPILib/WPILib/Synchronized.cpp
+++ b/aos/externals/WPILib/WPILib/Synchronized.cpp
@@ -20,7 +20,7 @@
 	semTake(m_semaphore, WAIT_FOREVER);
 }
 
-Synchronized::Synchronized(ReentrantSemaphore& semaphore)
+Synchronized::Synchronized(const ReentrantSemaphore& semaphore)
 {
 	m_semaphore = semaphore.m_semaphore;
 	semTake(m_semaphore, WAIT_FOREVER);
diff --git a/aos/externals/WPILib/WPILib/Synchronized.h b/aos/externals/WPILib/WPILib/Synchronized.h
index 28cfbc3..4e44b0e 100644
--- a/aos/externals/WPILib/WPILib/Synchronized.h
+++ b/aos/externals/WPILib/WPILib/Synchronized.h
@@ -27,11 +27,15 @@
  *
  * This class is safe to use in static variables because it does not depend on
  * any other C++ static constructors or destructors.
+ *
+ * The instance methods are marked const because using this class from multiple
+ * threads at once is safe and they don't actually modify the value of any of
+ * the instance variables.
  */
 class ReentrantSemaphore
 {
 public:
-	explicit ReentrantSemaphore() {
+	ReentrantSemaphore() {
 		m_semaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE);
 	}
 	~ReentrantSemaphore() {
@@ -42,7 +46,7 @@
 	 * Lock the semaphore, blocking until it's available.
 	 * @return 0 for success, -1 for error. If -1, the error will be in errno.
 	 */
-	int take() {
+	int take() const {
 		return semTake(m_semaphore, WAIT_FOREVER);
 	}
 
@@ -50,7 +54,7 @@
 	 * Unlock the semaphore.
 	 * @return 0 for success, -1 for error. If -1, the error will be in errno.
 	 */
-	int give() {
+	int give() const {
 		return semGive(m_semaphore);
 	}
 
@@ -83,8 +87,8 @@
 {
 public:
 	explicit Synchronized(SEM_ID);
-	explicit Synchronized(ReentrantSemaphore&);
-	virtual ~Synchronized();
+	explicit Synchronized(const ReentrantSemaphore&);
+	~Synchronized();
 private:
 	SEM_ID m_semaphore;
 
diff --git a/aos/externals/WPILib/WPILib/Task.cpp b/aos/externals/WPILib/WPILib/Task.cpp
index b81b16e..37f7d2f 100644
--- a/aos/externals/WPILib/WPILib/Task.cpp
+++ b/aos/externals/WPILib/WPILib/Task.cpp
@@ -18,6 +18,7 @@
 
 /**
  * Create but don't launch a task.
+ * Does not use any floating point registers.
  * @param name The name of the task.  "FRC_" will be prepended to the task name.
  * @param function The address of the function to run as the new task.
  * @param priority The VxWorks priority for the task.
@@ -38,6 +39,9 @@
 	nUsageReporting::report(nUsageReporting::kResourceType_Task, instances, 0, m_taskName);
 }
 
+/**
+ * Does not use any floating point registers.
+ */
 Task::~Task()
 {
 	if (m_taskID != kInvalidTaskID) Stop();
@@ -47,18 +51,20 @@
 
 /**
  * Starts this task.
- * If it is already running or unable to start, it fails and returns false.
+ * Does not use any floating point registers.
+ * @return true on success
  */
 bool Task::Start(UINT32 arg0, UINT32 arg1, UINT32 arg2, UINT32 arg3, UINT32 arg4, 
 		UINT32 arg5, UINT32 arg6, UINT32 arg7, UINT32 arg8, UINT32 arg9)
 {
+  // Don't have to lock m_prioritySemaphore because this code isn't changing it.
 	m_taskID = taskSpawn(m_taskName,
 						m_priority,
 						VX_FP_TASK,							// options
 						m_stackSize,						// stack size
 						m_function,							// function to start
-						arg0, arg1, arg2, arg3, arg4,	// parameter 1 - pointer to this class
-						arg5, arg6, arg7, arg8, arg9);// additional unused parameters
+						arg0, arg1, arg2, arg3, arg4,	// parameters to pass to m_function
+						arg5, arg6, arg7, arg8, arg9);// additional parameters
 	bool ok = HandleError(m_taskID);
 	if (!ok) m_taskID = kInvalidTaskID;
 	return ok;
@@ -66,8 +72,7 @@
 
 /**
  * Restarts a running task.
- * If the task isn't started, it starts it.
- * @return false if the task is running and we are unable to kill the previous instance
+ * @return true on success
  */
 bool Task::Restart()
 {
@@ -75,8 +80,8 @@
 }
 
 /**
- * Kills the running task.
- * @returns true on success false if the task doesn't exist or we are unable to kill it.
+ * Makes sure that the task is not running.
+ * @returns true on success
  */
 bool Task::Stop()
 {
@@ -114,7 +119,7 @@
 
 /**
  * Pauses a running task.
- * Returns true on success, false if unable to pause or the task isn't running.
+ * Returns true on success
  */
 bool Task::Suspend()
 {
@@ -123,7 +128,7 @@
 
 /**
  * Resumes a paused task.
- * Returns true on success, false if unable to resume or if the task isn't running/paused.
+ * Returns true on success
  */
 bool Task::Resume()
 {
@@ -146,6 +151,7 @@
  */
 INT32 Task::GetPriority()
 {
+  Synchronized sync(m_prioritySemaphore);
 	if (HandleError(taskPriorityGet(m_taskID, &m_priority)))
 		return m_priority;
 	else
@@ -161,6 +167,7 @@
  */
 bool Task::SetPriority(INT32 priority)
 {
+  Synchronized sync(m_prioritySemaphore);
 	m_priority = priority;
 	return HandleError(taskPrioritySet(m_taskID, m_priority));
 }
diff --git a/aos/externals/WPILib/WPILib/Task.h b/aos/externals/WPILib/WPILib/Task.h
index 03c9555..d1ad259 100644
--- a/aos/externals/WPILib/WPILib/Task.h
+++ b/aos/externals/WPILib/WPILib/Task.h
@@ -9,10 +9,14 @@
 
 #include "ErrorBase.h"
 #include <vxWorks.h>
+#include "Synchronized.h"
 
 /**
- * WPI task is a wrapper for the native Task object.
- * All WPILib tasks are managed by a static task manager for simplified cleanup.
+ * WPI task is a wrapper for a native VxWorks task.
+ *
+ * Some functions (documented) are guaranteed not to use any floating point so
+ * that it is safe to use them from tasks that do not have the VX_FP_TASK flag
+ * set (like during startup).
  **/
 class Task : public ErrorBase
 {
@@ -47,6 +51,7 @@
 	INT32 m_taskID;
 	UINT32 m_stackSize;
 	INT32 m_priority;
+  ReentrantSemaphore m_prioritySemaphore;
 	bool HandleError(STATUS results);
 	DISALLOW_COPY_AND_ASSIGN(Task);
 };
diff --git a/aos/externals/WPILib/WPILib/Timer.cpp b/aos/externals/WPILib/WPILib/Timer.cpp
index e9fcde7..7638b59 100644
--- a/aos/externals/WPILib/WPILib/Timer.cpp
+++ b/aos/externals/WPILib/WPILib/Timer.cpp
@@ -12,6 +12,7 @@
 
 #include "Synchronized.h"
 #include "Utility.h"
+#include "Global.h"
 
 /**
  * Pause the task for a specified time.
@@ -180,7 +181,7 @@
 {
 	// FPGA returns the timestamp in microseconds
 	// Call the helper GetFPGATime() in Utility.cpp
-	return GetFPGATime() * 1.0e-6;
+	return Global::GetInstance()->GetFPGATime() * 1.0e-6;
 }
 
 // Internal function that reads the PPC timestamp counter.
diff --git a/aos/externals/WPILib/WPILib/Utility.cpp b/aos/externals/WPILib/WPILib/Utility.cpp
index 5ce84f9..680da97 100644
--- a/aos/externals/WPILib/WPILib/Utility.cpp
+++ b/aos/externals/WPILib/WPILib/Utility.cpp
@@ -7,34 +7,34 @@
 #include "Utility.h"
 
 #include "NetworkCommunication/FRCComm.h"
-#include "ChipObject.h"
 #include "Task.h"
 #include <dbgLib.h>
 #include <stdio.h>
+#include <taskLib.h>
 #include <sysSymTbl.h>
 #include "nivision.h"
 
-#define DBG_DEMANGLE_PRINT_LEN 256  /* Num chars of demangled names to print */
+#define DBG_DEMANGLE_PRINT_LEN MAX_SYS_SYM_LEN  /* Num chars of demangled names to print */
 
 extern "C"
 {
 	extern char * cplusDemangle (char *source, char *dest, INT32 n);
 }
 
-char *wpi_getLabel(UINT addr, INT32 *found)
+void wpi_getLabel(UINT addr, char *label, INT32 *found)
 {
 	INT32 pVal;
 	SYM_TYPE pType;
 	char name[MAX_SYS_SYM_LEN + 1];
-	static char label[DBG_DEMANGLE_PRINT_LEN + 1 + 11];
-	bzero(label, DBG_DEMANGLE_PRINT_LEN + 1 + 11);
+  static const size_t kLabelSize = DBG_DEMANGLE_PRINT_LEN + 1 + 11;
+	bzero(label, kLabelSize);
 
 	if (symFindByValue(sysSymTbl, addr, name, &pVal, &pType) == OK)
 	{
-		cplusDemangle(name, label, sizeof(label) - 11);
+		cplusDemangle(name, label, kLabelSize - 11);
 		if ((UINT)pVal != addr)
 		{
-			sprintf(&label[strlen(label)], "+0x%04x", addr-pVal);
+			snprintf(label + strlen(label), kLabelSize - strlen(label), "+0x%04x", addr-pVal);
 			if (found) *found = 2;
 		}
 		else
@@ -44,11 +44,9 @@
 	}
 	else
 	{
-		sprintf(label, "0x%04x", addr);
+		snprintf(label, kLabelSize, "0x%04x", addr);
 		if (found) *found = 0;
 	}
-
-	return label;
 }
 /*
 static void wpiTracePrint(INSTR *caller, INT32 func, INT32 nargs, INT32 *args, INT32 taskId, BOOL isKernelAdrs)
@@ -56,15 +54,16 @@
 	char buf [MAX_SYS_SYM_LEN * 2];
 	INT32 ix;
 	INT32 len = 0;
-	len += sprintf (&buf [len], "%s <%#010x>: ", wpi_getLabel((UINT)caller), (INT32)caller);
-	len += sprintf (&buf [len], "%s <%#010x> (", wpi_getLabel((UINT)func), func);
+	len += snprintf (&buf [len], sizeof(buf) - len, "%s <%#010x>: ", wpi_getLabel((UINT)caller), (INT32)caller);
+	len += snprintf (&buf [len], sizeof(buf) - len, "%s <%#010x> (", wpi_getLabel((UINT)func), func);
 	for (ix = 0; ix < nargs; ix++)
 	{
-		if (ix != 0)
-			len += sprintf (&buf [len], ", ");
-		len += sprintf (&buf [len], "%#x", args [ix]);
+		if (ix != 0) {
+			len += snprintf (&buf [len], sizeof(buf) - len, ", ");
+    }
+		len += snprintf (&buf [len], sizeof(buf) - len, "%#x", args [ix]);
 	}
-	len += sprintf (&buf [len], ")\n");
+	len += snprintf (&buf [len], sizeof(buf) - len, ")\n");
 
 	printf(buf);
 }
@@ -77,7 +76,8 @@
 	INT32 nameFound = 0;
 	INT32 params = 0;
 	INT32 totalnargs = nargs;
-	char *funcName = wpi_getLabel((UINT)func, &nameFound);
+  char funcName[DBG_DEMANGLE_PRINT_LEN + 1 + 11];
+	wpi_getLabel((UINT)func, funcName, &nameFound);
 	// Ignore names that are not exact symbol address matches.
 	if (nameFound != 1) return;
 
@@ -107,23 +107,24 @@
 	}
 	char *funcNameEnd = strchr(funcName, '(');
 	*funcNameEnd = 0;
-	len += sprintf (&buf [len], funcName);
+	len += snprintf (buf + len, sizeof(buf) - len, funcName);
 
 	// If this is a member function, print out the this pointer value.
 	if (totalnargs - params == 1)
 	{
-		len += sprintf (&buf [len], "<this=%#x>", args [0]);
+		len += snprintf (buf + len, sizeof(buf) - len, "<this=%#x>", args [0]);
 	}
 
 	// Print out the argument values.
-	len += sprintf (&buf [len], "(");
+	len += snprintf (buf + len, sizeof(buf) - len, "(");
 	for (ix = totalnargs - params; ix < nargs; ix++)
 	{
-		if (ix != totalnargs - params)
-			len += sprintf (&buf [len], ", ");
-		len += sprintf (&buf [len], "%#x", args [ix]);
+		if (ix != totalnargs - params) {
+			len += snprintf (buf + len, sizeof(buf) - len, ", ");
+    }
+		len += snprintf (buf + len, sizeof(buf) - len, "%#x", args [ix]);
 	}
-	len += sprintf (&buf [len], ")\n");
+	len += snprintf (buf + len, sizeof(buf) - len, ")\n");
 
 	printf(buf);
 }
@@ -135,7 +136,11 @@
 
 static INT32 wpiStackTask(INT32 taskId)
 {
-	taskDelay(1);
+  // Make sure it's suspended in spite of any scheduler weirdness or whatever.
+  while (!taskIsSuspended(taskId)) {
+	  taskDelay(1);
+  }
+
 	//tt(taskId);
 
 	REG_SET regs;
@@ -212,10 +217,10 @@
 		// If an error message was specified, include it
 		// Build error string
 		if(message != NULL) {
-			sprintf(error, "Assertion failed: \"%s\", \"%s\" failed in %s() in %s at line %d\n", 
+			snprintf(error, sizeof(error), "Assertion failed: \"%s\", \"%s\" failed in %s() in %s at line %d\n",
 							 message, conditionText, funcName, fileName, lineNumber);
 		} else {
-			sprintf(error, "Assertion failed: \"%s\" in %s() in %s at line %d\n", 
+			snprintf(error, sizeof(error), "Assertion failed: \"%s\" in %s() in %s at line %d\n",
 							 conditionText, funcName, fileName, lineNumber);
 		}
 		
@@ -248,10 +253,10 @@
 	// If an error message was specified, include it
 	// Build error string
 	if(message != NULL) {
-		sprintf(error, "Assertion failed: \"%s\", \"%d\" %s \"%d\" in %s() in %s at line %d\n", 
+		snprintf(error, sizeof(error), "Assertion failed: \"%s\", \"%d\" %s \"%d\" in %s() in %s at line %d\n",
 						 message, valueA, equalityType, valueB, funcName, fileName, lineNumber);
 	} else {
-		sprintf(error, "Assertion failed: \"%d\" %s \"%d\" in %s() in %s at line %d\n", 
+		snprintf(error, sizeof(error), "Assertion failed: \"%d\" %s \"%d\" in %s() in %s at line %d\n",
 						 valueA, equalityType, valueB, funcName, fileName, lineNumber);
 	}
 	
@@ -302,137 +307,3 @@
 	}
 	return valueA != valueB;
 }
-
-
-/**
- * Return the FPGA Version number.
- * For now, expect this to be competition year.
- * @return FPGA Version number.
- */
-UINT16 GetFPGAVersion()
-{
-	tRioStatusCode status = 0;
-	tGlobal *global = tGlobal::create(&status);
-	UINT16 version = global->readVersion(&status);
-	delete global;
-	wpi_setGlobalError(status);
-	return version;
-}
-
-/**
- * Return the FPGA Revision number.
- * The format of the revision is 3 numbers.
- * The 12 most significant bits are the Major Revision.
- * the next 8 bits are the Minor Revision.
- * The 12 least significant bits are the Build Number.
- * @return FPGA Revision number.
- */
-UINT32 GetFPGARevision()
-{
-	tRioStatusCode status = 0;
-	tGlobal *global = tGlobal::create(&status);
-	UINT32 revision = global->readRevision(&status);
-	delete global;
-	wpi_setGlobalError(status);
-	return revision;
-}
-
-/**
- * Read the microsecond-resolution timer on the FPGA.
- * 
- * @return The current time in microseconds according to the FPGA (since FPGA reset).
- */
-UINT32 GetFPGATime()
-{
-	tRioStatusCode status = 0;
-	tGlobal *global = tGlobal::create(&status);
-	UINT32 time = global->readLocalTime(&status);
-	delete global;
-	wpi_setGlobalError(status);
-	return time;
-}
-
-// RT hardware access functions exported from ni_emb.out
-extern "C"
-{
-	INT32 UserSwitchInput(INT32 nSwitch);
-	INT32 LedInput(INT32 led);
-	INT32 LedOutput(INT32 led, INT32 value);
-}
-
-/**
- * Read the value of the USER1 DIP switch on the cRIO.
- */
-INT32 GetRIOUserSwitch()
-{
-	INT32 switchValue = UserSwitchInput(0);
-	wpi_assert(switchValue >= 0);
-	return switchValue > 0;
-}
-
-/**
- * Set the state of the USER1 status LED on the cRIO.
- */
-void SetRIOUserLED(UINT32 state)
-{
-	LedOutput(0, state > 0);
-}
-
-/**
- * Get the current state of the USER1 status LED on the cRIO.
- * @return The curent state of the USER1 LED.
- */
-INT32 GetRIOUserLED()
-{
-	return LedInput(0);
-}
-
-/**
- * Toggle the state of the USER1 status LED on the cRIO.
- * @return The new state of the USER1 LED.
- */
-INT32 ToggleRIOUserLED()
-{
-	INT32 ledState = !GetRIOUserLED();
-	SetRIOUserLED(ledState);
-	return ledState;
-}
-
-/**
- * Set the state of the FPGA status LED on the cRIO.
- */
-void SetRIO_FPGA_LED(UINT32 state)
-{
-	tRioStatusCode status = 0;
-	tGlobal *global = tGlobal::create(&status);
-	global->writeFPGA_LED(state, &status);
-	wpi_setGlobalError(status);
-	delete global;
-}
-
-/**
- * Get the current state of the FPGA status LED on the cRIO.
- * @return The curent state of the FPGA LED.
- */
-INT32 GetRIO_FPGA_LED()
-{
-	tRioStatusCode status = 0;
-	tGlobal *global = tGlobal::create(&status);
-	bool ledValue = global->readFPGA_LED(&status);
-	wpi_setGlobalError(status);
-	delete global;
-	return ledValue;
-}
-
-/**
- * Toggle the state of the FPGA status LED on the cRIO.
- * @return The new state of the FPGA LED.
- */
-INT32 ToggleRIO_FPGA_LED()
-{
-	INT32 ledState = !GetRIO_FPGA_LED();
-	SetRIO_FPGA_LED(ledState);
-	return ledState;
-}
-
-
diff --git a/aos/externals/WPILib/WPILib/Utility.h b/aos/externals/WPILib/WPILib/Utility.h
index 01bcbff..4462b7c 100644
--- a/aos/externals/WPILib/WPILib/Utility.h
+++ b/aos/externals/WPILib/WPILib/Utility.h
@@ -22,20 +22,10 @@
 bool wpi_assertEqual_impl(int valueA, int valueB, const char *message, const char *fileName,UINT32 lineNumber, const char *funcName);
 bool wpi_assertNotEqual_impl(int valueA, int valueB, const char *message, const char *fileName,UINT32 lineNumber, const char *funcName);
 
-char *wpi_getLabel(UINT addr, INT32 *found = NULL);
+// Will use up to (DBG_DEMANGLE_PRINT_LEN + 1 + 11) of label.
+void wpi_getLabel(UINT addr, char *label, INT32 *found = NULL);
 void wpi_selfTrace();
 void wpi_suspendOnAssertEnabled(bool enabled);
 void wpi_stackTraceOnAssertEnable(bool enabled);
 
-UINT16 GetFPGAVersion();
-UINT32 GetFPGARevision();
-UINT32 GetFPGATime();
-INT32 GetRIOUserSwitch();
-void SetRIOUserLED(UINT32 state);
-INT32 GetRIOUserLED();
-INT32 ToggleRIOUserLED();
-void SetRIO_FPGA_LED(UINT32 state);
-INT32 GetRIO_FPGA_LED();
-INT32 ToggleRIO_FPGA_LED();
-
 #endif // UTILITY_H_
diff --git a/aos/externals/WPILib/WPILib/networktables2/stream/FDIOStream.cpp b/aos/externals/WPILib/WPILib/networktables2/stream/FDIOStream.cpp
index 35d3c41..5f9fa3e 100644
--- a/aos/externals/WPILib/WPILib/networktables2/stream/FDIOStream.cpp
+++ b/aos/externals/WPILib/WPILib/networktables2/stream/FDIOStream.cpp
@@ -12,7 +12,7 @@
 #include <errno.h>
 #include <stdlib.h>
 #ifdef _WRS_KERNEL
-#include <iolib.h>
+#include <ioLib.h>
 #else
 #include <unistd.h>
 #endif
diff --git a/doc/wpilib-check-notes.txt b/doc/wpilib-check-notes.txt
new file mode 100644
index 0000000..e8c1a0f
--- /dev/null
+++ b/doc/wpilib-check-notes.txt
@@ -0,0 +1,50 @@
+This file has the results of going through all of the WPILib code that we use.
+If you use any more parts of WPILib, then check them carefully (preferrably get
+multiple people to do it) and then add the results here.
+The notes are so that it is clear what has been checked and how things interact
+in nonintiutive ways to potentially create subtle bugs.
+
+DriverStationEnhancedIO
+  only checked what DriverStation does to it
+  the rest of it is implemented horribly
+DriverStation
+  GetMatchTime() is garbage (DriverStation shouldn't keep track of that
+    information and it does a bad job of it)
+  don't call Set*PriorityDashboardPackerToUse
+  IsNewControlData() and WaitForData() are OK
+  make sure to GetDataReadLock() correctly when you want to read data
+  GetStickAxis uses brain-dead math
+  most of the "helper" methods to retrieve parts of the control data have no
+    benefit and do other weird things besides just get the value
+Dashboard
+  GetStatusBuffer and Flush do get called from a separate task by DriverStation
+  only checked what DriverStation does to it
+MotorSafetyHelper
+  CheckMotors() does get called from a separate task by DriverStation
+RobotBase
+  the Is* methods are garbage (call them directly on the instances of the
+    objects that they forward too)
+Task
+  the constructor and Start get called in RobotBase in a task without the
+    floating point save flag set
+ReentrantSemaphore
+Synchronized
+Error
+  it synchronizes all of the non-const methods internally, and does it right
+  Do not use EnableStackTrace.
+ErrorBase
+  the mutable Error instance varible is weird, but safe
+Utility
+  it is a bad idea to use wpi_selfTrace()
+    That gets called if you use Error::EnableStackTrace or
+      wpi_stackOnAssertEnable, so don't call those.
+  The assertions NEVER stop on failure unless you wpi_SuspendOnAssertEnabled.
+Global
+Watchdog
+  the return value of Feed() is garbage
+Module
+DigitalModule
+  didn't look at I2C
+AnalogChannel
+AnalogModule
+NetworkRobot
diff --git a/doc/wpilib-issues.txt b/doc/wpilib-issues.txt
new file mode 100644
index 0000000..1b46940
--- /dev/null
+++ b/doc/wpilib-issues.txt
@@ -0,0 +1,6 @@
+sprintf
+ErrorBase/Error not thread safe (aka CANJaguar)
+all of the sync issues last year
+Module not thread safe
+
+compressor turned on as soon as power was turned on (2 times in a row)
diff --git a/frc971/crio/crio.gyp b/frc971/crio/crio.gyp
index 9542e0b..9000f68 100644
--- a/frc971/crio/crio.gyp
+++ b/frc971/crio/crio.gyp
@@ -28,17 +28,35 @@
       ],
     },
     {
+      'target_name': 'dumb_main',
+      'type': 'static_library',
+      'sources': [
+        'dumb_main.cc',
+      ],
+      'dependencies': [
+        '<(EXTERNALS):WPILib',
+        '<(AOS)/common/common.gyp:common',
+      ],
+    },
+    {
       'target_name': 'FRC_UserProgram',
       'type': 'shared_library',
       'dependencies': [
-        'user_program'
+        '<(EXTERNALS):libgcc-4.5.2',
+        'dumb_main',
+      ],
+    },
+    {
+      'target_name': 'FRC_UserProgram_old',
+      'type': 'shared_library',
+      'dependencies': [
+        'user_program',
       ],
     },
     {
       'target_name': 'FRC_UserProgram_WithTests',
       'type': 'shared_library',
       'dependencies': [
-        # For testing.
         '<(AOS)/build/aos_all.gyp:Crio',
       ],
     },
diff --git a/frc971/crio/dumb_main.cc b/frc971/crio/dumb_main.cc
new file mode 100644
index 0000000..4accd9c
--- /dev/null
+++ b/frc971/crio/dumb_main.cc
@@ -0,0 +1,19 @@
+#include "WPILib/NetworkRobot/NetworkRobot.h"
+#include "WPILib/RobotBase.h"
+#include "aos/common/Configuration.h"
+
+namespace frc971 {
+
+class MyRobot : public NetworkRobot {
+ public:
+  MyRobot() : NetworkRobot(static_cast<uint16_t>(::aos::NetworkPort::kMotors),
+                           ::aos::configuration::GetIPAddress(
+                               ::aos::configuration::NetworkDevice::kAtom),
+                           static_cast<uint16_t>(::aos::NetworkPort::kDS),
+                           ::aos::configuration::GetIPAddress(
+                               ::aos::configuration::NetworkDevice::kAtom)) {}
+};
+
+}  // namespace frc971
+
+START_ROBOT_CLASS(::frc971::MyRobot);
diff --git a/frc971/output/AtomMotorWriter.cc b/frc971/output/AtomMotorWriter.cc
index 8431fa7..3ba3593 100644
--- a/frc971/output/AtomMotorWriter.cc
+++ b/frc971/output/AtomMotorWriter.cc
@@ -2,14 +2,12 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "aos/aos_core.h"
-#include "aos/common/control_loop/Timing.h"
-#include "aos/common/messages/RobotState.q.h"
-#include "aos/atom_code/output/MotorOutput.h"
+#include "aos/atom_code/output/motor_output.h"
+#include "aos/common/logging/logging.h"
+#include "aos/atom_code/init.h"
 
 #include "frc971/queues/Piston.q.h"
 #include "frc971/control_loops/drivetrain/drivetrain.q.h"
-#include "frc971/constants.h"
 #include "frc971/control_loops/wrist/wrist_motor.q.h"
 #include "frc971/control_loops/shooter/shooter_motor.q.h"
 #include "frc971/control_loops/index/index_motor.q.h"
@@ -26,77 +24,84 @@
 namespace frc971 {
 namespace output {
 
-class MotorWriter : public aos::MotorOutput {
+class MotorWriter : public ::aos::MotorOutput {
   // Maximum age of an output packet before the motors get zeroed instead.
   static const int kOutputMaxAgeMS = 20;
   static const int kEnableDrivetrain = true;
 
-  void RunIteration() {
+  virtual void RunIteration() {
+    values_.digital_module = 0;
+    values_.pressure_switch_channel = 14;
+    values_.compressor_channel = 1;
+    values_.solenoid_module = 0;
+
     drivetrain.output.FetchLatest();
     if (drivetrain.output.IsNewerThanMS(kOutputMaxAgeMS) && kEnableDrivetrain) {
-      AddMotor(TALON, 2, drivetrain.output->right_voltage / 12.0);
-      AddMotor(TALON, 3, drivetrain.output->right_voltage / 12.0);
-      AddMotor(TALON, 5, -drivetrain.output->left_voltage / 12.0);
-      AddMotor(TALON, 6, -drivetrain.output->left_voltage / 12.0);
+      SetPWMOutput(2, drivetrain.output->right_voltage / 12.0, kTalonBounds);
+      SetPWMOutput(3, drivetrain.output->right_voltage / 12.0, kTalonBounds);
+      SetPWMOutput(5, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
+      SetPWMOutput(6, -drivetrain.output->left_voltage / 12.0, kTalonBounds);
     } else {
-      AddMotor(TALON, 2, 0);
-      AddMotor(TALON, 3, 0);
-      AddMotor(TALON, 5, 0);
-      AddMotor(TALON, 6, 0);
+      DisablePWMOutput(2);
+      DisablePWMOutput(3);
+      DisablePWMOutput(5);
+      DisablePWMOutput(6);
       if (kEnableDrivetrain) {
         LOG(WARNING, "drivetrain not new enough\n");
       }
     }
     shifters.FetchLatest();
-    if (shifters.IsNewerThanMS(kOutputMaxAgeMS)) {
-      AddSolenoid(1, shifters->set);
-      AddSolenoid(2, !shifters->set);
+    if (shifters.get()) {
+      SetSolenoid(1, shifters->set);
+      SetSolenoid(2, !shifters->set);
     }
 
     wrist.output.FetchLatest();
     if (wrist.output.IsNewerThanMS(kOutputMaxAgeMS)) {
-      AddMotor(TALON, 10, wrist.output->voltage / 12.0);
+      SetPWMOutput(10, wrist.output->voltage / 12.0, kTalonBounds);
     } else {
-      AddMotor(TALON, 10, 0);
+      DisablePWMOutput(10);
       LOG(WARNING, "wrist not new enough\n");
     }
 
     shooter.output.FetchLatest();
     if (shooter.output.IsNewerThanMS(kOutputMaxAgeMS)) {
-      AddMotor(TALON, 4, shooter.output->voltage / 12.0);
+      SetPWMOutput(4, shooter.output->voltage / 12.0, kTalonBounds);
     } else {
-      AddMotor(TALON, 4, 0);
+      DisablePWMOutput(4);
       LOG(WARNING, "shooter not new enough\n");
     }
 
     angle_adjust.output.FetchLatest();
     if (angle_adjust.output.IsNewerThanMS(kOutputMaxAgeMS)) {
-      AddMotor(TALON, 1, -angle_adjust.output->voltage / 12.0);
+      SetPWMOutput(1, -angle_adjust.output->voltage / 12.0, kTalonBounds);
     } else {
-      AddMotor(TALON, 1, 0);
+      DisablePWMOutput(1);
       LOG(WARNING, "angle adjust is not new enough\n");
     }
 
     index_loop.output.FetchLatest();
+    if (index_loop.output.get()) {
+      SetSolenoid(7, index_loop.output->loader_up);
+      SetSolenoid(8, !index_loop.output->loader_up);
+      SetSolenoid(6, index_loop.output->disc_clamped);
+      SetSolenoid(3, index_loop.output->disc_ejected);
+    }
     if (index_loop.output.IsNewerThanMS(kOutputMaxAgeMS)) {
-      AddMotor(TALON, 8, index_loop.output->intake_voltage / 12.0);
-      AddMotor(TALON, 9, index_loop.output->transfer_voltage / 12.0);
-      AddMotor(TALON, 7, -index_loop.output->index_voltage / 12.0);
-      AddSolenoid(7, index_loop.output->loader_up);
-      AddSolenoid(8, !index_loop.output->loader_up);
-      AddSolenoid(6, index_loop.output->disc_clamped);
-      AddSolenoid(3, index_loop.output->disc_ejected);
+      SetPWMOutput(8, index_loop.output->intake_voltage / 12.0, kTalonBounds);
+      SetPWMOutput(9, index_loop.output->transfer_voltage / 12.0, kTalonBounds);
+      SetPWMOutput(7, -index_loop.output->index_voltage / 12.0, kTalonBounds);
     } else {
-      AddMotor(TALON, 8, 0);
-      AddMotor(TALON, 9, 0);
-      AddMotor(TALON, 7, 0);
+      DisablePWMOutput(8);
+      DisablePWMOutput(9);
+      DisablePWMOutput(7);
       LOG(WARNING, "index not new enough\n");
     }
 
     hangers.FetchLatest();
-    if (hangers.IsNewerThanMS(kOutputMaxAgeMS)) {
-      AddSolenoid(4, hangers->set);
-      AddSolenoid(5, hangers->set);
+    if (hangers.get()) {
+      SetSolenoid(4, hangers->set);
+      SetSolenoid(5, hangers->set);
     }
   }
 };
@@ -104,4 +109,9 @@
 }  // namespace output
 }  // namespace frc971
 
-AOS_RUN(frc971::output::MotorWriter)
+int main() {
+  ::aos::Init();
+  ::frc971::output::MotorWriter writer;
+  writer.Run();
+  ::aos::Cleanup();
+}
diff --git a/frc971/output/output.gyp b/frc971/output/output.gyp
index 4459984..b872713 100644
--- a/frc971/output/output.gyp
+++ b/frc971/output/output.gyp
@@ -29,8 +29,8 @@
             'sources': ['AtomMotorWriter.cc'],
             'dependencies': [
               '<(AOS)/atom_code/output/output.gyp:motor_output',
-              '<(AOS)/atom_code/messages/messages.gyp:messages',
               '<(AOS)/atom_code/atom_code.gyp:init',
+              '<(AOS)/build/aos.gyp:logging',
               '<(DEPTH)/frc971/control_loops/angle_adjust/angle_adjust.gyp:angle_adjust_loop',
               '<(DEPTH)/frc971/control_loops/wrist/wrist.gyp:wrist_loop',
               '<(DEPTH)/frc971/control_loops/index/index.gyp:index_loop',