文章目录
目录
IIO sysfs文件注册 iio_device_register_sysfs
前言
IIO(Industrial I/O)是linux内核中的一个子系统,专门用于处理工业控制、测量设备的数据采集和处理。IIO子系统支持的设备类型众多,包括模数转换器(ADC)、数模转换器(DAC)、加速度计、陀螺仪、惯性测量单元、温度传感器等
IIO子系统的主要组件包括:
- IIO核心。提供驱动程序和用户空间之间的接口,负责设备枚举、注册和管理。
- IIO设备驱动程序。用于控制和读取特定IIO设备的代码模块。
- IIO缓冲区。用于存储传感器和其他测量设备数据的内存区域。
- IIO事件处理。用于处理来自传感器和其他测量设备的中断和事件。
IIO源码路径
- IIO核心 drivers/iio/industrialio-core.c
- IIO设备驱动程序 drivers/iio/adc/mt6577_auxadc.c (每个设备的路径都不一样)
- IIO缓冲区 drivers/iio/industrialio-buffer.c
- IIO事件处理 drivers/iio/industrialio-event.c
IIO初始化
iio_init()函数主要内容:
- 注册iio_bus_type总线,在/sys/bus/下面创建iio目录
- 动态申请主设备号和次设备号,后续__iio_device_register有对应的file_operations
- debugfs创建,在/sys/kernel/debug/下面创建iio目录
struct bus_type iio_bus_type = {
.name = "iio",
};
static int __init iio_init(void)
{
int ret;
/* Register sysfs bus */
ret = bus_register(&iio_bus_type);
if (ret < 0) {
pr_err("could not register bus type\n");
goto error_nothing;
}
ret = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio");
if (ret < 0) {
pr_err("failed to allocate char dev region\n");
goto error_unregister_bus_type;
}
iio_debugfs_dentry = debugfs_create_dir("iio", NULL);
return 0;
error_unregister_bus_type:
bus_unregister(&iio_bus_type);
error_nothing:
return ret;
}
static void __exit iio_exit(void)
{
if (iio_devt)
unregister_chrdev_region(iio_devt, IIO_DEV_MAX);
bus_unregister(&iio_bus_type);
debugfs_remove(iio_debugfs_dentry);
}
subsys_initcall(iio_init);
module_exit(iio_exit);
IIO设备注册 iio_device_register
iio_device_register是一个宏
#define iio_device_register(indio_dev) \
__iio_device_register((indio_dev), THIS_MODULE)
__iio_device_register源码和注释如下,只列出了主要的代码
int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
{
int ret;
indio_dev->driver_module = this_mod;
/* If the calling driver did not initialize of_node, do it here */
if (!indio_dev->dev.of_node && indio_dev->dev.parent)
indio_dev->dev.of_node = indio_dev->dev.parent->of_node;
ret = iio_check_unique_scan_index(indio_dev); //检查是否有重复的索引
if (ret < 0)
return ret;
if (!indio_dev->info)
return -EINVAL;
//设置设备号,之前在iio_init()中调用alloc_chrdev_region分配了主设备号
indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
iio_device_register_debugfs(indio_dev); //创建debugfs相关的文件,受CONFIG_DEBUG_FS宏控制
ret = iio_device_register_sysfs(indio_dev);
if (ret) {
dev_err(indio_dev->dev.parent,
"Failed to register sysfs interfaces\n");
goto error_buffer_free_sysfs;
}
//创建cdev,应用层可以访问/dev/iio:device0设备文件
cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
indio_dev->chrdev.owner = this_mod;
ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
if (ret < 0)
goto error_unreg_eventset;
return 0;
error_unreg_eventset:
iio_device_unregister_eventset(indio_dev);
error_free_sysfs:
iio_device_unregister_sysfs(indio_dev);
error_buffer_free_sysfs:
iio_buffer_free_sysfs_and_mask(indio_dev);
error_unreg_debugfs:
iio_device_unregister_debugfs(indio_dev);
return ret;
}
indio_dev变量先在设备驱动程序中赋值(mt6577_auxadc.c),然后调用__iio_device_register。
iio_device_register_sysfs主要在目录/sys/bus/iio/devices/iio:device1先创建如下文件
应用层通过cat in_voltage0_input就可以获取ADC采集的电压值。
代码最后通过调用cdev_init、cdev_device_add创建cdev,应用层就可以通过open、read、write等接口访问/dev/iio:device0设备文件。
IIO sysfs文件注册 iio_device_register_sysfs
调用关系如下:
iio_device_register_sysfs
---> iio_device_add_channel_sysfs
---> iio_device_add_info_mask_type
---> __iio_add_chan_devattr
---> __iio_device_attr_init
其中在iio_device_add_info_mask_type中设置了读写回调函数,比如cat in_voltage0_input就会调用回调函数iio_read_channel_info
ret = __iio_add_chan_devattr(iio_chan_info_postfix[i],
chan,
&iio_read_channel_info,
&iio_write_channel_info,
i,
shared_by,
&indio_dev->dev,
&indio_dev->channel_attr_list);
最终在__iio_device_attr_init中确定sysfs中文件的name,如下代码
int __iio_device_attr_init()
{
..........
case IIO_SEPARATE:
if (chan->indexed)
name = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
iio_direction[chan->output],
iio_chan_type_name_spec[chan->type],
chan->channel,
full_postfix);
//in_voltage0_input
//
..........
}
格式为"%s_%s%d_%s", 对应了in_voltage0_input,其中iio_direction、iio_chan_type_name_spec、iio_chan_info_postfix对应的值为 "in" 、"voltage" 、"input"。如下代码片段。
static const char * const iio_direction[] = {
[0] = "in",
[1] = "out",
};
static const char * const iio_chan_type_name_spec[] = {
[IIO_VOLTAGE] = "voltage",
[IIO_CURRENT] = "current",
[IIO_POWER] = "power",
[IIO_ACCEL] = "accel",
// 以下省略
};
/* relies on pairs of these shared then separate */
static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_RAW] = "raw",
[IIO_CHAN_INFO_PROCESSED] = "input",
[IIO_CHAN_INFO_SCALE] = "scale",
[IIO_CHAN_INFO_OFFSET] = "offset",
// 以下省略
};