Before you start#
Review interrupts, locking, and device drivers. Source is upstream Linux v6.12. The examples use synthetic measurements and do not read or change a real sensor, clock or watchdog. Support depends on the driver, hardware, firmware and kernel configuration.
Learning goals#
Distinguish direct and buffered IIO access, explain the claim/release locking invariant, interpret raw samples using scale, and separate monitoring from watchdog recovery.
Source walkthrough#
- IIO
- Channel samples and triggered scans; decode format and scale
- hwmon
- Health channels and potentially cached sysfs readings
- RTC
- Calendar and alarm operations under device-specific rules
- Watchdog
- Timed recovery; nowayout and close semantics affect stop
The Industrial I/O (IIO) subsystem supports measurement and control devices such as accelerometers and analog-to-digital converters. An IIO channel describes a measured or controlled quantity and associated attributes. A direct read obtains a value via the driver's direct interface; a buffered stream collects configured scans, potentially driven by a trigger. A scan can contain multiple channels and possibly a timestamp. The scan format, endianness, storage width, shift and sign information determine how to interpret bytes; treating all channels as native 16-bit signed integers is unsafe.
In industrialio-core.c, the following exact v6.12 GPL-2.0-only excerpt is from iio_device_claim_direct_mode; preceding declaration and following return are omitted:
mutex_lock(&iio_dev_opaque->mlock);
if (iio_buffer_enabled(indio_dev)) {
mutex_unlock(&iio_dev_opaque->mlock);
return -EBUSY;
}
return 0;
First the function acquires the device's mutex. If the buffer is enabled, it unlocks and returns -EBUSY: the caller did not obtain the claim and must not release that failed claim. Otherwise it returns zero with the mutex still held. The successful caller must eventually call iio_device_release_direct_mode, whose implementation unlocks the same mutex. This prevents the protected mode from changing during the claimed direct operation; it does not guarantee sensor accuracy or assert that every driver operation needs this exact helper. Because it uses a mutex, callers must respect the relevant sleepable-context constraints.
A measurement and an exclusion scenario#
Assume an illustrative ADC reports raw value 1,250 and its documented scale is 0.8 millivolts per count, with zero offset. The converted voltage is 1,000 mV, or 1.0 V. If the scale or offset differs, so does the conversion; real IIO attributes define the units and processing, and not all devices share the same formula. The timestamp also needs a defined clock domain and acquisition point. A timestamp records when a specified event was observed; it is not automatically the exact instant every analog channel was sampled.
Now suppose a buffered acquisition is active when a competing direct operation claims direct mode. The check sees the enabled buffer while holding mlock, releases the mutex and returns -EBUSY. No direct claim exists. If the buffer is disabled and the claim succeeds, the caller performs its direct work and releases the mutex. Skipping the release would block later operations that need it; releasing after a failed claim would violate ownership. If the actual driver supports concurrent mechanisms differently, examine that driver's contract and source rather than extrapolating from this helper to the whole device.
Four different responsibilities#
hwmon commonly exposes sensor readings, thresholds and fan controls for hardware monitoring; IIO emphasizes channels, scan formats, triggers and buffered measurement. The same physical category does not guarantee identical units or update rates. An RTC is a real-time clock device that may retain calendar time across power states when supported; the system clock and an RTC are not interchangeable clock objects. A watchdog is a recovery mechanism: if the configured watchdog is not serviced within its timeout, the hardware or software may reset the system. It is not merely a temperature sensor, and testing it on a host could interrupt work.
The watchdog core coordinates registered devices, while individual drivers and hardware define behavior. Features such as nowayout, pretimeout and suspend interaction are configuration and device dependent. Do not trigger or stop a watchdog for this chapter; a watchdog or hardware hotplug experiment belongs only in a disposable guest or dedicated recoverable lab with explicit timeout and recovery planning. The watchdog API documentation describes a historically driver-dependent interface, so verify details against the specific driver's current contract.
IIO channels, formats, and sampling context#
An iio_dev is the core's representation of an IIO device. The driver's iio_chan_spec array describes channels and their type, modifiers, attributes and scan metadata. A channel is a defined measurement or output, not necessarily a separate physical chip. scan_index establishes its position among enabled scan elements; scan type describes properties such as signedness, real bits, storage bits, shift and byte order. A driver supplies callbacks through iio_info for operations it supports. A raw attribute is an integer representation, while processed attributes and scale or offset expose additional interpretation where provided. Availability and units depend on the channel's ABI: do not reuse an accelerometer conversion for voltage or assume all drivers implement a processed attribute.
In include/linux/iio/iio.h, v6.12, GPL-2.0-only (the documentation comment above the struct omitted):
struct iio_scan_type {
char sign;
u8 realbits;
u8 storagebits;
u8 shift;
u8 repeat;
enum iio_endian endianness;
};
Sign, real bits, storage bits, shift, repeat and endianness are separate facts. Storage width alone does not determine how to decode a sample, so the buffered bytes need the full scan type from the driver's channel declaration.
A scan mask selects enabled channels in a buffered scan. The enabled elements' type and index determine how userspace decodes the byte stream. Storage width, alignment and optional timestamp must be accounted for using the ABI; the byte length is not always the sum of real bit widths. A trigger represents a sampling event source; an IIO poll function can run in a trigger-driven acquisition flow and arrange for a driver's handler to read or collect data and push it into the buffer. Some triggers originate in hardware interrupts and some are software driven. Driver code must use the appropriate context and signal trigger completion through its API; an interrupt timestamp may record trigger time rather than the conversion instant of every channel.
In drivers/iio/industrialio-buffer.c, v6.12, GPL-2.0-only (surrounding functions omitted):
int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
int ret;
struct iio_buffer *buf;
list_for_each_entry(buf, &iio_dev_opaque->buffer_list, buffer_list) {
ret = iio_push_to_buffer(buf, data);
if (ret < 0)
return ret;
}
return 0;
}
The loop returns the first negative result from iio_push_to_buffer, so buffers after a failing one may not receive that scan. The function shows the fan-out to registered buffers, but not how a trigger is armed or whether a consumer missed data.
- Scan mask
Enabled channels define the scan layout and the decode rules
- Trigger event
A hardware or software source signals that a sample should be taken
- Driver handler
Reads channel data in the context the driver requires
- iio_push_to_buffers
Pushes the scan to each registered buffer; the first error stops the loop
- Userspace decode
Reads bytes using scan index and scan type, not a native int16 assumption
Work this fully specified synthetic scan: one enabled unsigned 12-bit voltage channel occupies a 16-bit little-endian storage unit shifted left four bits. The bytes 00 3a decode to storage value 0x3a00; shifting right four and masking 12 bits yields raw 0x3a0, or 928. Suppose this channel's documented offset is -128 counts and scale is 0.5 mV per count. For this specified ABI formula, (raw + offset) * scale is (928 - 128) * 0.5 = 400 mV, or 0.4 V. Those values are invented for calculation, not a claim about a particular sensor. With a signed scan type, sign extension after the shift changes how high-bit values are interpreted; with big-endian storage, the same two bytes decode differently. If another enabled channel or timestamp is present, use its scan index and layout rules rather than appending it by assumption.
Synthetic teaching data, not a device capture.
Select a field to connect its position, bytes, and meaning.
2 bytes in file order. Offsets start at zero.
Selected: Low byte. Least significant byte of the little-endian storage unit.
Read the byte table
| Offset | Field | Hex bytes | Interpretation |
|---|---|---|---|
| 0 | Low byte | 00 | Least significant byte of the little-endian storage unit |
| 1 | High byte | 3a | Storage 0x3a00; shift right 4 and mask 12 bits gives raw 928 |
A trigger can arrive while a consumer is slow. Buffer capacity and consumption rate affect whether samples are lost; a valid timestamp alone cannot prove no samples were missed. Similarly, a raw reading is not a calibrated physical measurement without the relevant scale, offset, units and device behavior. Inspect the attributes and driver before deciding whether an offset is applied in raw counts or in a different stage. The IIO core, channel declaration, and buffer implementation are version-pinned starting points.
hwmon: monitoring channels and cached values#
hwmon is designed around hardware monitoring attributes such as temperatures, fan speeds, voltages and alarms. The driver registers a hwmon device and exposes the attributes supported by its hardware. Names such as temp1_input, fan1_input and in0_input encode sensor class and channel; labels, if supplied, help identify the board signal. Standard hwmon conventions commonly express temperatures in millidegrees Celsius, fan speed in revolutions per minute, and voltages in millivolts, but check the specific documented attribute rather than assuming a unit based only on a filename. Values can be absent, unsupported, unreadable, or outside a plausible range due to wiring and driver behavior.
A sysfs read calls a kernel attribute method; it is not necessarily a fresh physical conversion. A driver may cache hardware registers for an update interval, especially when access to the underlying bus is slow. Reading the same value twice quickly therefore does not prove temperature was constant over that interval. Concurrent readers and a cache refresh require the driver's own locking; inspect its implementation before claiming a particular cache duration. Threshold attributes can affect alarms or policy if written, so this lesson reads only existing input and label attributes and does not change thresholds or fan controls. See the hwmon sysfs interface.
RTC calendar time and watchdog recovery#
An RTC class device supplies calendar-oriented operations through rtc_class_ops, including supported read-time and alarm operations. A calendar rtc_time contains fields such as year, month, day, hour and minute; validation matters because hardware may return an invalid calendar. In drivers/rtc/interface.c, rtc_read_time takes ops_lock interruptibly, invokes __rtc_read_time, unlocks and returns its result. The inner function handles absent operations, calls the driver's read callback, applies any configured offset and validates the calendar. An interrupted mutex acquisition returns an error; a missing callback or invalid returned time is not a valid measurement. RTC calendar time can be adjusted and is different from a monotonic interval clock. Alarm support and wake-from-suspend behavior depend on device and platform; an RTC node alone does not prove wake capability.
A watchdog is registered with core structures including watchdog_device, information and driver operations. Opening a watchdog character device can start or otherwise affect the watchdog. A running watchdog needs timely keepalive pings or, subject to hardware and configuration, it can reset the system. The timeout is the expected servicing window, not proof that recovery will succeed. Some devices expose a pretimeout notification or boot status indicating a previous reset cause; bootstatus is evidence reported by that driver/hardware, not a universal crash diagnosis.
In watchdog_dev.c, watchdog_ping requires the core-data lock and returns early if hardware is not running. Otherwise it records a keepalive status and time, then calls the underlying ping helper. On close, watchdog_release evaluates whether stopping is allowed. This exact v6.12 excerpt is GPL-2.0-or-later (GPL-2.0+ in the file), copyright Alan Cox and contributors; preceding and following lines are omitted:
else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) ||
!(wdd->info->options & WDIOF_MAGICCLOSE))
err = watchdog_stop(wdd);
The first term consumes the release permission set by the magic-close mechanism; the second permits a stop attempt when the device does not require magic close. The call attempts a stop; it does not prove the hardware stopped. In particular, nowayout can make stopping fail. If the stop result is negative, the following code logs a critical message and sends a keepalive; it also updates worker state. Thus closing the file without the required magic condition, or with nowayout enabled, can leave a watchdog running. Never open, ping, write to, or close /dev/watchdog* as an experiment on a shared host.
Read-only Linux observation lab#
On an authorized host or disposable guest, inspect only sysfs class attributes. No command below opens a watchdog character device, enables a buffer, changes a clock, or writes a control:
uname -r
for d in /sys/bus/iio/devices/iio:device*; do [ -d "$d" ] && { printf '%s ' "$d"; cat "$d/name" 2>/dev/null; }; done
for d in /sys/class/hwmon/hwmon*; do [ -d "$d" ] && { printf '%s ' "$d"; cat "$d/name" 2>/dev/null; }; done
for d in /sys/class/rtc/rtc*; do [ -d "$d" ] && { printf '%s ' "$d"; cat "$d/name" 2>/dev/null; }; done
for d in /sys/class/watchdog/watchdog*; do [ -d "$d" ] && { printf '%s ' "$d"; cat "$d/identity" 2>/dev/null; }; done
If present and readable, inspect name, *_label, *_input, date, time, timeout, state or bootstatus attributes individually under the listed directories. Some reads can access hardware or report cached values; availability and permissions differ. Missing directories, empty glob results or an unavailable attribute are valid results, especially in a guest without modeled hardware. Do not use sudo, write attributes, or infer that an absent watchdog class means a physical watchdog cannot exist.
Record the running uname -r, paths and actual values with documented units, omitting any identifying data you do not wish to share. The listed device names and readings do not prove fresh conversion or correct physical calibration. Source-only alternative: use the exact direct-mode failure branch and watchdog close excerpt above to make a table: buffer enabled yields -EBUSY with mutex released; buffer disabled yields a claim requiring release; a watchdog stop attempt can fail under nowayout. Deliver either table with the 12-bit conversion and state what remains unknown without the device.
Practice#
Run a synthetic scale and ownership experiment with Python 3. It does not access sysfs or any device:
python3 - <<'PY'
raw, scale_mv = 1250, 0.8
print('millivolts:', raw * scale_mv, 'volts:', raw * scale_mv / 1000)
for buffer_enabled in (False, True):
claimed = not buffer_enabled
print('buffer:', buffer_enabled, 'claim:', 'success; release required' if claimed else 'EBUSY; no release')
PY
Expected results are 1000.0 mV and 1.0 V; disabled buffering yields a successful claim, while enabled buffering yields EBUSY. Counterexample: substitute scale 0.08; the result becomes 0.1 V, demonstrating why a raw value alone is insufficient. Deliver a two-row ownership table plus the calculation, labeling the chosen scale synthetic and stating the real device attributes you would need. This program creates no files.
Knowledge check#
- Who unlocks
mlockwhen direct-mode claim fails due to an enabled buffer? - Why is raw 1,250 insufficient to infer volts?
- Does a successful watchdog configuration prove a system can recover all failures?
- Why might repeated hwmon reads return the same value without proving a sensor stayed constant?
- How does nowayout change the meaning of a watchdog close?
Answers#
iio_device_claim_direct_modeunlocks before returning-EBUSY; no matching release should be made for that failed claim. A successful claim retains the lock until the matching release.- Scale, offset, units and encoding depend on the particular channel and driver. The synthetic conversion only follows from the explicitly supplied scale and offset.
- No. Reset behavior, timeout servicing, boot recovery, persistent failures and device support must all be tested in the actual recoverable environment.
- The driver may serve cached registers until its next update. The reader must inspect the actual driver and update behavior before treating each read as a new conversion.
- It can cause the stop attempt to fail, leaving the watchdog running even after close. Magic-close permission and nowayout are separate conditions; neither should be tested on a shared host.
Checkpoint#
Submit the conversion and claim table, including successful and failed paths. Ready means your successful path releases once, the failed path does not release, and your units are explicit. Re-evaluate the excerpt with buffer enabled and disabled if the lock ownership is unclear. This is not certification.