前言
本笔记基于linux5.10.xx内核,dht11温湿度传感器总结对IIO驱动子系统的使用,基于运行linux内核的arm系列处理器soc;
一、概念
IIO为industrial I/O的简称,为一些传感器的数据采集提供了一套方便的软件框架,作为内核驱动子系统可以和温度、湿度、压力、加速度等传感器的驱动相结合,为相应数据获取提供方便的访问方式;
二、系统拓扑结构
通过将传感器抽象为iio设备后,最终会将其注册为一个字符设备;
芯片和传感器之间的接口可以是多种形式,如i2c、spi、adc、本例中的dht11单总线等;
通过iio可以将传感器的物理数据建立一个数据模型,iio将其定义通道,如dht11可以采集温度和湿度则定义了温度和湿度通道,其它传感器同理;
三、IIO子系统文件
industrialio-core.c
iio子系统的核心层;
iio总线的注册接口;
提供iio_dev结构的分配、、成员初始化、注册接口;
提供iio对应的字符设备的file_operation接口;
提供iio设备属性操作接口;
industrialio-event.c
提供iio设备event事件方式获取数据的接口;
主要操作的是struct iio_dev_opaque结构的event_interface成员和其子成员;
industrialio-buffer.c
提供iio设备数据buffer的功能接口;
对struct iio_buffer结构的操作封装;
iio设备可以选择不使用buffer功能,具体考虑应用场景;
buffer/kfifo_buf.c
提供struct iio_buffer结构的分配结构;
提供struct iio_buffer结构的access成员操作函数集;
将struct iio_buffer作为struct iio_kfifo结构的成员进行操作;
buffer/industrialio-buffer-cb.c
对struct iio_cb_buffer结构的操作封装;
inkern.c
对struct iio_channel结构的操作封装;
industrialio-sw-trigger.c
industrialio-triggered-event.c
buffer/industrialio-triggered-buffer.c
trigger相关模式需要的驱动iio子系统的文件,具体的驱动也要实现相关机制;
四、主要数据结构
struct iio_dev_opaque
定义于include/linux/iio/iio-opaque.h
通过此结构的定义来定义iio_dev;
成员iio_event_interface封装用户层阻塞访问设备时的等待队列头;
buffer_list定义数据buffer的列表;
struct iio_dev_opaque {
struct iio_dev indio_dev;
struct iio_event_interface *event_interface;
struct list_head buffer_list;
struct list_head channel_attr_list;
struct attribute_group chan_attr_group;
#if defined(CONFIG_DEBUG_FS)
struct dentry *debugfs_dentry;
unsigned cached_reg_addr;
char read_buf[20];
unsigned int read_buf_len;
#endif
};
struct iio_dev
定义于include/linux/iio/iio.h
对iio设备的数据表示;
struct iio_dev {
//iio设备的id号,在设备节点中体现为/dev/iio:device+id号,如1则为/dev/iio:device1
int id;
struct module *driver_module;
//iio设备的操作模式,目前一共有6种,如INDIO_DIRECT_MODE为直接模式
int modes;
int currentmode;
struct device dev;
//当操作模式使用buffer机制时,此成员需要分配
struct iio_buffer *buffer;
int scan_bytes;
struct mutex mlock;
const unsigned long *available_scan_masks;
unsigned masklength;
const unsigned long *active_scan_mask;
bool scan_timestamp;
unsigned scan_index_timestamp;
//使用触发机制时需要分配此成员
struct iio_trigger *trig;
bool trig_readonly;
struct iio_poll_func *pollfunc;
struct iio_poll_func *pollfunc_event;
//iio设备针某个器件所定义的通道
struct iio_chan_spec const *channels;
//通道个数如dth11温湿度会定义两个通道
int num_channels;
const char *name;
const char *label;
//此结构定义对器件原始数据操作的函数
const struct iio_info *info;
clockid_t clock_id;
struct mutex info_exist_lock;
const struct iio_buffer_setup_ops *setup_ops;
//通过此成员将iio设备定义为标准字符设备
struct cdev chrdev;
#define IIO_MAX_GROUPS 6
const struct attribute_group *groups[IIO_MAX_GROUPS + 1];
int groupcounter;
unsigned long flags;
void *priv;
};
struct iio_info
定义于include/linux/iio/iio.h
定义操作通道原始数据的接口;
定义操作通道事件的接口;
struct iio_chan_spec
定义于include/linux/iio/iio.h
对器件通道类型定义;
如定义dht11温湿度传感器的温度和湿度通道;
成员enum iio_chan_type定义了iio支持的一些通道类型可以满足大多数场景;
struct iio_buffer
include/linux/iio/buffer_impl.h;
数据buffer的结构定义;
struct iio_trigger
定义于include/linux/iio/trigger.h;
使用trigger方式时,分配iio_dev的此类型成员;
struct file_operations iio_buffer_fileops
定义于industrialio-core.c;
以iio_dev的成员struct cdev chrdev注册为字符设备时,绑定的操作集;
五、驱动使用iio子系统的步骤
DHT11温湿度传感器直接模式
1、定义设备驱动
这里定义为平台设备驱动,并定义兼容属性表用于probe函数执行
static const struct of_device_id dht11_dt_ids[] = {
{ .compatible = “dht11”, },
{ }
};
static struct platform_driver dht11_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = dht11_dt_ids,
},
.probe = dht11_probe,
};
2、自定义设备结构体
不同的传感器按传感器实际特性定义
struct dht11 {
struct device *dev;
struct gpio_desc gpiod;
int irq;
struct completion completion;
/ The iio sysfs interface doesn’t prevent concurrent reads: /
struct mutex lock;
s64 timestamp;
int temperature;
int humidity;
/ num_edges: -1 means “no transmission in progress” */
int num_edges;
struct {s64 ts; int value; } edges[DHT11_EDGES_PER_READ];
};
3、定义通道
static const struct iio_chan_spec dht11_chan_spec[] = {
{ .type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), },
{ .type = IIO_HUMIDITYRELATIVE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), }
};
4、定义iio_info结构
static const struct iio_info dht11_iio_info = {
.read_raw = dht11_read_raw,
};
5、驱动的初始化
平台设备和驱动match之后,dht11_probe函数中做一些初始化将以上结构进行赋值,最后调用devm_iio_device_register,以此完成驱动的工作,剩余的工作交给了iio子系统的核心层去处理;
6、应用层获取传感器数据
方法一:通过open 、read、poll函数操作/dev/iio:device0等设备节点号获取传感器数据
方法二: 通过/sys/bus/iio/devices/*****/温度属性文件或者湿度属性文件获取数据;
其它类型传感器
同理;
可能会使用buffer、event、trigger相关模式;