monado/doc/writing-a-new-driver.md

4.8 KiB

Writing a new driver

Map

The components you will be interacting with is @ref st_prober to find the hardware devices and setup a working system, along with the @ref aux code that provides various helpers. You will actually be implementing the @ref xrt_device interface by writing a driver. It is convention in Monado for interfaces to allow full, complete control of anything a device might want to modify/control, and to provide helper functionality in @ref aux to simplify implementation of the most common cases.

Getting started

The easiest way to begin writing a driver is to start from a working example. The @ref drv_sample driver is provided explicitly for this purpose: it creates an HMD device, with a custom @ref xrt_auto_prober implementation for hardware discovery, and some simple display parameters that should be easy to modify.

Copy that directory and rename the files in it. Then, use the following sed command to perform some bulk renames before you begin actually writing code. The command as written assumes your new device type is called my_device or md for short, and your auto-prober is called my_device_auto_prober or mdap for short: change the replacement side of each pattern to match the real names you are using.

# First pattern is for renaming device types,
# second is for renaming device variables,
# third is for renaming device macros.
# Fourth and fifth are for renaming auto prober types and variables, respectively.
# The last two are for renaming the environment variable and function name
# for the environment variable logging config.
sed -r -e 's/sample_hmd/my_device/g' \
  -e 's/\bsh\b/md/g' \
  -e 's/sample_auto_prober/my_device_auto_prober/g' \
  -e 's/\bsap\b/mdap/g' \
  -e 's/\bSH_/MD_/g' \
  -e 's/sample/my_device/g' \
  -e 's/SAMPLE/MY_DEVICE/g' \
  -i *.c *.h

You will want to go through each function of the sample code you started from, implement any missing functionality, and adapt any existing functionality to match your device. Refer to other @ref drv for additional guidance. Most drivers are fairly simple, as large or complex functionality in drivers is often factored out into separate auxiliary libraries.

What to Implement

You will definitely make at least one implementation of @ref xrt_device. If your driver can talk to e.g. both a headset and corresponding controllers, you can choose to expose all those through a single xrt_device implementation, or through multiple implementations that may share some underlying component (by convention called ..._system). Both are valid choices, and the right one to choose depends on which maps better to your underlying device or API you are connecting to. It is more common to have one xrt_device per piece of hardware, however. @ref hydra_device serves as a nice example of two controllers that are enumerated as a single overall USB HID device but expose two separate xrt_device instances.

Depending on whether your device can be created from a detected USB HID device, you will also need to implement either @ref xrt_auto_prober or a function matching @ref xrt_prober_found_function_t which is the function pointer type of @ref xrt_prober_entry::found. See below for more details.

Probing

When should I implement the @ref xrt_auto_prober interface? The answer is not too hard: you use the auto prober interface when the basic USB VID/PID-based interface is not sufficient for you to detect presence/absence of your device, or if you don't want to use the built-in HID support for some reason.

If you can detect based on VID/PID, you will instead implement If you can use built-in HID, you might consider looking at @ref hdk_found, which is a nice example of how to implement @ref xrt_prober_found_function_t to perform detection of an HMD based on the USB HID for its IMU.

Either way, your device's detection details will need to be added to a list used by the prober at @ref xrt_instance startup time. The stock lists for mainline Monado are in src/xrt/targets/common/target_lists.c. These are shared by the various targets (OpenXR runtime shared library, service executable, utility executables) also found in src/xrt/targets. If you're using Monado as a toolkit or component rather than as a standalone runtime and service, you can replicate whatever portions of the target lists in your own target, or just directly implement the @ref xrt_instance interface more directly, linking in only those drivers and components you need. Note, however, that Monado is intended to not expose any external API other than the OpenXR API: the @ref xrt_iface are subject to change as required so those writing drivers or other software on those interfaces are encouraged to upstream as much as possible to minimize maintenance burden.