incremental
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e9b3e75..501c2c0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -94,9 +94,10 @@
 set(Boost_USE_STATIC_LIBS ON)
 hunter_add_package(Boost)
 find_package(Boost)
+include_directories(${Boost_INCLUDE_DIRS}) 
 
 #Openssl
-hunter_add_package(OpenSSL)
+#hunter_add_package(OpenSSL)
 find_package(OpenSSL REQUIRED)
 
 #g3 logging
@@ -200,7 +201,7 @@
     src/test_utils.cpp
     src/msan_test.cpp
     src/ci_map_tests.cpp
-
+    src/ast_video_puller_test.cpp
     ${CMAKE_BINARY_DIR}/generated/blns.hpp
 )
 
diff --git a/aspeed_purley.cmake b/aspeed_purley.cmake
new file mode 100644
index 0000000..2c48ed2
--- /dev/null
+++ b/aspeed_purley.cmake
@@ -0,0 +1,32 @@
+# this one is important
+SET(CMAKE_SYSTEM_NAME Linux)
+#this one not so much
+SET(CMAKE_SYSTEM_VERSION 1)
+SET(CMAKE_SYSTEM_PROCESSOR "armv6")
+set(ARCH "armv6")
+
+SET(CMAKE_CROSSCOMPILING True)
+
+# specify the cross compiler
+#SET(CMAKE_C_COMPILER arm-linux-gnueabi-gcc-4.9) 
+#SET(CMAKE_CXX_COMPILER arm-linux-gnueabi-g++-4.9)
+#SET(CMAKE_C_LINK_EXECUTABLE "clang <OBJECTS> -o  <TARGET> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES>")
+#SET(CMAKE_CXX_LINK_EXECUTABLE "clang++ <OBJECTS> -o  <TARGET> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES>")
+
+set(CMAKE_C_COMPILER /home/ed/deg-bmcfw-core/ToolChain/Host/AST2500/x-tools/arm-aspeed-linux-gnueabi/bin/arm-linux-gcc)
+set(CMAKE_CXX_COMPILER /home/ed/deg-bmcfw-core/ToolChain/Host/AST2500/x-tools/arm-aspeed-linux-gnueabi/bin/arm-linux-g++)
+
+
+set(triple arm-linux-gnueabi)
+set(CMAKE_C_COMPILER_TARGET ${triple})
+set(CMAKE_CXX_COMPILER_TARGET ${triple})
+
+# where is the target environment 
+SET(CMAKE_FIND_ROOT_PATH /home/ed/deg-bmcfw-core/_sysroot/AST2500)
+
+# search for programs in the build host directories
+SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+# for libraries and headers in the target directories
+SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
diff --git a/include/ast_video_puller.hpp b/include/ast_video_puller.hpp
index 214e2df..fd29ca5 100644
--- a/include/ast_video_puller.hpp
+++ b/include/ast_video_puller.hpp
@@ -2,73 +2,76 @@
 
 #include <assert.h>
 #include <ast_video_types.hpp>
+#include <g3log/g3log.hpp>
 #include <iostream>
+#include <mutex>
 #include <vector>
+#include <boost/asio.hpp>
 
 namespace AstVideo {
-class VideoPuller {
-  //
-  // Cursor struct is used in User Mode
-  //
-  typedef struct _cursor_attribution_tag {
-    unsigned int posX;
-    unsigned int posY;
-    unsigned int cur_width;
-    unsigned int cur_height;
-    unsigned int cur_type;  // 0:mono 1:color 2:disappear cursor
-    unsigned int cur_change_flag;
-  } AST_CUR_ATTRIBUTION_TAG;
 
-  //
-  // For storing Cursor Information
-  //
-  typedef struct _cursor_tag {
-    AST_CUR_ATTRIBUTION_TAG attr;
-    // unsigned char     icon[MAX_CUR_OFFSETX*MAX_CUR_OFFSETY*2];
-    unsigned char *icon;  //[64*64*2];
-  } AST_CURSOR_TAG;
+//
+// Cursor struct is used in User Mode
+//
+typedef struct _cursor_attribution_tag {
+  unsigned int posX;
+  unsigned int posY;
+  unsigned int cur_width;
+  unsigned int cur_height;
+  unsigned int cur_type;  // 0:mono 1:color 2:disappear cursor
+  unsigned int cur_change_flag;
+} AST_CUR_ATTRIBUTION_TAG;
 
-  //
-  // For select image format, i.e. 422 JPG420, 444 JPG444, lumin/chrom table, 0
-  // ~ 11, low to high
-  //
-  typedef struct _video_features {
-    short jpg_fmt;  // 422:JPG420, 444:JPG444
-    short lumin_tbl;
-    short chrom_tbl;
-    short tolerance_noise;
-    int w;
-    int h;
-    unsigned char *buf;
-  } FEATURES_TAG;
+//
+// For storing Cursor Information
+//
+typedef struct _cursor_tag {
+  AST_CUR_ATTRIBUTION_TAG attr;
+  // unsigned char     icon[MAX_CUR_OFFSETX*MAX_CUR_OFFSETY*2];
+  unsigned char *icon;  //[64*64*2];
+} AST_CURSOR_TAG;
 
-  //
-  // For configure video engine control registers
-  //
-  typedef struct _image_info {
-    short do_image_refresh;  // Action 0:motion 1:fullframe 2:quick cursor
-    char qc_valid;           // quick cursor enable/disable
-    unsigned int len;
-    int crypttype;
-    char cryptkey[16];
-    union {
-      FEATURES_TAG features;
-      AST_CURSOR_TAG cursor_info;
-    } parameter;
-  } IMAGE_INFO;
+//
+// For select image format, i.e. 422 JPG420, 444 JPG444, lumin/chrom table, 0
+// ~ 11, low to high
+//
+typedef struct _video_features {
+  short jpg_fmt;  // 422:JPG420, 444:JPG444
+  short lumin_tbl;
+  short chrom_tbl;
+  short tolerance_noise;
+  int w;
+  int h;
+  unsigned char *buf;
+} FEATURES_TAG;
 
+//
+// For configure video engine control registers
+//
+typedef struct _image_info {
+  short do_image_refresh;  // Action 0:motion 1:fullframe 2:quick cursor
+  char qc_valid;           // quick cursor enable/disable
+  unsigned int len;
+  int crypttype;
+  char cryptkey[16];
+  union {
+    FEATURES_TAG features;
+    AST_CURSOR_TAG cursor_info;
+  } parameter;
+} IMAGE_INFO;
+
+class SimpleVideoPuller {
  public:
-  VideoPuller() : image_info(){};
+  SimpleVideoPuller() : image_info(){};
 
   void initialize() {
     std::cout << "Opening /dev/video\n";
     video_fd = open("/dev/video", O_RDWR);
     if (!video_fd) {
       std::cout << "Failed to open /dev/video\n";
-      // TODO(Ed) throw exception?
-    } else {
-      std::cout << "Opened successfully\n";
+      throw std::runtime_error("Failed to open /dev/video");
     }
+    std::cout << "Opened successfully\n";
   }
 
   RawVideoBuffer read_video() {
@@ -93,13 +96,13 @@
 
     std::cout << "Write done\n";
     */
-    std::cout << "Reading\n";
+    LOG(DEBUG) << "Reading\n";
     status = read(video_fd, reinterpret_cast<char *>(&image_info),
                   sizeof(image_info));
-    std::cout << "Reading\n";
+    LOG(DEBUG) << "Done reading\n";
 
     if (status != 0) {
-      std::cout << "Read failed with status " << status << "\n";
+      LOG(WARNING) << "Read failed with status " << status << "\n";
     }
 
     raw.buffer.resize(image_info.len);
@@ -118,4 +121,66 @@
   int video_fd;
   IMAGE_INFO image_info;
 };
+
+#if defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
+class AsyncVideoPuller {
+ public:
+  typedef std::function<void(RawVideoBuffer &)> video_callback;
+
+  AsyncVideoPuller(boost::asio::io_service &io_service)
+      : image_info(), dev_video(io_service, open("/dev/video", O_RDWR)) {
+    videobuf = std::make_shared<RawVideoBuffer>();
+
+    image_info.do_image_refresh = 1;  // full frame refresh
+    image_info.qc_valid = 0;          // quick cursor disabled
+    image_info.parameter.features.buf =
+        reinterpret_cast<unsigned char *>(videobuf->buffer.data());
+    image_info.crypttype = -1;
+  };
+
+  void register_callback(video_callback &callback) {
+    std::lock_guard<std::mutex> lock(callback_mutex);
+    callbacks.push_back(callback);
+    start_read();
+  }
+
+  void start_read() {
+    auto mutable_buffer = boost::asio::buffer(&image_info, sizeof(image_info));
+    boost::asio::async_read(
+        dev_video, mutable_buffer, [this](const boost::system::error_code &ec,
+                                          std::size_t bytes_transferred) {
+          if (ec) {
+            LOG(WARNING) << "Read failed with status " << ec << "\n";
+          } else {
+            this->read_done();
+          }
+        });
+  }
+
+  void read_done() {
+    LOG(DEBUG) << "Done reading\n";
+    videobuf->buffer.resize(image_info.len);
+
+    videobuf->height = image_info.parameter.features.h;
+    videobuf->width = image_info.parameter.features.w;
+    if (image_info.parameter.features.jpg_fmt == 422) {
+      videobuf->mode = YuvMode::YUV420;
+    } else {
+      videobuf->mode = YuvMode::YUV444;
+    }
+    std::lock_guard<std::mutex> lock(callback_mutex);
+    for (auto &callback : callbacks) {
+      // TODO(ed) call callbacks async and double buffer frames
+      callback(*videobuf);
+    }
+  }
+
+ private:
+  std::shared_ptr<RawVideoBuffer> videobuf;
+  boost::asio::posix::stream_descriptor dev_video;
+  IMAGE_INFO image_info;
+  std::mutex callback_mutex;
+  std::vector<video_callback> callbacks;
+};
+#endif  // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
 }
diff --git a/include/web_kvm.hpp b/include/web_kvm.hpp
index 10291ca..1671382 100644
--- a/include/web_kvm.hpp
+++ b/include/web_kvm.hpp
@@ -282,7 +282,7 @@
                     // Todo(ed) lifecycle of the video puller and decoder
                     // should be
                     // with the websocket, not recreated every time
-                    AstVideo::VideoPuller p;
+                    AstVideo::SimpleVideoPuller p;
                     p.initialize();
                     auto out = p.read_video();
                     AstVideo::AstJpegDecoder d;
diff --git a/src/ast_video_puller_test.cpp b/src/ast_video_puller_test.cpp
new file mode 100644
index 0000000..ef1cbe3
--- /dev/null
+++ b/src/ast_video_puller_test.cpp
@@ -0,0 +1,49 @@
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ast_jpeg_decoder.hpp>
+#include <ast_video_puller.hpp>
+#include <chrono>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <thread>
+#include <vector>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+TEST(AstvideoPuller, BasicRead) {
+  std::cout << "Started\n";
+  AstVideo::RawVideoBuffer out;
+  bool have_hardware = false;
+  if (access("/dev/video", F_OK) != -1) {
+    AstVideo::SimpleVideoPuller p;
+    p.initialize();
+    out = p.read_video();
+  } else {
+    FILE *fp = fopen("test_resources/ubuntu_444_800x600_0chrom_0lum.bin", "rb");
+    if (fp) {
+      size_t newLen = fread(out.buffer.data(), sizeof(char),
+                            out.buffer.size() * sizeof(long), fp);
+      if (ferror(fp) != 0) {
+        fputs("Error reading file", stderr);
+      }
+      fclose(fp);
+      out.buffer.resize(newLen);
+      out.mode = AstVideo::YuvMode::YUV444;
+      out.width = 800;
+      out.height = 600;
+      out.y_selector = 0;
+      out.uv_selector = 0;
+    }
+  }
+
+  FILE *fp = fopen("/tmp/screendata.bin", "wb");
+  fwrite(out.buffer.data(), sizeof(char), out.buffer.size(), fp);
+
+  AstVideo::AstJpegDecoder d;
+  std::cout << "MODE " << static_cast<int>(out.mode);
+  d.decode(out.buffer, out.width, out.height, out.mode, out.y_selector,
+           out.uv_selector);
+}
diff --git a/src/getvideo_main.cpp b/src/getvideo_main.cpp
index 8c92bd3..33885ee 100644
--- a/src/getvideo_main.cpp
+++ b/src/getvideo_main.cpp
@@ -24,7 +24,7 @@
   AstVideo::RawVideoBuffer out;
   bool have_hardware = false;
   if (access("/dev/video", F_OK) != -1) {
-    AstVideo::VideoPuller p;
+    AstVideo::SimpleVideoPuller p;
     p.initialize();
     out = p.read_video();
   } else {