上一章我们介绍了iio子系统中的iio trigger模块,本章我们将介绍iio event模块,iio event主要用于阈值监测、自由落体监测等监测功能。因为IIO EVENT涉及IIO DEVICE字符设备文件操作,因此本章内容主要分为如下几部分:
一、 IIO DEVICE字符设备文件操作接口
二、IIO EVENT设计分析
三、IIO EVENT相关接口说明
一、 IIO DEVICE字符设备文件操作接口
在IIO 子系统中,每一个IIO DEVICE均会创建一个字符设备文件,名称为/dev/iio:deviceX,该字符设备文件节点在iio_device_register中调用cdev_init、cdev_add完成字符设备文件节点的创建,且文件操作接口为iio_buffer_fileops(而借助sysfs的kobject uevent,则会将cdev add的信息发送给应用程序,应用层的mdev/udev接收到cdev add的uevent之后,则会调用mknod完成字符设备文件节点的创建,详细内容可参考我之前写的字符设备文件专栏的内容《》)。如下即是/dev/iio:deviceX的访问流程,应用程序通过open/read/poll/ioctl接口则会调用内核中VFS提供的操作接口,最终则调用iio_buffer_fileops中定义的接口。
iio_buffer_fileops的定义如下
问题来了,iio device对应的字符设备文件节点主要提供哪些服务呢?
主要提供两方面的内容:
- 提供对iio device各通道连续采集数据的读取操作(前提是该iio device的某些通道提供了iio buffer功能);
- 提供创建event数据读取对应的匿名字符设备文件节点的功能(通过ioctl功能,则创建一个匿名的字符设备文件节点,用于进行iio device各通道相关的event数据的读取功能)。所谓匿名即该字符设备文件节点并不会显示在文件系统中(无法在应用层中找到该文件名称),且一个iio device同一时刻仅可创建一个匿名字符设备文件节点。
二、IIO EVENT设计分析
iio event相关的数据结构
struct iio_event_interface是iio event相关的数据结构,该数据结构的定义如下
该数据结构主要是对event子模块的定义,其中:
- 等待队列wait,当应用程序读取触发事件信息时,若当前无数据可读,则将当前进程加入到该等待队列,待调用iio_push_event将触发事件信息加入kfifo后,则wakeup该队列中的进程;
- 定义kfifo,存储所有触发的事件信息,供应用程序获取;
- 将even子模块动态定义的event attribute均添加至该链表中(属性名称格式为{iio_dir}_{iio_channel_type}{channel-Index/channel_modify}_{ev_type}_{ev_dir}_{ev_info});
- flags标记该event是否已使能(即应用程序是否通过ioctl调用创建一个匿名fd,若使能则置位IIO_BUSY_BIT_POS)
iio event fd创建
IIO event数据信息也是通过字符设备文件节点与应用程序进行交互的,但就像上面所说的,iio event数据读取对应的字符设备文件节点是一个匿名字符设备文件节点,且必须借助字符设备文件节点/dev/iio:deviceX的ioctl方可创建。创建流程如下图所示。借助字符设备文件/dev/iio:deviceX提供的ioctl,即创建一个匿名的文件节点,并返回该文件节点对应的event detect fd。
而event detect fd的文件操作接口的定义如下,提供event detect信息的读取及是否可读监控接口poll。
event 检测及读取流程
针对event 信息而言,主要就涉及event检测、event信息读取两部分,这两部分的关联如下图所示:
- 应用程序可直接通过read接口读取event信息(若event信息存在则读取检测到的event信息;若event信息不存在且fd设置为阻塞读方式,则将该读取进程休眠,加入到event的wait队列中);
- 应用程序可通过select或者epoll检测event fd是否可读,最终会调用event fd的poll接口,加入到event的等待队列中;
- 当iio device检测到某一个事件后,则通过中断(或其他的方式通知cpu)通知cpu,cpu则调用中断处理函数处理中断,在中断处理函数中读取检测到的event,然后调用iio_push_event将event加入到iio device对应event子模块的kfifo中,并wakeup event的wait队列,这样就唤醒了上述1和2中sleep的进程,进程即可进行event事件的读取操作。
备注:在IIO子系统中,针对iio event,也设计与iio trigger的关联,即由iio trigger触发event的detect操作。若将iio event与iiotrigger关联,则执行的流程大致如下所述:
- 为iio event定义中断处理接口,并赋值给struct iio_dev的pollfunc_event成员(struct iio_poll_func *);
- 从iio trigger中申请一个虚拟的irq,并完成对应中断处理函数的注册(即上述1中的pollfunc_event);
- 注册一个iio trigger,用于处理iio device检测的event,则调用iio trigger 提供的iio_trigger_poll接口,由该接口调度iio trigger中所有申请的虚拟irq的中断处理函数;
- pollfunc_event接口中读取检测的event接口,然后wakeup event wait队列。
以下即是使用trigger-event方式的event detect检测及读取流程,相比上面的流程而言,则增加了虚拟irq的中断处理流程,而针对event信息而言,一般也就是出现告警等信息而检测的,并不像连续的数据采集那样频繁读取,因此这一种方式的event信息检测及读取流程并没有多少iio device driver采用,iio device driver一般使用上面的检测及读取流程。
三、IIO EVENT相关接口说明
iio event相关的sysfs属性的创建接口
iio_device_register_eventset接口用于创建iio event相关的event属性,该接口主要由iio_device_register接口调用,主要是根据struct iio_event_spec *event_spec中定义的变量,创建对应的event属性,
下面是我实现的虚拟温度传感器芯片定义的event信息,其创建了温度上限告警、温度下限告警相关的属性,主要包括温度上限检测使能、温度下限检测使能、温度上限告警值设置及读取、温度下限告警值设置及读取等属性。
static const struct iio_event_spec virt0824_temp_event[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
event属性的名称格式为{iio_dir}_{iio_channel_type}{channel-Index/channel_modify}_{ev_type}_{ev_dir}_{ev_info}),针对我们上述定义的event属性,其生成的属性文件名称如下所示,生成的属性文件也是符合该格式的。
event push接口
iio_event_poll接口主要实现将event信息放入event的kfifo中,并wakeup event wait queue,然后应用程序即可读取该event信息。
event chrdev ops
iio_event_chrdev_fileops即为event fd的文件操作接口,我们在上面已经说明,此处不再赘述。
以上即是iio event的主要内容,针对iio device driver而言,仅需要实现struct iio_event_spec *类型的变量定义event的sysfs属性,并在检测到event之后,调用iio_event_poll将event信息压入event kfifo中即可。相对来说这部分驱动实现还是很简单的,而我们上面介绍的则是iio event内部的设计实现。下一章介绍iio buffer的设计实现。