Developing Your First Sensor Driver – Part 1
The Windows Driver Kit (WDK) is a great place to get started when writing your first sensor driver. In this post we will take a look at the Sensor Driver Skeleton Sample, which provides a basic sensor driver template. A future post will investigate the Time Sensor Sample to learn about incorporating events. If you have the WDK installed, the source is located under <WDK Install Path>\src\SensorsAndLocation. For more in-depth information, the WDK Sensor Devices section of MSDN includes a design guide, valuable sensor references, and information about the two sensor driver samples.
Three of the four classes in the skeleton sample (CMyDriver, CMyDevice, and CMyQueue) implement various User-Mode Driver Framework (UMDF) interfaces. In these we initialize the device, configure the IO queue, and create the class extension and device driver interface (DDI). Head over to MSDN for more information about UMDF.
The remainder of this post will focus on the fourth class, CSensorDdi, which implements the ISensorDriver interface. This class emulates access to hardware devices and handles communication with the sensor class extension. Your first step – determine the properties and data fields your sensor supports. Properties describe a sensor device, while data fields describe sensor-generated data. Take a look at the code snippets below from SensorDDI.cpp.
// Sensor properties, lines 26-51
const PROPERTYKEY g_SupportedCommonProperties[] =
{
WPD_OBJECT_ID,
WPD_OBJECT_PERSISTENT_UNIQUE_ID,
WPD_OBJECT_PARENT_ID,
WPD_OBJECT_NAME,
WPD_OBJECT_FORMAT,
WPD_OBJECT_CONTENT_TYPE,
WPD_OBJECT_CAN_DELETE,
};
const PROPERTYKEY g_SupportedTemperatureProperties[] =
{
SENSOR_PROPERTY_TYPE,
SENSOR_PROPERTY_STATE,
SENSOR_PROPERTY_MIN_REPORT_INTERVAL,
SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL,
SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
SENSOR_PROPERTY_MANUFACTURER,
SENSOR_PROPERTY_MODEL,
SENSOR_PROPERTY_SERIAL_NUMBER,
SENSOR_PROPERTY_FRIENDLY_NAME,
SENSOR_PROPERTY_DESCRIPTION,
SENSOR_PROPERTY_CONNECTION_TYPE,
WPD_FUNCTIONAL_OBJECT_CATEGORY,
};
// Sensor data fields, lines 69-73
const PROPERTYKEY g_SupportedTemperatureDataFields[] =
{
SENSOR_DATA_TYPE_TIMESTAMP,
SENSOR_DATA_TYPE_TEMPERATURE_CELSIUS,
};
These properties and data fields describe the mock temperature sensor. The g_SupportedCommonProperties array holds properties shared by all sensors for this driver, while the other two are temperature-sensor specific. Note that these strings and associated GUIDs can be user-defined, but they must be shared and understood between the driver and client applications to be useful. The sensor platform defines a set of constants for sensor drivers, which can also be found in <WDK Install Path>\inc\api\Sensors.h.
We initialize each sensor by storing its properties and data fields into collections for future reference. The following code from InitializeTemperatureSensor demonstrates adding our temperature properties to the m_pSupportedTemperatureSensorProperties collection and setting some default values. A similar collection is maintained for the data field values.
// Add temperature sensor property keys, lines 161-165
for (DWORD dwIndex=0;
dwIndex<ARRAYSIZE(g_SupportedTemperatureProperties);
dwIndex++)
{
m_pSupportedTemperatureSensorProperties->Add(
g_SupportedTemperatureProperties[dwIndex]);
}
// Add temperature sensor property values, lines 184-186
m_pTemperatureSensorPropertyValues->SetGuidValue(
WPD_FUNCTIONAL_OBJECT_CATEGORY, SENSOR_CATEGORY_ENVIRONMENTAL);
m_pTemperatureSensorPropertyValues->SetGuidValue(
SENSOR_PROPERTY_TYPE, SENSOR_TYPE_ENVIRONMENTAL_TEMPERATURE);
m_pTemperatureSensorPropertyValues->SetStringValue(
SENSOR_PROPERTY_MANUFACTURER, DEVICE_MANUFACTURER_VALUE);
...
Once the CSensorDdi initialization is finished, the sensor class extension loads and calls OnGetSupportedSensorObjects. Here we report each sensor to the class extension and provide its supported properties. The final step is to enable the driver in the control panel so that it can be accessed by client applications. A quick note – although we are describing each sensor separately, permissions are granted at the driver level and cannot be set for specific sensors.
With our driver initialized and sensor values defined, let’s take a look at some key callback methods of the ISensorDriver interface.
-
OnClientConnect, OnClientDisconnect, OnClientSubscribeToEvents, OnClientUnsubscribeFromEvents
These methods inform the driver of client application activity. The driver can use this information to optimize reporting of state change and data values.
-
OnGetSupportedProperties, OnGetSupportedDataFields, OnGetSupportedEvents
These methods provide the sensor class extension with access to a sensor’s supported properties, data fields, and events. Remember those collections we populated earlier? This is how each sensor exposes its functionality. Recall we will talk about events in a future blog post.
-
OnGetProperties, OnSetProperties, OnGetDataFields
These methods expose the property and data field values that client applications will likely care about. OnSetProperties allows an application to update settable properties, such as a sensor’s report interval.
OnGetDataFields returns a sensor’s data field values. These could be from a cached copy or requested from the device on demand. In our sample the default values stored in the m_pTemperatureSensorDataFieldValues collection are returned for each supported data field.
That’s it! While this skeleton sample offers minimal functionality, hopefully you now have a better understanding of the driver architecture and its interaction with the sensor class extension. Starting with some simple modifications to the properties and data fields, your driver will be up and running in no time. One more thing – the Sensor Diagnostic Tool offers a great mechanism for interacting with your driver during development.
Good luck and stay tuned for the follow-up post.
-- Sensor & Location Platform Team
This posting is provided "AS IS" with no warranties and confers no rights.