diff --git a/CMakeLists.txt b/CMakeLists.txt
index 25d7ae3bea67912e77dabc6a91f9aae25dc05504..a637d567d4acf704c4663aa79e77314c750a64a6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2018-2019, Collabora, Ltd.
+# Copyright 2018-2020, Collabora, Ltd.
 # SPDX-License-Identifier: BSL-1.0
 
 cmake_minimum_required(VERSION 3.10.0)
@@ -71,6 +71,7 @@ cmake_dependent_option(BUILD_WITH_OPENCV "Enable OpenCV backend" ON "OpenCV_FOUN
 cmake_dependent_option(BUILD_WITH_LIBUVC "Enable libuvc video driver" ON "LIBUVC_FOUND" OFF)
 cmake_dependent_option(BUILD_WITH_FFMPEG "Enable ffmpeg testing video driver" ON "FFMPEG_FOUND" OFF)
 cmake_dependent_option(BUILD_WITH_PSVR "Enable PSVR HMD driver" ON "HIDAPI_FOUND" OFF)
+option(BUILD_WITH_DUMMY "Enable dummy driver" ON)
 cmake_dependent_option(BUILD_WITH_VIVE "Enable Vive driver" ON "ZLIB_FOUND" OFF)
 cmake_dependent_option(BUILD_WITH_OPENHMD "Enable OpenHMD driver" ON "OPENHMD_FOUND" OFF)
 cmake_dependent_option(BUILD_WITH_SDL2 "Enable SDL2 based test application" ON "SDL2_FOUND" OFF)
@@ -127,6 +128,10 @@ if(BUILD_WITH_SDL2)
 	set(BUILD_TARGET_GUI TRUE)
 endif()
 
+if(BUILD_WITH_DUMMY)
+	set(BUILD_DRIVER_DUMMY TRUE)
+endif()
+
 if(BUILD_WITH_OPENHMD)
 	add_definitions(-DXRT_HAVE_OPENHMD)
 
diff --git a/meson.build b/meson.build
index c23bca33aa491c946951c9bbfd58113976b68762..6f3e47ef63b12c7e5e9d42fa1fceec695a542e1e 100644
--- a/meson.build
+++ b/meson.build
@@ -1,4 +1,4 @@
-# Copyright 2019, Collabora, Ltd.
+# Copyright 2019-2020, Collabora, Ltd.
 # SPDX-License-Identifier: BSL-1.0
 
 project(
@@ -139,7 +139,7 @@ if 'v4l2' in drivers
 endif
 
 if 'auto' in drivers
-	drivers += ['psmv', 'hydra', 'hdk']
+	drivers += ['dummy', 'hdk', 'hydra', 'psmv']
 endif
 
 openhmd = dependency('openhmd', required: openhmd_required)
diff --git a/meson_options.txt b/meson_options.txt
index 5780a175d751f36d0b8e11eb2d1bc570c669b589..abfaaa8a449e9b24fdf474d0a63fc4b89ef5c679 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,9 +1,9 @@
-# Copyright 2019, Collabora, Ltd.
+# Copyright 2019-2020, Collabora, Ltd.
 # SPDX-License-Identifier: BSL-1.0
 
 option('drivers',
 	type: 'array',
-	choices: ['auto', 'hdk', 'hydra', 'ohmd', 'psmv', 'psvr', 'v4l2', 'vive'],
+	choices: ['auto', 'dummy', 'hdk', 'hydra', 'ohmd', 'psmv', 'psvr', 'v4l2', 'vive'],
 	value: ['auto'],
 	description: 'Set of drivers to build')
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b2c0bb0ffc38dc3232a0d2ec300152429a3b7f7e..a01ae616b05a4c867abbe7637f1fe381a7fd95bc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2019, Collabora, Ltd.
+# Copyright 2019-2020, Collabora, Ltd.
 # SPDX-License-Identifier: BSL-1.0
 
 
@@ -6,6 +6,10 @@ if(BUILD_TRACKING)
 	set(XRT_BUILD_TRACKING TRUE)
 endif()
 
+if(BUILD_DRIVER_DUMMY)
+	set(XRT_BUILD_DRIVER_DUMMY TRUE)
+endif()
+
 if(BUILD_DRIVER_HDK)
 	set(XRT_BUILD_DRIVER_HDK TRUE)
 endif()
diff --git a/src/meson.build b/src/meson.build
index f4ad397c05570c3189923244fc024b5d82f2cd69..9edb2351d1b8e029c2093eec6d381fe6da1b440a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,8 +1,9 @@
-# Copyright 2019, Collabora, Ltd.
+# Copyright 2019-2020, Collabora, Ltd.
 # SPDX-License-Identifier: BSL-1.0
 
 conf_data = configuration_data()
 conf_data.set('XRT_BUILD_TRACKING', build_tracking)
+conf_data.set('XRT_BUILD_DRIVER_DUMMY', 'dummy' in drivers)
 conf_data.set('XRT_BUILD_DRIVER_HDK', 'hdk' in drivers)
 conf_data.set('XRT_BUILD_DRIVER_HYDRA', 'hydra' in drivers)
 conf_data.set('XRT_BUILD_DRIVER_OHMD', 'ohmd' in drivers)
diff --git a/src/targets_enabled_drivers.h.cmake_in b/src/targets_enabled_drivers.h.cmake_in
index 93f90eaf3af780ca389ab5a038b691bf0171d6da..db6bafecf75ce916e51189bb0ce96ecc88df09d8 100644
--- a/src/targets_enabled_drivers.h.cmake_in
+++ b/src/targets_enabled_drivers.h.cmake_in
@@ -1,4 +1,4 @@
-// Copyright 2019, Collabora, Ltd.
+// Copyright 2019-2020, Collabora, Ltd.
 // SPDX-License-Identifier: BSL-1.0
 /*!
  * @file
@@ -9,6 +9,8 @@
 
 #cmakedefine XRT_BUILD_TRACKING
 
+#cmakedefine XRT_BUILD_DRIVER_DUMMY
+
 #cmakedefine XRT_BUILD_DRIVER_HDK
 
 #cmakedefine XRT_BUILD_DRIVER_HYDRA
diff --git a/src/targets_enabled_drivers.h.meson_in b/src/targets_enabled_drivers.h.meson_in
index f2ef7fb760cd5335682c08d96ba40e249f5e7545..6209d7a07091103c578f724527b3a78f3ddc6f7c 100644
--- a/src/targets_enabled_drivers.h.meson_in
+++ b/src/targets_enabled_drivers.h.meson_in
@@ -1,4 +1,4 @@
-// Copyright 2019, Collabora, Ltd.
+// Copyright 2019-2020, Collabora, Ltd.
 // SPDX-License-Identifier: BSL-1.0
 /*!
  * @file
@@ -9,6 +9,8 @@
 
 #mesondefine XRT_BUILD_TRACKING
 
+#mesondefine XRT_BUILD_DRIVER_DUMMY
+
 #mesondefine XRT_BUILD_DRIVER_HDK
 
 #mesondefine XRT_BUILD_DRIVER_HYDRA
diff --git a/src/xrt/drivers/CMakeLists.txt b/src/xrt/drivers/CMakeLists.txt
index e5cedbd93a298b543e331341f7be2ba184471da6..0e716da6bf0a0bd6d342a9cac39c64a67fc1514e 100644
--- a/src/xrt/drivers/CMakeLists.txt
+++ b/src/xrt/drivers/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2019, Collabora, Ltd.
+# Copyright 2019-2020, Collabora, Ltd.
 # SPDX-License-Identifier: BSL-1.0
 
 
@@ -11,6 +11,18 @@ include_directories(
 set(ENABLED_HEADSET_DRIVERS)
 set(ENABLED_DRIVERS)
 
+if(BUILD_DRIVER_DUMMY)
+	set(DUMMY_SOURCE_FILES
+		dummy/dummy_hmd.c
+		dummy/dummy_interface.h
+		dummy/dummy_prober.c
+		)
+
+	# Use OBJECT to not create a archive, since it just gets in the way.
+	add_library(drv_dummy OBJECT ${DUMMY_SOURCE_FILES})
+	list(APPEND ENABLED_HEADSET_DRIVERS dummy)
+endif()
+
 if(BUILD_DRIVER_HDK)
 	set(HDK_SOURCE_FILES
 		hdk/hdk_device.cpp
diff --git a/src/xrt/drivers/dummy/dummy_hmd.c b/src/xrt/drivers/dummy/dummy_hmd.c
new file mode 100644
index 0000000000000000000000000000000000000000..54df11da8b4271baaa1813956d43cb2db9dbd369
--- /dev/null
+++ b/src/xrt/drivers/dummy/dummy_hmd.c
@@ -0,0 +1,198 @@
+// Copyright 2020, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Dummy HMD device.
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup drv_dummy
+ */
+
+
+#include <math.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "math/m_api.h"
+#include "xrt/xrt_device.h"
+#include "util/u_var.h"
+#include "util/u_misc.h"
+#include "util/u_debug.h"
+#include "util/u_device.h"
+#include "util/u_time.h"
+#include "util/u_distortion_mesh.h"
+
+/*
+ *
+ * Structs and defines.
+ *
+ */
+
+struct dummy_hmd
+{
+	struct xrt_device base;
+
+	struct xrt_pose pose;
+
+	bool print_spew;
+	bool print_debug;
+};
+
+
+/*
+ *
+ * Functions
+ *
+ */
+
+static inline struct dummy_hmd *
+dummy_hmd(struct xrt_device *xdev)
+{
+	return (struct dummy_hmd *)xdev;
+}
+
+DEBUG_GET_ONCE_BOOL_OPTION(dummy_spew, "DUMMY_PRINT_SPEW", false)
+DEBUG_GET_ONCE_BOOL_OPTION(dummy_debug, "DUMMY_PRINT_DEBUG", false)
+
+#define DH_SPEW(dh, ...)                                                       \
+	do {                                                                   \
+		if (dh->print_spew) {                                          \
+			fprintf(stderr, "%s - ", __func__);                    \
+			fprintf(stderr, __VA_ARGS__);                          \
+			fprintf(stderr, "\n");                                 \
+		}                                                              \
+	} while (false)
+
+#define DH_DEBUG(dh, ...)                                                      \
+	do {                                                                   \
+		if (dh->print_debug) {                                         \
+			fprintf(stderr, "%s - ", __func__);                    \
+			fprintf(stderr, __VA_ARGS__);                          \
+			fprintf(stderr, "\n");                                 \
+		}                                                              \
+	} while (false)
+
+#define DH_ERROR(dh, ...)                                                      \
+	do {                                                                   \
+		fprintf(stderr, "%s - ", __func__);                            \
+		fprintf(stderr, __VA_ARGS__);                                  \
+		fprintf(stderr, "\n");                                         \
+	} while (false)
+
+static void
+dummy_hmd_destroy(struct xrt_device *xdev)
+{
+	struct dummy_hmd *dh = dummy_hmd(xdev);
+
+	// Remove the variable tracking.
+	u_var_remove_root(dh);
+
+	u_device_free(&dh->base);
+}
+
+static void
+dummy_hmd_update_inputs(struct xrt_device *xdev, struct time_state *timekeeping)
+{
+	// Empty
+}
+
+static void
+dummy_hmd_get_tracked_pose(struct xrt_device *xdev,
+                           enum xrt_input_name name,
+                           struct time_state *timekeeping,
+                           int64_t *out_timestamp,
+                           struct xrt_space_relation *out_relation)
+{
+	struct dummy_hmd *dh = dummy_hmd(xdev);
+
+	if (name != XRT_INPUT_GENERIC_HEAD_POSE) {
+		DH_ERROR(dh, "unknown input name");
+		return;
+	}
+
+	int64_t now = time_state_get_now(timekeeping);
+
+	*out_timestamp = now;
+	out_relation->pose = dh->pose;
+	out_relation->relation_flags = (enum xrt_space_relation_flags)(
+	    XRT_SPACE_RELATION_ORIENTATION_VALID_BIT |
+	    XRT_SPACE_RELATION_POSITION_VALID_BIT);
+}
+
+static void
+dummy_hmd_get_view_pose(struct xrt_device *xdev,
+                        struct xrt_vec3 *eye_relation,
+                        uint32_t view_index,
+                        struct xrt_pose *out_pose)
+{
+	struct xrt_pose pose = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}};
+	bool adjust = view_index == 0;
+
+	pose.position.x = eye_relation->x / 2.0f;
+	pose.position.y = eye_relation->y / 2.0f;
+	pose.position.z = eye_relation->z / 2.0f;
+
+	// Adjust for left/right while also making sure there aren't any -0.f.
+	if (pose.position.x > 0.0f && adjust) {
+		pose.position.x = -pose.position.x;
+	}
+	if (pose.position.y > 0.0f && adjust) {
+		pose.position.y = -pose.position.y;
+	}
+	if (pose.position.z > 0.0f && adjust) {
+		pose.position.z = -pose.position.z;
+	}
+
+	*out_pose = pose;
+}
+
+struct xrt_device *
+dummy_hmd_create(void)
+{
+	enum u_device_alloc_flags flags = (enum u_device_alloc_flags)(
+	    U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE);
+	struct dummy_hmd *dh = U_DEVICE_ALLOCATE(struct dummy_hmd, flags, 1, 0);
+	dh->base.update_inputs = dummy_hmd_update_inputs;
+	dh->base.get_tracked_pose = dummy_hmd_get_tracked_pose;
+	dh->base.get_view_pose = dummy_hmd_get_view_pose;
+	dh->base.destroy = dummy_hmd_destroy;
+	dh->base.name = XRT_DEVICE_GENERIC_HMD;
+	dh->pose.orientation.w = 1.0f; // All other values set to zero.
+	dh->print_spew = debug_get_bool_option_dummy_spew();
+	dh->print_debug = debug_get_bool_option_dummy_debug();
+
+	// Print name.
+	snprintf(dh->base.str, XRT_DEVICE_NAME_LEN, "Dummy HMD");
+
+	// Setup input.
+	dh->base.inputs[0].name = XRT_INPUT_GENERIC_HEAD_POSE;
+
+	// Setup info.
+	struct u_device_simple_info info;
+	info.display.w_pixels = 1920;
+	info.display.h_pixels = 1080;
+	info.display.w_meters = 0.13f;
+	info.display.h_meters = 0.07f;
+	info.lens_horizontal_separation_meters = 0.13f / 2.0f;
+	info.lens_vertical_position_meters = 0.07f / 2.0f;
+	info.views[0].fov = 85.0f * (M_PI / 180.0f);
+	info.views[1].fov = 85.0f * (M_PI / 180.0f);
+
+	if (!u_device_setup_split_side_by_side(&dh->base, &info)) {
+		DH_ERROR(dh, "Failed to setup basic device info");
+		dummy_hmd_destroy(&dh->base);
+		return NULL;
+	}
+
+	// Setup variable tracker.
+	u_var_add_root(dh, "Dummy HMD", true);
+
+	if (dh->base.hmd->distortion.preferred == XRT_DISTORTION_MODEL_NONE) {
+		// Setup the distortion mesh.
+		u_distortion_mesh_none(dh->base.hmd);
+	}
+
+	return &dh->base;
+}
diff --git a/src/xrt/drivers/dummy/dummy_interface.h b/src/xrt/drivers/dummy/dummy_interface.h
new file mode 100644
index 0000000000000000000000000000000000000000..46767183711357e28826746779cef480fa9745fc
--- /dev/null
+++ b/src/xrt/drivers/dummy/dummy_interface.h
@@ -0,0 +1,48 @@
+// Copyright 2020, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Interface to dummy driver.
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup drv_dummy
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * @defgroup drv_dummy Dummy driver.
+ * @ingroup drv
+ *
+ * @brief Simple do nothing dummy driver.
+ */
+
+/*!
+ * Create a auto prober for dummy devices.
+ *
+ * @ingroup drv_dummy
+ */
+struct xrt_auto_prober *
+dummy_create_auto_prober(void);
+
+/*!
+ * Create a dummy hmd.
+ *
+ * @ingroup drv_dummy
+ */
+struct xrt_device *
+dummy_hmd_create(void);
+
+/*!
+ * @dir drivers/dummy
+ *
+ * @brief @ref drv_dummy files.
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/xrt/drivers/dummy/dummy_prober.c b/src/xrt/drivers/dummy/dummy_prober.c
new file mode 100644
index 0000000000000000000000000000000000000000..3380c2d0e626b3d7a2c31f879504fda583b903ca
--- /dev/null
+++ b/src/xrt/drivers/dummy/dummy_prober.c
@@ -0,0 +1,64 @@
+// Copyright 2020, Collabora, Ltd.
+// SPDX-License-Identifier: BSL-1.0
+/*!
+ * @file
+ * @brief  Dummy prober code.
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @ingroup drv_ohmd
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "xrt/xrt_prober.h"
+
+#include "util/u_misc.h"
+#include "util/u_debug.h"
+
+#include "dummy_interface.h"
+
+
+struct dummy_prober
+{
+	struct xrt_auto_prober base;
+};
+
+static inline struct dummy_prober *
+dummy_prober(struct xrt_auto_prober *p)
+{
+	return (struct dummy_prober *)p;
+}
+
+static void
+dummy_prober_destroy(struct xrt_auto_prober *p)
+{
+	struct dummy_prober *dp = dummy_prober(p);
+
+	free(dp);
+}
+
+static struct xrt_device *
+dummy_prober_autoprobe(struct xrt_auto_prober *xap,
+                       bool no_hmds,
+                       struct xrt_prober *xp)
+{
+	struct dummy_prober *dp = dummy_prober(xap);
+	(void)dp;
+
+	// Do not create a dummy HMD if we are not looking for HMDs.
+	if (no_hmds) {
+		return NULL;
+	}
+
+	return dummy_hmd_create();
+}
+
+struct xrt_auto_prober *
+dummy_create_auto_prober()
+{
+	struct dummy_prober *dp = U_TYPED_CALLOC(struct dummy_prober);
+	dp->base.destroy = dummy_prober_destroy;
+	dp->base.lelo_dallas_autoprobe = dummy_prober_autoprobe;
+
+	return &dp->base;
+}
diff --git a/src/xrt/drivers/meson.build b/src/xrt/drivers/meson.build
index a2fcbb6612eca93931a940c4cecd7628d94c20e2..16499dc1700245c2675765f6b8f68f2c0f1d40b7 100644
--- a/src/xrt/drivers/meson.build
+++ b/src/xrt/drivers/meson.build
@@ -1,8 +1,20 @@
-# Copyright 2019, Collabora, Ltd.
+# Copyright 2019-2020, Collabora, Ltd.
 # SPDX-License-Identifier: BSL-1.0
 
 drv_include = include_directories('.')
 
+lib_drv_dummy = static_library(
+	'drv_dummy',
+	files(
+		'dummy/dummy_hmd.c',
+		'dummy/dummy_interface.h',
+		'dummy/dummy_prober.c',
+	),
+	include_directories: xrt_include,
+	dependencies: [aux],
+	build_by_default: 'dummy' in drivers,
+)
+
 lib_drv_hdk = static_library(
 	'drv_hdk',
 	files(
diff --git a/src/xrt/targets/CMakeLists.txt b/src/xrt/targets/CMakeLists.txt
index 56c123b1cfe16e9d1e254ef9e7de701cc81b79c0..86330fc97a3ee4d3aab431d437c353bd5d24644d 100644
--- a/src/xrt/targets/CMakeLists.txt
+++ b/src/xrt/targets/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2019, Collabora, Ltd.
+# Copyright 2019-2020, Collabora, Ltd.
 # SPDX-License-Identifier: BSL-1.0
 
 ######
@@ -20,6 +20,10 @@ if(BUILD_WITH_JPEG)
 	list(APPEND DRIVER_LIBRARIES ${JPEG_LIBRARIES})
 endif()
 
+if(BUILD_DRIVER_DUMMY)
+	list(APPEND DRIVER_OBJECTS $<TARGET_OBJECTS:drv_dummy>)
+endif()
+
 if(BUILD_DRIVER_HDK)
 	list(APPEND DRIVER_OBJECTS $<TARGET_OBJECTS:drv_hdk>)
 	list(APPEND DRIVER_LIBRARIES ${HIDAPI_LIBRARIES})
diff --git a/src/xrt/targets/common/target_lists.c b/src/xrt/targets/common/target_lists.c
index ce972caa1686a692af52beccf844ddd4b701135a..d5af4f6a235f13330e346d356906b11d9e82f06c 100644
--- a/src/xrt/targets/common/target_lists.c
+++ b/src/xrt/targets/common/target_lists.c
@@ -1,4 +1,4 @@
-// Copyright 2019, Collabora, Ltd.
+// Copyright 2019-2020, Collabora, Ltd.
 // SPDX-License-Identifier: BSL-1.0
 /*!
  * @file
@@ -10,6 +10,10 @@
 
 #include "target_lists.h"
 
+#ifdef XRT_BUILD_DRIVER_DUMMY
+#include "dummy/dummy_interface.h"
+#endif
+
 #ifdef XRT_BUILD_DRIVER_HDK
 #include "hdk/hdk_interface.h"
 #endif
@@ -88,6 +92,11 @@ xrt_auto_prober_creator target_auto_list[] = {
     // OpenHMD last as we want to override it with native drivers.
     oh_create_auto_prober,
 #endif
+
+#ifdef XRT_BUILD_DRIVER_DUMMY
+    // Dummy headset driver last.
+    dummy_create_auto_prober,
+#endif
     NULL, // Terminate
 };
 
diff --git a/src/xrt/targets/meson.build b/src/xrt/targets/meson.build
index ed889e98a6af5d844e0870e7438de0e6fcd8854b..c37b6b2d5f07c54b19e5247e13601f80ea9285df 100644
--- a/src/xrt/targets/meson.build
+++ b/src/xrt/targets/meson.build
@@ -1,4 +1,4 @@
-# Copyright 2019, Collabora, Ltd.
+# Copyright 2019-2020, Collabora, Ltd.
 # SPDX-License-Identifier: BSL-1.0
 
 driver_libs = []
@@ -12,6 +12,10 @@ if libjpeg.found()
 	driver_deps += [libjpeg]
 endif
 
+if 'dummy' in drivers
+	driver_libs += [lib_drv_dummy]
+endif
+
 if 'hdk' in drivers
 	driver_libs += [lib_drv_hdk]
 endif