IIO Triggered Buffer Support

Triggered Buffer Support in 10 Easy Steps!

Here's how I added Triggered Buffer support to the HDC100x humidity and temperature sensor driver.  I'm using the output of git format-patch to illustrate the process. 

Begin with these references to get familiar with triggered buffers:
And, or course, always use the header files for the most up-to-date info:

1.  It's Simple Addition

First off, I want to point out one of the nice things about adding triggered buffer support is that it really is an 'addition'.   As you can see, it's almost all added code.  Why is this good?  Well, you're not likely to break existing code and it's really clean to review. Someone did a nice job of designing how this lays nicely on top of an existing driver!

2.  Select Kconfig Options

Do the Kconfig selects.  Remember to do them and remember to submit them.  Sounds obvious, but when you locally have configs turned on for multiple drivers, it's quite easy to overlook. The updated driver may compile because some other driver has done these selections.  Test it by removing all other IIO selections, then select your MODULE, and verify it builds independently.

3.  Include Header Files

Header files to include and header files to read!
These 3 are a must. (You may need more based on your handler implementation.)

Now we'll get to some definitions/declarations...

4.  Defines for your device

For the HDC100x I wanted to use the dual acquisition mode that allows reading of humidity and temperature in one shot.  When this mode is set, you start the measurement by writing a byte to
the temp register, wait for conversion time, and then read the temperature data followed by the humidity data.

This differs from how the raw reads were operating so we will need to be diligent in the setting and resetting of that bit.  Raw reads do the write byte to either the temp or humidity register, wait for conversion time to pass, and then read the measurement from that register.

Since I've started explaining the acquisition mode bit, I'll move right onto the setup operations where we manage that bit.

5.  Define buffer pre/post-enable/disable operations

You'll see in the documentation that you have the option of defining operations, specific to your driver, that you want to occur pre- and post- buffer enables and buffer disables.

If you have nothing special to do, you'll just use the defaults.  Otherwise you'll define them as I've done below.

The enabling/disabling of the buffers is a sequence of events.   You need to think through the steps your driver needs to take in order to transition from DIRECT mode to BUFFERED mode, and back to DIRECT mode.

My sticky bit was that Acquisition mode bit.  It has to be <ON> in BUFFERED mode and <OFF> in DIRECT mode.  The *buffer_postenable and *buffer_predisable functions allow us to sneak into that space between toggling BUFFERED mode and attaching/detaching the triggered buffer poll function.

I think the comment below is the only comment I felt the need to add to the code.  Now it seems obvious, but since it gave me pause, I'll leave it in.

6.  Protect DIRECT and BUFFERED modes

The following is another way we need to protect modes.  Above you saw how we were careful to set the ACQ bit when BUFFERED mode was on.  Now, we need to make sure that while BUFFERED mode is on, no raw reads of temperature or humidity channels are attempted.  We do that below using the claim/release helper functions to be sure that the device stays in DIRECT mode during the raw reads.  (meaning it is not in buffered mode)

You can see here that I did not wrap the heater_status update in claim/release.   The heater element actually only works when reads are ongoing, so we want to allow it to be turned on/off during buffered mode.  It entails a write to the config register which does not interfere with the measurements.

Furthermore, you're not seeing any blocking of integration time changes.  We allow that in buffered mode.  Theoretically and practically, there will be some lag time before the change takes affect in the buffer stream, but requiring userspace to stop/restart buffered reading around that event is not required by the sensor, and may not be practical to the user.   This is the type of decision that will be made on a per device and per operation basis.

7.  Define channel buffers

Now lets set up our channels.  Here is where we expose to userspace, via sysfs, the index of each channel in the buffer array and the format of the data in the buffer itself. (the 'scan')

So, here I'm setting my temp and humid channels to have scan_indices of 0 and 1.  I set the
heater element index to -1 to indicate the it is not part of the buffer scan.  Then, I add a channel for the timestamp and set it's scan_index to 2.

Temperature is a signed value and humidity is unsigned...just as you would logically expect.
I define this data as Big Endian (from sensor datasheet) and I do not have to do any special manipulating.  And, in this case, I have 16 bits of data stored in 16 bits.

These fields are all explained further in the iio buffer document referenced earlier.

8.  Define available scan masks

This little define is the secret sauce in this recipe.

The scan_index tells the position of data in the scan.  If the data in the scan is sparse, for example the user only asks for 1 of the 2 elements, and that is the element at position 2 in the scan, they expect to find it at position 1 when they get the scan.  So, although, the user knows the scan_index, it really only gives the order of items.  To illustrate further, if there were 5 possible items, and user only wants 2 and 4.  They ask for 2 and 4.  But, they don't expect you to send out a huge buffer and they pick out 2 and 4.  They want you to put 2 followed by 4 in that buffer.  You do that by defining available_scan_masks and letting the IIO core do it's magic.  (It's assigned in probe.)

9.  Update probe and remove functions

Previously, this probe ended with a resource managed devm_iio_device_register.  Now we'll revert back to iio_device_register because we will need the ability to explicity unregister the device before we do the trigger buffer cleanup in our remove function.

In the case of HDC100X I added another config parameter to that set of "be sure we are in a known state".  We need to make sure that acquisition mode is off.

Finally, the call to iio_triggered_buffer_setup()
The cleanup.  You need to clean it up if you fail to register.  We'll use cleanup again in remove.

Recall that we removed the resource managed device register.  We did that so we could control the order of cleanup.    Since we now have a remove function, we define it in our i2c_struct.

10.  Triggered Buffer Handler

Ta-da! Finally the handler.  This is that 'bottom half' poll function.  It's kind of anti-climactic once all the setup is done.  Read the data, push it to the buffer, and notify when done.  You'll see the buffer is padded.  I chose to grab the timestamp now, as explained during the probe setup.  In the case of the HDC100x, I do the dual read of both temp and humidity, and let the available_scan_mask setting do the de-muxing in the iio core.

Labels: ,