diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..adf83287cf2e2a0c1f6248b6d9ae7750f5f0dafa
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "src/external/libsurvive"]
+	path = src/external/libsurvive
+	url = https://github.com/cnlohr/libsurvive.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7ce1d41a3b5da758808dd676f18f1d09bc9f1956..04bbddb4e4c6102b878aaf5afc0805181166b8f6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,6 +36,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
     pkg_check_modules(XCB xcb xcb-randr)
 endif()
 
+option(BUILD_WITH_LIBSURVIVE "Enable libsurvive driver" ON)
 cmake_dependent_option(BUILD_WITH_OPENHMD "Enable OpenHMD driver" ON "OPENHMD_FOUND" OFF)
 cmake_dependent_option(BUILD_WITH_WAYLAND "Enable Wayland support" ON "WAYLAND_FOUND" OFF)
 cmake_dependent_option(BUILD_WITH_XLIB "Enable xlib support" ON "X11_FOUND" OFF)
diff --git a/README.md b/README.md
index c69b62b67ae8d33c4a536e28edbf739b8aab6314..973cb6c66f5b1d16706a9b8737b88c3b0c99870e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 
-# Monado - XR Runtime (XRT)
+# Monado - XR Runtime (XRT) - experimental libsurvive branch
 
 Monado is an open source XR runtime delivering immersive experiences such as VR
 and AR on on mobile, PC/desktop, and any other device
@@ -11,6 +11,26 @@ The project currently is being developed for GNU/Linux
 and aims to support other operating systems in the near future.
 "Monado" has no specific meaning and is just a name.
 
+# About the libsurvive Branch
+
+This branch contains libsurvive as a submodule in src/xrt/external/libsurvive.
+Libsurvive is compiled as a shared library and the runtime links to libsurvive.
+A device prober using the libsurvive simple api is implemented in src/xrt/drivers/survive.
+
+When starting an OpenXR application, libsurvive will run calibration and save configuration
+and calibration data in the current working directory.
+
+Make sure the HMD can see both basestations and is not moved during calibration.
+
+To remove libsurvive's data delete the following files/directories:
+
+    rm -r config.json HMD_config.json calinfo
+
+Though fully working and usable, support for the libsurvive driver is **experimental**.
+
+For example it contains many hardcoded values from OpenHMD's configuration for the Vive 1 and
+does not behave well yet when no Vive is connected.
+
 ## Monado source tree
 
 * `src/xrt/include` - headers that define the internal interfaces of Monado.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7524309dfdc22ab55ad3fde0ecb3ae7f3286fa49..0cc2e4aec7cf6cee220d8a142372eb431924d408 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,4 +1,8 @@
 # Copyright 2019, Collabora, Ltd.
 # SPDX-License-Identifier: BSL-1.0
 
+if (BUILD_WITH_LIBSURVIVE)
+	add_subdirectory(external/libsurvive)
+endif()
+
 add_subdirectory(xrt)
diff --git a/src/external/libsurvive b/src/external/libsurvive
new file mode 160000
index 0000000000000000000000000000000000000000..f26e88d35c0e278685b99f8e81cc6c4f6cadee0f
--- /dev/null
+++ b/src/external/libsurvive
@@ -0,0 +1 @@
+Subproject commit f26e88d35c0e278685b99f8e81cc6c4f6cadee0f
diff --git a/src/xrt/drivers/CMakeLists.txt b/src/xrt/drivers/CMakeLists.txt
index f7beac62a4c2c6f52e2dd285a591182e37bb6228..cd721efb77e5e5332ddfdc135074da2981dc69a4 100644
--- a/src/xrt/drivers/CMakeLists.txt
+++ b/src/xrt/drivers/CMakeLists.txt
@@ -35,3 +35,23 @@ if(HIDAPI_FOUND)
 		PRIVATE ${HIDAPI_INCLUDE_DIRS}
 		)
 endif()
+
+if (BUILD_WITH_LIBSURVIVE)
+	set(SURVIVE_SOURCE_FILES
+		survive/survive_device.c
+		survive/survive_device.h
+		survive/survive_interface.h
+		survive/survive_prober.c
+		)
+
+	# Use OBJECT to not create a archive, since it just gets in the way.
+	add_library(drv_survive OBJECT ${SURVIVE_SOURCE_FILES})
+	set_property(TARGET drv_survive PROPERTY POSITION_INDEPENDENT_CODE ON)
+
+	target_include_directories(drv_survive SYSTEM
+		PRIVATE
+		${PROJECT_SOURCE_DIR}/src/external/libsurvive/include
+		${PROJECT_SOURCE_DIR}/src/external/libsurvive/include/libsurvive
+		${PROJECT_SOURCE_DIR}/src/external/libsurvive/redist
+		)
+endif()
diff --git a/src/xrt/drivers/survive/survive_device.c b/src/xrt/drivers/survive/survive_device.c
new file mode 100644
index 0000000000000000000000000000000000000000..a806b27b4be899fa996915ee549989c6c43ee2ad
--- /dev/null
+++ b/src/xrt/drivers/survive/survive_device.c
@@ -0,0 +1,421 @@
+// Copyright 2019, Collabora, Ltd.
+// SPDX-License-Identifier: Apache-2.0
+/*!
+ * @file
+ * @brief  Adaptor to a Libsurvive.
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ * @author Christoph Haag <christoph.haag@collabora.com>
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "math/m_api.h"
+#include "xrt/xrt_device.h"
+#include "util/u_debug.h"
+#include "util/u_device.h"
+#include "util/u_misc.h"
+
+#include "survive_device.h"
+
+static void
+survive_device_destroy(struct xrt_device *xdev)
+{
+	struct survive_device *survive = survive_device(xdev);
+
+	if (survive->ctx != NULL) {
+		survive_simple_close(survive->ctx);
+	}
+
+	free(survive);
+}
+
+static void
+survive_device_get_tracked_pose(struct xrt_device *xdev,
+                                struct xrt_space_relation *out_relation)
+{
+	struct survive_device *survive = survive_device(xdev);
+	out_relation->relation_flags = 0;
+
+	bool new_data = false;
+
+	static struct xrt_quat last_rot = {.x = 0, .y = 0, .z = 0, .w = 1};
+	static struct xrt_vec3 last_pos = {.x = 0, .y = 0, .z = 0};
+
+	for (const SurviveSimpleObject *it =
+	         survive_simple_get_next_updated(survive->ctx);
+	     it != 0; it = survive_simple_get_next_updated(survive->ctx)) {
+		const char *codename = survive_simple_object_name(it);
+
+		if (strcmp(codename, "HMD") != 0)
+			continue;
+
+		new_data = true;
+		SurvivePose pose;
+
+		uint32_t timecode =
+		    survive_simple_object_get_latest_pose(it, &pose);
+		(void)timecode;
+
+		struct xrt_quat out_rot = {.x = pose.Rot[1],
+		                           .y = pose.Rot[2],
+		                           .z = pose.Rot[3],
+		                           .w = pose.Rot[0]};
+
+		/* libsurvive looks down when it should be looking forward, so
+		 * rotate the quat.
+		 * because the HMD quat is the opposite of the in world
+		 * rotation, we rotate down. */
+		struct xrt_quat down_rot;
+		down_rot.x = sqrtf(2) / 2.;
+		down_rot.y = 0;
+		down_rot.z = 0;
+		down_rot.w = -sqrtf(2) / 2.;
+
+		math_quat_rotate(&down_rot, &out_rot, &out_rot);
+
+		out_relation->pose.orientation = out_rot;
+
+		/* because the quat is rotated, y and z axes are switched. */
+		out_relation->pose.position.x = pose.Pos[0];
+		out_relation->pose.position.y = pose.Pos[2];
+		out_relation->pose.position.z = -pose.Pos[1];
+
+		out_relation->relation_flags =
+		    (enum xrt_space_relation_flags)(
+		        XRT_SPACE_RELATION_ORIENTATION_VALID_BIT |
+		        XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT) |
+		    XRT_SPACE_RELATION_POSITION_VALID_BIT |
+		    XRT_SPACE_RELATION_POSITION_TRACKED_BIT;
+
+		SURVIVE_SPEW(
+		    survive,
+		    "GET_POSITION (%f %f %f) GET_ORIENTATION (%f, %f, %f, %f)",
+		    out_relation->pose.position.x,
+		    out_relation->pose.position.y,
+		    out_relation->pose.position.z, out_rot.x, out_rot.y,
+		    out_rot.z, out_rot.w);
+		last_rot = out_relation->pose.orientation;
+		last_pos = out_relation->pose.position;
+	}
+
+	/// @todo: Handle device supplying data too slowly better
+	if (!new_data) {
+		out_relation->pose.orientation = last_rot;
+		out_relation->pose.position = last_pos;
+		out_relation->relation_flags =
+		    (enum xrt_space_relation_flags)(
+		        XRT_SPACE_RELATION_ORIENTATION_VALID_BIT |
+		        XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT) |
+		    XRT_SPACE_RELATION_POSITION_VALID_BIT |
+		    XRT_SPACE_RELATION_POSITION_TRACKED_BIT;
+	}
+}
+
+static void
+survive_device_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 display_info
+{
+	float w_meters;
+	float h_meters;
+	int w_pixels;
+	int h_pixels;
+};
+
+struct device_info
+{
+	struct display_info display;
+
+	float lens_horizontal_separation;
+	float lens_vertical_position;
+
+	float pano_distortion_k[4];
+	float pano_aberration_k[4];
+	float pano_warp_scale;
+
+	struct
+	{
+		float fov;
+
+		struct display_info display;
+
+		float lens_center_x_meters;
+		float lens_center_y_meters;
+	} views[2];
+};
+
+static struct device_info
+get_info()
+{
+	struct device_info info = {0};
+
+	//! @todo: hardcoded values for Vive 1 from openhmd vive driver
+	info.display.w_meters = 0.122822f;
+	info.display.h_meters = 0.068234f;
+	info.lens_horizontal_separation = 0.056;
+	info.lens_vertical_position = 0.032;
+	info.views[0].fov =
+	    2 * atan2f(0.122822f / 2. - 0.056 / 2., 0.023226876441867737);
+	info.views[1].fov = info.views[0].fov;
+	info.display.w_pixels = 2160;
+	info.display.h_pixels = 1200;
+	info.pano_distortion_k[0] = 1.318397;
+	info.pano_distortion_k[1] = -1.490242;
+	info.pano_distortion_k[2] = 0.663824;
+	info.pano_distortion_k[3] = 0.508021;
+	info.pano_aberration_k[0] = 1.00010147892f;
+	info.pano_aberration_k[1] = 1.000f;
+	info.pano_aberration_k[2] = 1.00019614479f;
+
+	/*
+	 * Assumptions made here:
+	 *
+	 * - There is a single, continuous, flat display serving both eyes, with
+	 *   no dead space/gap between eyes.
+	 * - This single panel is (effectively) perpendicular to the forward
+	 *   (-Z) direction, with edges aligned with the X and Y axes.
+	 * - Lens position is symmetrical about the center ("bridge of  nose").
+	 * - Pixels are square and uniform across the entirety of the panel.
+	 *
+	 * If any of these are not true, then either the rendering will
+	 * be inaccurate, or the properties will have to be "fudged" to
+	 * make the math work.
+	 */
+
+	info.views[0].display.w_meters = info.display.w_meters / 2.0;
+	info.views[0].display.h_meters = info.display.h_meters;
+	info.views[1].display.w_meters = info.display.w_meters / 2.0;
+	info.views[1].display.h_meters = info.display.h_meters;
+
+	info.views[0].display.w_pixels = info.display.w_pixels / 2;
+	info.views[0].display.h_pixels = info.display.h_pixels;
+	info.views[1].display.w_pixels = info.display.w_pixels / 2;
+	info.views[1].display.h_pixels = info.display.h_pixels;
+
+	/*
+	 * Assuming the lenses are centered vertically on the
+	 * display. It's not universal, but 0.5 COP on Y is more
+	 * common than on X, and it looked like many of the
+	 * driver lens_vpos values were copy/pasted or marked
+	 * with FIXME. Safer to fix it to 0.5 than risk an
+	 * extreme geometry mismatch.
+	 */
+
+	const double cop_y = 0.5;
+	const double h_1 = cop_y * info.display.h_meters;
+
+	//! @todo This are probably all wrong!
+	info.views[0].lens_center_x_meters =
+	    info.views[0].display.w_meters -
+	    info.lens_horizontal_separation / 2.0;
+	info.views[0].lens_center_y_meters = h_1;
+
+	info.views[1].lens_center_x_meters =
+	    info.lens_horizontal_separation / 2.0;
+	info.views[1].lens_center_y_meters = h_1;
+
+	//! @todo This is most definitely wrong!
+	//!       3Glasses likes the oposite better.
+	info.pano_warp_scale = (info.views[0].lens_center_x_meters >
+	                        info.views[0].lens_center_x_meters)
+	                           ? info.views[0].lens_center_x_meters
+	                           : info.views[0].lens_center_x_meters;
+	return info;
+}
+
+struct survive_device *
+survive_device_create(bool print_spew, bool print_debug)
+{
+	char *survive_args[] = {
+	    "Monado-libsurvive",
+	    // Improves the situation when one basestation goes out of view
+	    //"--time-window", "1500000"
+	};
+	SurviveSimpleContext *actx = survive_simple_init(
+	    sizeof(survive_args) / sizeof(survive_args[0]), survive_args);
+
+	//! @todo: when no vive is connected, this prober will behave badly.
+	// * No calibration present: It segfaults
+	// * Calibration present. It calls exit()
+	// it should really return NULL instead.
+
+	if (!actx)
+		return NULL;
+
+	survive_simple_start_thread(actx);
+
+	struct survive_device *survive =
+	    (struct survive_device *)calloc(1, sizeof(struct survive_device));
+	survive->base.destroy = survive_device_destroy;
+	survive->base.get_tracked_pose = survive_device_get_tracked_pose;
+	survive->base.get_view_pose = survive_device_get_view_pose;
+	survive->ctx = actx;
+	survive->print_spew = print_spew;
+	survive->print_debug = print_debug;
+
+	const struct device_info info = get_info();
+
+	{
+		/* right eye */
+		if (!math_compute_fovs(info.views[1].display.w_meters,
+		                       info.views[1].lens_center_x_meters,
+		                       info.views[1].fov,
+		                       info.views[1].display.h_meters,
+		                       info.views[1].lens_center_y_meters, 0,
+		                       &survive->base.views[1].fov)) {
+			SURVIVE_ERROR(
+			    survive,
+			    "Failed to compute the partial fields of view.");
+			free(survive);
+			return NULL;
+		}
+	}
+	{
+		/* left eye - just mirroring right eye now */
+		survive->base.views[0].fov.angle_up =
+		    survive->base.views[1].fov.angle_up;
+		survive->base.views[0].fov.angle_down =
+		    survive->base.views[1].fov.angle_down;
+
+		survive->base.views[0].fov.angle_left =
+		    -survive->base.views[1].fov.angle_right;
+		survive->base.views[0].fov.angle_right =
+		    -survive->base.views[1].fov.angle_left;
+	}
+
+	// clang-format off
+	// Main display.
+	survive->base.distortion.models = XRT_DISTORTION_MODEL_PANOTOOLS;
+	survive->base.distortion.preferred = XRT_DISTORTION_MODEL_PANOTOOLS;
+	survive->base.screens[0].w_pixels = info.display.w_pixels;
+	survive->base.screens[0].h_pixels = info.display.h_pixels;
+	survive->base.distortion.pano.distortion_k[0] = info.pano_distortion_k[0];
+	survive->base.distortion.pano.distortion_k[1] = info.pano_distortion_k[1];
+	survive->base.distortion.pano.distortion_k[2] = info.pano_distortion_k[2];
+	survive->base.distortion.pano.distortion_k[3] = info.pano_distortion_k[3];
+	survive->base.distortion.pano.aberration_k[0] = info.pano_aberration_k[0];
+	survive->base.distortion.pano.aberration_k[1] = info.pano_aberration_k[1];
+	survive->base.distortion.pano.aberration_k[2] = info.pano_aberration_k[2];
+
+	// Left
+	survive->base.views[0].display.w_meters = info.views[0].display.w_meters;
+	survive->base.views[0].display.h_meters = info.views[0].display.h_meters;
+	survive->base.views[0].lens_center.x_meters = info.views[0].lens_center_x_meters;
+	survive->base.views[0].lens_center.y_meters = info.views[0].lens_center_y_meters;
+	survive->base.views[0].display.w_pixels = info.views[0].display.w_pixels;
+	survive->base.views[0].display.h_pixels = info.views[0].display.h_pixels;
+	survive->base.views[0].viewport.x_pixels = 0;
+	survive->base.views[0].viewport.y_pixels = 0;
+	survive->base.views[0].viewport.w_pixels = info.views[0].display.w_pixels;
+	survive->base.views[0].viewport.h_pixels = info.views[0].display.h_pixels;
+	survive->base.views[0].rot = u_device_rotation_ident;
+
+	// Right
+	survive->base.views[1].display.w_meters = info.views[1].display.w_meters;
+	survive->base.views[1].display.h_meters = info.views[1].display.h_meters;
+	survive->base.views[1].lens_center.x_meters = info.views[1].lens_center_x_meters;
+	survive->base.views[1].lens_center.y_meters = info.views[1].lens_center_y_meters;
+	survive->base.views[1].display.w_pixels = info.views[1].display.w_pixels;
+	survive->base.views[1].display.h_pixels = info.views[1].display.h_pixels;
+	survive->base.views[1].viewport.x_pixels = info.views[0].display.w_pixels;
+	survive->base.views[1].viewport.y_pixels = 0;
+	survive->base.views[1].viewport.w_pixels = info.views[1].display.w_pixels;
+	survive->base.views[1].viewport.h_pixels = info.views[1].display.h_pixels;
+	survive->base.views[1].rot = u_device_rotation_ident;
+	// clang-format on
+
+	// Find any needed quirks.
+	bool quirk_video_see_through = false;
+	bool quirk_video_distortion_vive = false;
+
+	quirk_video_distortion_vive = true;
+	quirk_video_see_through = false;
+
+	// Which blend modes does the device support.
+	survive->base.blend_mode |= XRT_BLEND_MODE_OPAQUE;
+	if (quirk_video_see_through) {
+		survive->base.blend_mode |= XRT_BLEND_MODE_ALPHA_BLEND;
+	}
+
+	if (quirk_video_distortion_vive) {
+		survive->base.distortion.models |= XRT_DISTORTION_MODEL_VIVE;
+		survive->base.distortion.preferred = XRT_DISTORTION_MODEL_VIVE;
+
+		// clang-format off
+		// These need to be aquired from the vive config
+		survive->base.distortion.vive.aspect_x_over_y = 0.8999999761581421f;
+		survive->base.distortion.vive.grow_for_undistort = 0.6000000238418579f;
+		survive->base.distortion.vive.undistort_r2_cutoff[0] = 1.11622154712677f;
+		survive->base.distortion.vive.undistort_r2_cutoff[1] = 1.101870775222778f;
+		survive->base.distortion.vive.center[0][0] = 0.08946027017045266f;
+		survive->base.distortion.vive.center[0][1] = -0.009002181016260827f;
+		survive->base.distortion.vive.center[1][0] = -0.08933516629552526f;
+		survive->base.distortion.vive.center[1][1] = -0.006014565287238661f;
+
+		// left
+		// green
+		survive->base.distortion.vive.coefficients[0][0][0] = -0.188236068524731f;
+		survive->base.distortion.vive.coefficients[0][0][1] = -0.221086205321053f;
+		survive->base.distortion.vive.coefficients[0][0][2] = -0.2537849057915209f;
+
+		// blue
+		survive->base.distortion.vive.coefficients[0][1][0] = -0.07316590815739493f;
+		survive->base.distortion.vive.coefficients[0][1][1] = -0.02332400789561968f;
+		survive->base.distortion.vive.coefficients[0][1][2] = 0.02469959434698275f;
+
+		// red
+		survive->base.distortion.vive.coefficients[0][2][0] = -0.02223805567703767f;
+		survive->base.distortion.vive.coefficients[0][2][1] = -0.04931309279533211f;
+		survive->base.distortion.vive.coefficients[0][2][2] = -0.07862881939243466f;
+
+		// right
+		// green
+		survive->base.distortion.vive.coefficients[1][0][0] = -0.1906209981894497f;
+		survive->base.distortion.vive.coefficients[1][0][1] = -0.2248896677207884f;
+		survive->base.distortion.vive.coefficients[1][0][2] = -0.2721364516782803f;
+
+		// blue
+		survive->base.distortion.vive.coefficients[1][1][0] = -0.07346071902951497f;
+		survive->base.distortion.vive.coefficients[1][1][1] = -0.02189527566250131f;
+		survive->base.distortion.vive.coefficients[1][1][2] = 0.0581378652359256f;
+
+		// red
+		survive->base.distortion.vive.coefficients[1][2][0] = -0.01755850332081247f;
+		survive->base.distortion.vive.coefficients[1][2][1] = -0.04517245633373419f;
+		survive->base.distortion.vive.coefficients[1][2][2] = -0.0928909347763f;
+		// clang-format on
+	}
+
+	if (survive->print_debug) {
+		u_device_dump_config(&survive->base, __func__, "libsurvive");
+	}
+	return survive;
+}
diff --git a/src/xrt/drivers/survive/survive_device.h b/src/xrt/drivers/survive/survive_device.h
new file mode 100644
index 0000000000000000000000000000000000000000..51b20de9d7c8319d64468606ec6b1d60c806be38
--- /dev/null
+++ b/src/xrt/drivers/survive/survive_device.h
@@ -0,0 +1,61 @@
+// Copyright 2019, Collabora, Ltd.
+// SPDX-License-Identifier: Apache-2.0
+/*!
+ * @file
+ * @brief  Interface to OpenHMD driver code.
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <survive_api.h>
+
+struct survive_device
+{
+	struct xrt_device base;
+	SurviveSimpleContext *ctx;
+
+	bool print_spew;
+	bool print_debug;
+};
+
+static inline struct survive_device *
+survive_device(struct xrt_device *xdev)
+{
+	return (struct survive_device *)xdev;
+}
+
+struct survive_device *
+survive_device_create(bool print_spew, bool print_debug);
+
+#define SURVIVE_SPEW(c, ...)                                                   \
+	do {                                                                   \
+		if (c->print_spew) {                                           \
+			fprintf(stderr, "%s - ", __func__);                    \
+			fprintf(stderr, __VA_ARGS__);                          \
+			fprintf(stderr, "\n");                                 \
+		}                                                              \
+	} while (false)
+#define SURVIVE_DEBUG(c, ...)                                                  \
+	do {                                                                   \
+		if (c->print_debug) {                                          \
+			fprintf(stderr, "%s - ", __func__);                    \
+			fprintf(stderr, __VA_ARGS__);                          \
+			fprintf(stderr, "\n");                                 \
+		}                                                              \
+	} while (false)
+
+#define SURVIVE_ERROR(c, ...)                                                  \
+	do {                                                                   \
+		fprintf(stderr, "%s - ", __func__);                            \
+		fprintf(stderr, __VA_ARGS__);                                  \
+		fprintf(stderr, "\n");                                         \
+	} while (false)
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/xrt/drivers/survive/survive_interface.h b/src/xrt/drivers/survive/survive_interface.h
new file mode 100644
index 0000000000000000000000000000000000000000..0656b6ae6cc3b90d00f1aef7dae6afc51cca13c5
--- /dev/null
+++ b/src/xrt/drivers/survive/survive_interface.h
@@ -0,0 +1,22 @@
+// Copyright 2019, Collabora, Ltd.
+// SPDX-License-Identifier: Apache-2.0
+/*!
+ * @file
+ * @brief  Interface to OpenHMD driver code.
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct xrt_prober*
+survive_create_prober();
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/xrt/drivers/survive/survive_prober.c b/src/xrt/drivers/survive/survive_prober.c
new file mode 100644
index 0000000000000000000000000000000000000000..a559b08c1ef84b7f263116c0d5f93102e1b9a0c5
--- /dev/null
+++ b/src/xrt/drivers/survive/survive_prober.c
@@ -0,0 +1,80 @@
+// Copyright 2019, Collabora, Ltd.
+// SPDX-License-Identifier: Apache-2.0
+/*!
+ * @file
+ * @brief  OpenHMD prober code.
+ * @author Jakob Bornecrantz <jakob@collabora.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <survive_api.h>
+#include "xrt/xrt_prober.h"
+
+#include "util/u_misc.h"
+#include "util/u_debug.h"
+
+#include "survive_device.h"
+
+DEBUG_GET_ONCE_BOOL_OPTION(survive_spew, "SURVIVE_PRINT_SPEW", false)
+DEBUG_GET_ONCE_BOOL_OPTION(survive_debug, "SURVIVE_PRINT_DEBUG", false)
+
+struct survive_prober
+{
+	struct xrt_prober base;
+	SurviveSimpleContext *ctx;
+};
+
+static inline struct survive_prober *
+survive_prober(struct xrt_prober *p)
+{
+	return (struct survive_prober *)p;
+}
+
+static void
+survive_prober_destroy(struct xrt_prober *p)
+{
+	struct survive_prober *sp = survive_prober(p);
+
+	if (sp->ctx != NULL) {
+		/// @todo: This segfaults
+		// survive_simple_close(ohp->ctx);
+		sp->ctx = NULL;
+	}
+
+	free(sp);
+}
+
+static struct xrt_device *
+survive_prober_autoprobe(struct xrt_prober *p)
+{
+	struct survive_prober *sp = survive_prober(p);
+
+
+	bool print_spew = debug_get_bool_option_survive_spew();
+	bool print_debug = debug_get_bool_option_survive_debug();
+
+	/// @todo: with libsurvive, whether a context can be created tells us if
+	/// a supported device is connected and readable.
+
+	struct survive_device *sd =
+	    survive_device_create(print_spew, print_debug);
+
+	if (sd == NULL || sd->ctx == NULL)
+		return NULL;
+
+	sp->ctx = sd->ctx;
+
+	return &sd->base;
+}
+
+struct xrt_prober *
+survive_create_prober()
+{
+	struct survive_prober *sp = calloc(1, sizeof(struct survive_prober));
+	sp->base.destroy = survive_prober_destroy;
+	sp->base.lelo_dallas_autoprobe = survive_prober_autoprobe;
+
+	return &sp->base;
+}
diff --git a/src/xrt/targets/openxr/CMakeLists.txt b/src/xrt/targets/openxr/CMakeLists.txt
index d6b04603433cb733e85afb2a95cde77600ff69a5..dd02e38dc5cfaf375684a35a25b58f58e7a67a91 100644
--- a/src/xrt/targets/openxr/CMakeLists.txt
+++ b/src/xrt/targets/openxr/CMakeLists.txt
@@ -72,9 +72,14 @@ add_library(${RUNTIME_TARGET} SHARED
 	$<TARGET_OBJECTS:st_oxr>
 	)
 
+if (TARGET drv_survive)
+	SET(LIBSURVIVE_LIBRARIES survive)
+endif()
+
 target_link_libraries(${RUNTIME_TARGET}
 	${OPENHMD_LIBRARIES}
 	${Vulkan_LIBRARIES}
+	${LIBSURVIVE_LIBRARIES}
 	${XCB_LIBRARIES}
 	)
 
@@ -86,6 +91,11 @@ if(TARGET drv_hdk)
 	target_compile_definitions(${RUNTIME_TARGET} PRIVATE XRT_HAVE_HDK)
 endif()
 
+if (TARGET drv_survive)
+	target_sources(${RUNTIME_TARGET} PRIVATE $<TARGET_OBJECTS:drv_survive>)
+	target_compile_definitions(${RUNTIME_TARGET} PRIVATE XRT_HAVE_SURVIVE)
+endif()
+
 if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
 	include(GNUInstallDirs)
 	install(TARGETS ${RUNTIME_TARGET}
diff --git a/src/xrt/targets/openxr/target.c b/src/xrt/targets/openxr/target.c
index 9bdd1613c803e6f093e2740b2ceab1f2ae559d53..765517870ca4f92ac688a0d97772a886a47d6d7e 100644
--- a/src/xrt/targets/openxr/target.c
+++ b/src/xrt/targets/openxr/target.c
@@ -18,6 +18,10 @@
 #include "hdk/hdk_interface.h"
 #endif
 
+#ifdef XRT_HAVE_SURVIVE
+#include "survive/survive_interface.h"
+#endif
+
 
 typedef struct xrt_prober *(*prober_creator)();
 
@@ -28,6 +32,10 @@ static const prober_creator DRIVERS[] = {
     hdk_create_prober,
 #endif
 
+#ifdef XRT_HAVE_SURVIVE
+    survive_create_prober,
+#endif
+
 #ifdef XRT_HAVE_OHMD
     oh_create_prober,
 #endif