LINUX IIO子系统分析之七 虚拟iio device驱动实现
前面几章我们基本完成了IIO子系统的所有内容,而该章即为本专栏的结束篇,主要用来实现一个虚拟的IIO DEVICE DRIVER,本章的内容主要包括如下几部分:一、 虚拟IIO DEVICE的说明二、虚拟IIO DEVICE DRIVER实现所需的知识点三、虚拟IIO DEVICE DRIVER的数据结构及实现说明四、虚拟IIO DEVICE DRIVER的测试验证一、 虚拟IIO DEVICE的
前面几章我们基本完成了IIO子系统的所有内容,而该章即为本专栏的结束篇,主要用来实现一个虚拟的IIO DEVICE DRIVER,本章的内容主要包括如下几部分:
一、 虚拟IIO DEVICE的说明
二、虚拟IIO DEVICE DRIVER实现所需的知识点
三、虚拟IIO DEVICE DRIVER的数据结构及实现说明
四、虚拟IIO DEVICE DRIVER的测试验证
一、 虚拟IIO DEVICE的说明
本次我们将实现一个虚拟的温度传感器芯片(芯片型号为virt0824),该设备提供2个channel,并且提
供7个寄存器,定义如下:
Channel0_current_temp(0x00,表示通道0的当前温度)
Channel1_current_temp(0x01,表示通道1的当前温度)
Channel0_high_alarm_temp(0x02,表示通道0的温度过高告警阈值)
Channel1_high_alarm_temp(0x03,表示通道1的温度过高告警阈值)
Channel0_low_alarm_temp(0x04,表示通道0的温度过低告警阈值)
Channel1_low_alarm_temp(0x05,表示通道0的温度过低告警阈值)
Channel_fault_mask//4个bit;(0x06,表示温度告警fault bit,bit0-bit1表示温度过高告警标志、bit2-bit3表示温度过低告警标志)
该温度传感器为iic device,并且支持温度告警中断,同时该温度传感器支持温度数据ready中断(此中断主要用于支持iio buffer对应的数据连续采集功能,实际的芯片可能并不提供该中断)
二、虚拟IIO DEVICE DRIVER实现所需的知识点
为了让本次实现的IIO DEVICE能够尽量实现数据单次采集、iio buffer、iio event等功能,本次虚拟iio
device driver主要涉及如下几个知识点:
- 提供虚拟iio adapter controller,并完成一个虚拟的温度传感器芯片,并提供sysfs下的属性文件,可以修改温度传感器芯片中的各寄存器的值,可以置位温度告警位等等;
- 提供虚拟的irq chip,用于触发温度传感器温度告警中断、温度数据可读中断;
- 提供iio device driver,实现温度传感器对应iio device的创建及注册。
三、虚拟IIO DEVICE DRIVER的数据结构及实现说明
数据结构定义
定义数据结构virt0824_temp,该结构体中包含iic client对应的regmap对象(用于读取virt0824设备的寄存器值)、该iio device对应的iio trigger,用于处理温度传感器温度告警中断信息,
struct virt0824_temp
{
char name[32];
struct regmap *regmap;
struct virt0824_temp_info *temp_info;
struct iio_trigger *trig;
spinlock_t lock;
unsigned short data[2] ____cacheline_aligned;
};
实现说明
iio event实现(温度告警)
基于温度告警,我们通过在温度告警中断处理函数中读取告警信息,并event信息发送给iio event的kfifo中,接口调度过程如下所示,通过该调度过程,应用程序即可监控到温度传感器上报的告警。
温度告警中断处理函数的实现如下所示
static irqreturn_t virt0824_event_handler(int irq, void * data)
{
struct iio_dev *indio_dev = (struct iio_dev *)data;
struct virt0824_temp *devp = iio_priv(indio_dev);
unsigned long flags = 0;
unsigned int val = 0;
int ret = 0;
spin_lock_irqsave(&devp->lock, flags);
ret = regmap_read(devp->regmap, CHANNEL_FAULT_MASK, &val);
if(ret)
goto unlock;
if (val & BIT(CHANNEL0_HIGH_ALARM_TEMP_BIT))
{
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
CHANNEL0_HIGH_ALARM_TEMP_BIT,
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), iio_get_time_ns());
printk("%s:%d \n", __FUNCTION__, __LINE__);
}
if (val & BIT(CHANNEL1_HIGH_ALARM_TEMP_BIT))
{
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
CHANNEL1_HIGH_ALARM_TEMP_BIT,
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), iio_get_time_ns());
printk("%s:%d\n", __FUNCTION__, __LINE__);
}
if (val & BIT(CHANNEL0_LOW_ALARM_TEMP_BIT))
{
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
CHANNEL0_HIGH_ALARM_TEMP_BIT,
IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), iio_get_time_ns());
printk("%s:%d\n", __FUNCTION__, __LINE__);
}
if (val & BIT(CHANNEL1_LOW_ALARM_TEMP_BIT))
{
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
CHANNEL1_HIGH_ALARM_TEMP_BIT,
IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), iio_get_time_ns());
printk("%s:%d\n", __FUNCTION__, __LINE__);
}
unlock:
spin_unlock_irqrestore(&devp->lock, flags);
return IRQ_HANDLED;
}
iio buffer实现
主要是连续采集各通道的当前温度,接口调度过程如下所示,通过该调度过程,应用程序即连续可读取温度传感器的当前温度。
在本次的测试中,温度传感器通过温度数据可读中断从而将数据push到iio buffer中,用于连续数据采集的iio trigger,iio trigger-buffer的中断处理函数的实现如下
static irqreturn_t virt0824_trigger_handler(int irq, void * data)
{
struct iio_dev *indio_dev = (struct iio_dev *)data;
struct virt0824_temp *devp = iio_priv(indio_dev);
disable_irq_nosync(irq);
printk("%s:%d\n", __FUNCTION__, __LINE__);
iio_trigger_poll(devp->trig);
printk("%s:%d\n", __FUNCTION__, __LINE__);
return IRQ_HANDLED;
}
该虚拟温度传感器的iio device相关的参数设置如下所示
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),
},
};
static const struct iio_chan_spec virt0824_channels[] = {
{
.type = IIO_TEMP,
.indexed = 1,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.event_spec =virt0824_temp_event,
.num_event_specs = ARRAY_SIZE(virt0824_temp_event),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
{
.type = IIO_TEMP,
.indexed = 1,
.channel = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) ,
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.event_spec = virt0824_temp_event,
.num_event_specs = ARRAY_SIZE(virt0824_temp_event),
.scan_index = 1,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
}
};
另外针对虚拟中断控制器、虚拟iic device已经在其他专栏中说明,此处直接调用不再细说。
四、虚拟IIO DEVICE DRIVER的测试验证
本驱动已经在ubuntu16.04上测试验证,由于ubuntu16.04内核默认没有将iio 子系统 编译进内核,但是已编译成驱动模块,可以在路径/lib/modules/4.4.0-66-generic/kernel/drivers/iio下找到对应的驱动模块,我们需要这些驱动模块: industrialio.ko、industrialio-triggered-event.ko、kfifo_buf.ko、industrialio-triggered-buffer.ko即可,先将这些驱动insmod 系统中;
然后按照本次虚拟驱动主要涉及如下内容:
insmod virt_i2c_controller.ko
insmod virt0808_dev.ko
insmod virt0808_irq.ko
insmod virt0824_temp_driver.ko
测试温度告警阈值
主要测试iio event功能,我们的虚拟程序中已经实现了iio event的应用测试程序。
- 首先运行该测试程序;
- 修改虚拟温度传感器的两个通道的当前温度、通道0的温度过高告警阈值、并设置温度0过高温度告警fault bit为1,执行的命令如下:
- 寄存器值修改好后,我们手动触发一个温度过高告警中断,执行命令如下
- 查看测试程序已经收到iio event事件,如下图所示
测试连续数据采集(iio buffer功能)
- 首先使能通道0的温度采集功能,设置iio buffer的缓存为16,watermark为1
- 开始监控/dev/iio:device,进行数据采集,命令如下
cat /dev/iio:device0 |xxd -c 2
- 触发数据可读中断,如下图所示
- 查看读取的数据,如下图所示,可正常进行数据采集
测试单次数据采集
执行如下命令即可对两个通道当前的数据进行温度采集工作。
至此完成了IIO子系统专栏的分析,即完成了IIO子系统内部的设计说明,也完成了一个虚拟的IIO DEVICE DIRVER,希望对大家有所帮助。 本次测试代码的路径为https://gitee.com/jerry_chg/virt_iio_device_driver
更多推荐
所有评论(0)