Linux IIO子系统分析6

Linux IIO子系统分析6(基于Linux6.6)---device的驱动开发流程介绍

 


一、IIO子系统的关键技术点总结

IIO子系统主要使用如下几个关键技术点实现其主要功能:

  1. 借助sysfs、kobject机制,实现IIO DEVICE各类的设备属性,并借助于sysfs暴露给应用程序。
    1. 实现iio event相关的设备属性,并将属性参数以sysfs文件的形式显示在IIO DEVICE的events子目录(/sys/bus/iio/devices/iio:deviceX/events),包括event的使能、event参数设置与读取文件等;
    2. 实现iio trigger设备及其属性,并将属性参数以sysfs文件的显示在trigger子目录(/sys/bus/iio/devices/triggerX),可查看trigger的名称;而iio device则可以通过文件/sys/bus/iio/devices/iio:deviceX/trigger查看或修改当前iio device绑定的trigger;
    3. 实现iio buffer相关的设备属性,用于进行iio buffer的使能与否、各channel 相关的数据采集缓存使能等,主要在目录/sys/bus/iio/devices/iio:deviceX/scan_elements、/sys/bus/iio/devices/iio:deviceX/buffer下;
    4. 实现iio device相关的raw 数据读取等设备属性(在目录/sys/bus/iio/devices/iio:deviceX/下),实现读取各channel的raw数据信息(可理解单次数据采集,其与iio buffer采集是互斥的,即一个channel若开启了iio buffer进行数据采集及缓存,则无法通过dir_type_raw文件获取该通道的raw data);
  2. 借助于字符设备文件机制,应用程序可获取到iio buffer缓存的采集数据以及iio device触发的iio event信息,这两个信息目前仅能通过字符设备文件进行获取;
  3. iio trigger内部通过实现虚拟的irq chip,实现iio trigger与iio trigger consumer的1:N绑定,而不需要关注iio trigger consumer具体的数据类型等信息;

IIO子系统实现以上三种技术完成了iio device及其内部iio buffer、iio event、iio trigger等模块的功能实现。

二、IIO DEVICE的注册与注销接口说明

     iio device注册接口为devm_iio_device_register、iio_device_register,而devm_iio_device_register接口则实现了iio device的自动注销功能,因此iio device driver尽量使用该接口进行iio device的注册

2.1 iio_device_register实现的主要功能

 include/linux/iio/iio.h

/**
 * iio_device_register() - register a device with the IIO subsystem
 * @indio_dev:		Device structure filled by the device driver
 **/
#define iio_device_register(indio_dev) \
	__iio_device_register((indio_dev), THIS_MODULE)
int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod);
void iio_device_unregister(struct iio_dev *indio_dev);

  drivers/iio/industrialio-core.c

int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
{
	struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
	struct fwnode_handle *fwnode = NULL;
	int ret;

	if (!indio_dev->info)
		return -EINVAL;

	iio_dev_opaque->driver_module = this_mod;

	/* If the calling driver did not initialize firmware node, do it here */
	if (dev_fwnode(&indio_dev->dev))
		fwnode = dev_fwnode(&indio_dev->dev);
	/* The default dummy IIO device has no parent */
	else if (indio_dev->dev.parent)
		fwnode = dev_fwnode(indio_dev->dev.parent);
	device_set_node(&indio_dev->dev, fwnode);

	fwnode_property_read_string(fwnode, "label", &indio_dev->label);

	ret = iio_check_unique_scan_index(indio_dev);
	if (ret < 0)
		return ret;

	ret = iio_check_extended_name(indio_dev);
	if (ret < 0)
		return ret;

	iio_device_register_debugfs(indio_dev);

	ret = iio_buffers_alloc_sysfs_and_mask(indio_dev);
	if (ret) {
		dev_err(indio_dev->dev.parent,
			"Failed to create buffer sysfs interfaces\n");
		goto error_unreg_debugfs;
	}

	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;
	}
	ret = iio_device_register_eventset(indio_dev);
	if (ret) {
		dev_err(indio_dev->dev.parent,
			"Failed to register event set\n");
		goto error_free_sysfs;
	}
	if (indio_dev->modes & INDIO_ALL_TRIGGERED_MODES)
		iio_device_register_trigger_consumer(indio_dev);

	if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
		indio_dev->setup_ops == NULL)
		indio_dev->setup_ops = &noop_ring_setup_ops;

	if (iio_dev_opaque->attached_buffers_cnt)
		cdev_init(&iio_dev_opaque->chrdev, &iio_buffer_fileops);
	else if (iio_dev_opaque->event_interface)
		cdev_init(&iio_dev_opaque->chrdev, &iio_event_fileops);

	if (iio_dev_opaque->attached_buffers_cnt || iio_dev_opaque->event_interface) {
		indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), iio_dev_opaque->id);
		iio_dev_opaque->chrdev.owner = this_mod;
	}

	/* assign device groups now; they should be all registered now */
	indio_dev->dev.groups = iio_dev_opaque->groups;

	ret = cdev_device_add(&iio_dev_opaque->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_buffers_free_sysfs_and_mask(indio_dev);
error_unreg_debugfs:
	iio_device_unregister_debugfs(indio_dev);
	return ret;
}
EXPORT_SYMBOL(__iio_device_register);

iio device register主要实现如下几个功能:

  1. 完成该iio device对应的字符设备文件的创建,字符设备文件的操作接口为iio_buffer_fileops;
  2. 完成iio buffer对象指针的创建及iio buffer相关的设备属性的创建(主要通过函数iio_buffer_alloc_sysfs_and_mask实现);
  3. 完成iio event interface的内存申请及初始化,并完成iio event相关的设备属性的创建(iio_device_register_eventset);
  4. 完成iio device相关的设备属性的创建(即是上面所说的raw data获取相关的属性文件,通过iio_device_add_channel_sysfs、iio_device_register_sysfs接口调用实现);
  5. 若iio device支持trigger-buffer、trigger-event,则通过iio_device_register_trigger_consumer完成current_trigger属性的创建,以便进行iio device与trigger的绑定与解绑操作;
  6. 调用device_add完成iio device对应struct device类型变量的注册,因为上面所需创建的设备属性均是指struct device类型变量;

     iio device register主要就完成以上6步的操作,也就是我们上面一中所说的大部分内容,而iio device的注销则主要完成相反的功能。

三、IIO DEVICE的驱动开发流程

 在 Linux 中,IIO(Industrial I/O)子系统用于处理传感器、数据采集设备等硬件的输入输出操作。IIO 设备驱动的开发涉及创建驱动程序,以便正确地与硬件进行交互,提供数据采集、触发、缓冲区管理等功能。IIO 驱动开发流程包括设备的初始化、数据采集、错误处理、与用户空间的交互等多个环节。

下面是 IIO 设备驱动开发的基本流程:

1. 了解硬件特性

首先,必须理解设备的硬件特性,包括:

  • 数据采集方式(单次采样、连续采样、轮询、触发等)。
  • 支持的传感器类型(温度、压力、加速度、磁场等)。
  • 设备的接口(SPI、I2C、GPIO、并行总线等)。
  • 设备支持的功能(例如缓冲区、触发机制、数据格式等)。

2. 创建 IIO 设备结构 (iio_dev)

在 IIO 驱动中,最重要的数据结构是 iio_dev,它表示一个 IIO 设备。你需要定义并初始化一个 iio_dev 结构体,用于描述你的设备。

定义 iio_dev 结构体

#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/platform_device.h>

static int my_iio_device_probe(struct platform_device *pdev)
{
    struct iio_dev *indio_dev;
    int ret;

    indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*indio_dev));
    if (!indio_dev)
        return -ENOMEM;

    indio_dev->dev.parent = &pdev->dev;
    indio_dev->name = "my_iio_device";  // 设置设备名称
    indio_dev->modes = INDIO_DIRECT_MODE; // 设备工作模式
    indio_dev->info = &my_iio_info; // 设备的操作函数

    ret = iio_device_register(indio_dev);
    if (ret)
        return ret;

    platform_set_drvdata(pdev, indio_dev);

    return 0;
}

static int my_iio_device_remove(struct platform_device *pdev)
{
    struct iio_dev *indio_dev = platform_get_drvdata(pdev);

    iio_device_unregister(indio_dev);

    return 0;
}

static const struct of_device_id my_iio_device_dt_ids[] = {
    { .compatible = "my_company,my_device", },
    { }
};
MODULE_DEVICE_TABLE(of, my_iio_device_dt_ids);

static struct platform_driver my_iio_driver = {
    .probe = my_iio_device_probe,
    .remove = my_iio_device_remove,
    .driver = {
        .name = "my_iio_driver",
        .of_match_table = my_iio_device_dt_ids,
    },
};

module_platform_driver(my_iio_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("IIO Device Driver Example");

3. 定义 IIO 信息结构体 (iio_info)

iio_info 结构体包含与 IIO 设备交互的各种操作函数。例如,获取传感器数据、配置设备、控制缓冲区等。

定义操作函数

static const struct iio_info my_iio_info = {
    .read_raw = my_iio_read_raw,  // 读取原始数据
    .write_raw = my_iio_write_raw, // 写入原始数据
    .driver_module = THIS_MODULE,
};

实现读写操作函数

read_rawwrite_raw 函数是用来处理传感器数据的核心函数,通常这些函数与硬件的 I/O 操作直接相关。

static ssize_t my_iio_read_raw(struct iio_dev *indio_dev,
                                const struct iio_chan_spec *chan, 
                                int *val, int *val2, long mask)
{
    switch (mask) {
    case IIO_CHAN_INFO_RAW:
        // 从硬件中读取原始数据
        *val = read_sensor_data();
        return IIO_VAL_INT;
    default:
        return -EINVAL;
    }
}

static ssize_t my_iio_write_raw(struct iio_dev *indio_dev,
                                 const struct iio_chan_spec *chan, 
                                 int val, int val2, long mask)
{
    switch (mask) {
    case IIO_CHAN_INFO_RAW:
        // 写入数据到硬件
        write_sensor_data(val);
        return IIO_VAL_INT;
    default:
        return -EINVAL;
    }
}

4. 配置 IIO 设备的通道

每个 IIO 设备可能有多个数据通道。通过 iio_chan_spec 结构来描述这些通道。每个通道通常对应一个传感器的某个数据输出(如温度、加速度、磁场等)。

static const struct iio_chan_spec my_iio_channels[] = {
    {
        .type = IIO_TEMP,
        .indexed = 0,
        .channel = 0,
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), // 指定读取 raw 数据
    },
    { }
};

5. 处理缓冲区

IIO 子系统支持缓冲区机制,可以将数据批量采集并存储到内存中,然后供用户空间访问。你可以选择启用缓冲区,以便在硬件驱动和用户空间之间高效地传输数据。

static int my_iio_device_buffer_postenable(struct iio_dev *indio_dev)
{
    /* 初始化硬件或开始数据采集 */
    start_data_collection();
    return 0;
}

static int my_iio_device_buffer_predisable(struct iio_dev *indio_dev)
{
    /* 停止数据采集 */
    stop_data_collection();
    return 0;
}

static const struct iio_buffer_setup_ops my_iio_buffer_ops = {
    .buffer_postenable = my_iio_device_buffer_postenable,
    .buffer_predisable = my_iio_device_buffer_predisable,
};

static const struct iio_info my_iio_info_with_buffer = {
    .driver_module = THIS_MODULE,
    .buffer_setup_ops = &my_iio_buffer_ops,
};

6. 注册 IIO 设备

设备初始化完成后,需要调用 iio_device_register() 来注册 IIO 设备。这个过程将设备与 IIO 子系统连接,开始接收数据并与用户空间交互。

ret = iio_device_register(indio_dev);
if (ret) {
    pr_err("IIO device registration failed\n");
    return ret;
}

7. 与用户空间交互

IIO 驱动通常会通过 sysfs、ioctl 或直接的设备文件接口与用户空间进行交互。通过 sysfs,用户可以查询设备状态、控制缓冲区等。

static ssize_t iio_device_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct iio_dev *indio_dev = iio_device_get(dev);
    bool enabled = iio_buffer_enabled(indio_dev);
    return snprintf(buf, PAGE_SIZE, "%d\n", enabled);
}

static ssize_t iio_device_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
{
    struct iio_dev *indio_dev = iio_device_get(dev);
    bool enable;
    
    if (kstrtobool(buf, &enable))
        return -EINVAL;

    if (enable)
        iio_buffer_enable(indio_dev);
    else
        iio_buffer_disable(indio_dev);

    return len;
}

static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, iio_device_enable_show, iio_device_enable_store);

8. 清理和卸载

当设备被卸载时,你需要正确地移除 IIO 设备、释放资源并停止缓冲区。

static void my_iio_device_remove(struct platform_device *pdev)
{
    struct iio_dev *indio_dev = platform_get_drvdata(pdev);
    iio_device_unregister(indio_dev);
}

9.总结

IIO 驱动的开发流程涉及几个关键步骤:

  1. 理解硬件特性:包括传感器类型、接口、数据采集方式等。
  2. 初始化 IIO 设备结构:定义 iio_dev 结构,并与硬件交互。
  3. 定义操作函数:如 read_rawwrite_raw,以及缓冲区操作函数。
  4. 配置设备的通道:定义和注册 IIO 设备的各个通道。
  5. 缓冲区管理:根据需要启用数据缓冲区。
  6. 与用户空间交互:通过 sysfs 或设备文件与用户空间通信。
  7. 卸载清理:设备移除时进行资源释放。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值