El subsistema IIO: por qué los sensores tienen su propio mundo
Qué es Industrial I/O (IIO), el subsistema del kernel para ADCs, DACs, acelerómetros, giroscopios y sensores de todo tipo. Su ABI de sysfs con el modelo raw + scale + offset y unidades estándar, sus dos caminos de datos, los consumidores dentro del kernel, y por qué no encaja ni en el subsistema input ni en hwmon.
Un acelerómetro, un conversor analógico-digital, un sensor de temperatura y un giroscopio no son teclados ni ventiladores, y sin embargo el kernel necesitaba una casa común para todos ellos. Esa casa es IIO —Industrial I/O—, el subsistema que unifica cómo se describen, se leen y se transmiten los datos de sensores y conversores. No es un capricho de nomenclatura: sin IIO, cada fabricante inventaría su propio char device, sus propias unidades y su propio formato, y userspace tendría que aprenderlos uno a uno. IIO impone un contrato, y ese contrato es lo que hace que iio_read_channel_processed devuelva grados Celsius vengan del chip que vengan.
- Entender qué clase de dispositivos cubre IIO y por qué merecen un subsistema propio.
- Leer la ABI de sysfs de IIO: canales, atributos
_raw,_scale,_offsety sus unidades. - Distinguir los dos caminos de datos: lectura puntual por sysfs y captura con buffer.
- Situar a IIO frente a los subsistemas input y hwmon, y conocer a los consumidores internos.
Qué cubre IIO y por qué existe
IIO, en drivers/iio/, agrupa dispositivos que miden o generan magnitudes analógicas: conversores ADC y DAC, acelerómetros, giroscopios, magnetómetros, sensores de luz, de temperatura, de presión, de humedad, de proximidad, potenciómetros digitales. Lo que tienen en común no es la física sino el patrón de uso: producen valores numéricos que representan una magnitud del mundo, a menudo a alta frecuencia, y userspace quiere leerlos con unidades conocidas y, a veces, en ráfagas con marca de tiempo.
Antes de IIO, cada driver de sensor resolvía esto por su cuenta. Uno exponía un char device con un ioctl propietario; otro colgaba números crudos de un archivo de debugfs; un tercero mentía sobre las unidades. IIO sustituye ese caos por un modelo de datos declarativo: el dispositivo se describe como un conjunto de canales, cada canal tiene un tipo y unos atributos, y el núcleo se encarga de exponerlos de forma uniforme. El módulo del núcleo se llama industrialio.
flowchart TD HW[Sensores y conversores: ADC, acelerometro, temperatura] --> Core[Nucleo IIO industrialio] Core -->|ABI de sysfs| SysFS[Userspace: lectura puntual] Core -->|char device iio:deviceN| Buf[Userspace: captura con buffer] Core -->|linux/iio/consumer.h| InK[Consumidores en el kernel: thermal, hwmon, input]
La ABI de sysfs: raw, scale y offset
Cada dispositivo IIO aparece en /sys/bus/iio/devices/iio:deviceN/, y sus canales se exponen como archivos con un esquema de nombres rígido: dirección (in para lectura, out para salida), tipo (voltage, accel, temp…), índice y atributo. La clave del modelo es que el valor bruto y su conversión a unidades físicas viajan por separado:
$ ls /sys/bus/iio/devices/iio:device0/
name in_accel_x_raw in_accel_y_raw in_accel_z_raw
in_accel_scale sampling_frequency buffer0 trigger0
$ cat /sys/bus/iio/devices/iio:device0/in_accel_x_raw
-16543
$ cat /sys/bus/iio/devices/iio:device0/in_accel_scale
0.000598
La regla universal de IIO para obtener la magnitud física es:
valor_fisico = (raw + offset) * scale
Con los números de arriba: -16543 * 0.000598 da unos -9.89, es decir cerca de -1 g en la unidad canónica del acelerómetro, metros por segundo al cuadrado. El offset —ausente aquí, luego cero— corrige el cero del sensor; el scale traduce cuentas del ADC a unidades reales. Separar raw de scale no es burocracia: permite leer el valor crudo a máxima velocidad sin dividir en el camino caliente, y deja la conversión, más cara, para quien la necesite.
Las unidades son parte del contrato, fijadas en Documentation/ABI/testing/sysfs-bus-iio. Un acelerómetro reporta en metros por segundo al cuadrado; un giroscopio en radianes por segundo; un magnetómetro en Gauss; la temperatura en milésimas de grado Celsius; el voltaje en milivoltios; la presión en kilopascales; la luz en lux. Cualquier driver que exponga in_temp_scale promete que raw * scale son milésimas de grado, sea un termopar o un diodo.
Algunos sensores hacen la conversión internamente y entregan la magnitud ya calculada. Para ellos IIO ofrece el atributo _input (por ejemplo in_temp_input) en lugar de _raw, y su valor ya está en la unidad estándar, sin scale que aplicar. La regla mental es simple: si ves _raw, tú conviertes con scale y offset; si ves _input, el número ya es físico.
Dos caminos de datos
IIO ofrece dos formas de sacar datos, pensadas para necesidades opuestas. La lectura puntual por sysfs es cómoda y lenta: cada cat in_voltage0_raw es una syscall que dispara una conversión y devuelve un número. Sirve para consultas esporádicas —¿qué temperatura hace?— pero no para muestrear a kilohercios, porque el coste por muestra es enorme y no hay marca de tiempo precisa.
El camino con buffer, por el contrario, entrega ráfagas de muestras a través del char device /dev/iio:deviceN. Un trigger marca el ritmo, el driver empuja cada conjunto de muestras a un buffer con su marca de tiempo, y userspace las lee en bloque. Es el camino para adquisición continua a alta frecuencia. La lección 33.5 lo construye entero; aquí basta con saber que existe y por qué la ABI separa desde el principio “leer un valor” de “muestrear un flujo”.
IIO frente a input y hwmon
La pregunta natural es por qué un acelerómetro no vive en el subsistema input, que ya maneja teclados y ratones. La respuesta es que input modela eventos de interfaz humana —una tecla EV_KEY, un eje absoluto EV_ABS— con semántica de interacción, no un flujo de medidas físicas con unidades y marcas de tiempo a miles de muestras por segundo. Un acelerómetro para detectar la orientación de la pantalla puede tener un puente hacia input, pero su naturaleza es de sensor.
Tampoco encaja en hwmon, el subsistema de monitorización de salud del hardware: temperaturas de CPU, voltajes de la placa, velocidad de ventiladores. hwmon tiene semántica fija y orientada a supervisar la máquina, no a adquirir señales arbitrarias con buffers y triggers. De hecho, la relación entre ambos es tan estrecha que existe un puente, iio_hwmon, que expone canales IIO como si fueran sensores hwmon cuando lo que quieres es justamente monitorizar.
IIO (Industrial I/O)
Sensores y conversores con unidades estándar, lectura cruda o procesada, y captura con buffer y marca de tiempo a alta frecuencia. ADC, acelerómetros, giroscopios, temperatura.
input
Eventos de interfaz humana: teclas, botones, ejes de ratón o joystick. Semántica de interacción, no de medida física.
hwmon
Monitorización de salud del sistema: temperatura de CPU, voltajes, ventiladores. Semántica fija de supervisión, sin buffers ni triggers.
El otro superpoder de IIO son los consumidores dentro del kernel. Gracias a <linux/iio/consumer.h>, otro driver puede leer un canal IIO sin pasar por userspace: una zona térmica que necesita la temperatura de un ADC llama a iio_read_channel_processed y recibe grados; un driver de batería lee la tensión de una celda. El device tree conecta consumidor y sensor con la propiedad io-channels, y el driver toma el canal por su nombre:
#include <linux/iio/consumer.h>
struct fan_ctl {
struct iio_channel *temp; /* canal IIO de otro driver */
};
static int fan_probe(struct platform_device *pdev)
{
struct fan_ctl *f = /* ... reservado con devm_kzalloc ... */;
int millicelsius, ret;
/* "ambient" enlaza con io-channel-names en el device tree */
f->temp = devm_iio_channel_get(&pdev->dev, "ambient");
if (IS_ERR(f->temp))
return PTR_ERR(f->temp);
/* magnitud ya convertida a la unidad estandar: milesimas de grado */
ret = iio_read_channel_processed(f->temp, &milicelsius);
if (ret < 0)
return ret;
dev_info(&pdev->dev, "temperatura ambiente: %d.%03d C\n",
milicelsius / 1000, milicelsius % 1000);
return 0;
}
El driver del ventilador no sabe ni le importa qué chip mide la temperatura: pide un canal por su nombre lógico y recibe grados. El sensor se describe una vez y lo consumen tanto userspace como el resto del kernel.
Piensa en qué añade IIO que no estaba en los drivers sueltos que reemplazó, porque ahí se ve para qué existe un subsistema. Cada uno de esos drivers ya sabía leer su chip; lo que no existía era un acuerdo compartido sobre el significado de los números. IIO no aporta código para hablar con el hardware —eso lo pone cada driver—, aporta una ontología: esto es un canal, es de tipo aceleración, su valor crudo se convierte a metros por segundo al cuadrado con este scale, se puede muestrear con este trigger y su marca de tiempo va en este formato. Ese acuerdo es lo que permite que libiio grafique cualquier sensor sin conocerlo, que una zona térmica lea un ADC que nunca previó, que un acelerómetro de un fabricante y el de otro se usen con el mismo programa. La lección general trasciende los sensores: un subsistema del kernel no es una carpeta de drivers parecidos, es la definición de una frontera semántica estable —una ABI— por encima de hardware heterogéneo. Los drivers son intercambiables y mortales; el contrato es lo que perdura y lo que hace componible el sistema. Cuando entiendes que el valor de IIO está en lo que estandariza y no en lo que ejecuta, entiendes por qué el diseño de un buen subsistema es, ante todo, un ejercicio de definir significados que no traicionen a nadie.
- En una máquina con un sensor IIO (o en QEMU con un driver de prueba) lista
/sys/bus/iio/devices/iio:device0/y clasifica los archivos en canales, escalas y control. - Toma un
in_*_raw, suin_*_scaley suin_*_offsetsi existe, y calcula a mano la magnitud física con la fórmula de IIO. - Busca en
Documentation/ABI/testing/sysfs-bus-iiola unidad canónica de tu canal y verifica que el resultado tiene sentido. - Argumenta por qué un acelerómetro no debería vivir en el subsistema input aunque a veces se puentee hacia él.
- Encuentra en el árbol un uso de
iio_read_channel_processedfuera dedrivers/iio/y explica qué subsistema consume el sensor.