Linux驱动开发 - iio子系统(1) 驱动框架

本文详细介绍了Linux内核中的IIO子系统,包括其组成部分、IIO初始化过程,特别是iio_device_register函数的作用以及与sysfs文件系统的关联,展示了如何通过sysfs获取设备数据。
摘要由CSDN通过智能技术生成

文章目录

前言

IIO源码路径

IIO初始化

IIO设备注册 iio_device_register

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",
    // 以下省略
};

  • 35
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值