ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux IIO子系统

Linux IIO子系统 因实际项目资料涉密在此将项目中Linux common的部分和学习心得总结于此以备时习之。部分示例代码为脱敏经过删减和简化处理仅供参考请见谅参考图来源于网络侵删。keywordIIO、Industrial I/O、ADC、DAC一.background工业及消费电子手机、物联网、可穿戴设备中大量使用ADC将模拟物理量转为数字量。加速度计、光传感器、陀螺仪、气压计、磁力计等许多传感器内部均集成ADC并通过IIC/SPI接口对外提供数据。同时某些SoC也提供on chip的ADC pin。为统一管理这类ADC传感器Linux内核推出了IIO子系统开发者需基于IIO子系统编写驱动。二.简介IIOIndustrial I/O工业输入输出是Linux内核中专用于管理模数转换器ADC和数模转换器DAC类设备的子系统。它于2009年由Jonathan Cameron加入Linux内核旨在为各类传感器和数据转换设备提供统一的驱动框架。当你使用的传感器本质是 ADC 或 DAC 器件时可以优先考虑使用 IIO 驱动框架。1.iio_dev 结构体IIO 子系统使用结构体 iio_dev 来描述一个具体 IIO 设备此设备结构体定义在include/linux/iio/iio.h 文件中使用IIO子系统的外设需要根据该结构体的成员进行注册。struct iio_dev { int modes; /* 设备支持的工作模式 */ int currentmode; /* 当前使用的工作模式 */ struct device dev; /* Linux 设备对象 */ struct iio_buffer *buffer; /* 数据缓冲区 */ int scan_bytes; /* 单次采集的总字节数 */ const unsigned long *available_scan_masks; /* 允许的通道组合掩码列表 */ const unsigned long *active_scan_mask; /* 当前启用的通道掩码 */ bool scan_timestamp; /* 是否在数据包末尾附加时间戳 */ struct iio_trigger *trig; /* 当前绑定的触发器 */ struct iio_poll_func *pollfunc; /* 触发信号到来时的回调函数 */ struct iio_chan_spec const *channels; /* 通道配置数组 */ int num_channels; /* 通道总数 */ const char *name; /* 设备名称用户空间可见 */ const struct iio_info *info; /* 驱动功能回调函数集 */ const struct iio_buffer_setup_ops *setup_ops; /* 缓冲启用/禁用钩子 */ struct cdev chrdev; /* 字符设备 */ };2.iio_dev 申请与释放在使用之前要先申请 iio_dev申请函数为 iio_device_alloc函数原型如下struct iio_dev *iio_device_alloc(int sizeof_priv)sizeof_priv私有数据内存空间大小一般我们会将自己定义的设备结构体变量作为 iio_dev 的私有数据这样可以直接通过 iio_device_alloc 函数同时完成 iio_dev 和设备结构体变量的内存申请。申请成功以后使用 iio_priv 函数来得到自定义的设备结构体变量首地址。原生IIO使用的是iio_device_alloc这种方式方式分配的设备对象需要手动释放调用iio_device_free()通常在驱动remove或probe出错时进行释放。我们也可以使用devmdevice managed机制设备驱动无需手动释放资源会在设备销毁时自动清理可有效防止内存泄漏。struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv);此函数采用“一次分配两块空间”的设计在连续的内存中同时分配了struct iio_dev 和驱动私有数据private data的空间这种设计能提升 CPU 缓存的利用率。返回值如果申请成功就返回一个指向已分配 iio_dev结构体的指针如果失败就返回 NULL。3.iio_info在 iio_dev 结构体中有一个关键成员指针 info它指向 iio_info 结构体。iio_info 是 IIO 驱动开发中需要重点实现的部分因为它定义了设备的具体操作接口。用户空间发起的各种访问请求最终都会通过 VFS 层回调到 iio_info 所注册的函数中从而实现对硬件设备的控制与数据采集。iio_info 结构体的定义位于内核源码 include/linux/iio/iio.h中。struct iio_info { struct module *driver_module; const struct attribute_group *attrs; const struct attribute_group *event_attrs; int (*read_raw)(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask); int (*write_raw)(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask); int (*write_raw_get_fmt)(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask); int (*read_event_config)(struct iio_dev *indio_dev, u64 event_code); int (*write_event_config)(struct iio_dev *indio_dev, u64 event_code, int state); int (*read_event_value)(struct iio_dev *indio_dev, u64 event_code, int *val); int (*write_event_value)(struct iio_dev *indio_dev, u64 event_code, int val); int (*validate_trigger)(struct iio_dev *indio_dev, struct iio_trigger *trig); int (*update_scan_mode)(struct iio_dev *indio_dev, const unsigned long *scan_mask); int (*debugfs_reg_access)(struct iio_dev *indio_dev, unsigned reg, unsigned writeval, unsigned *readval); int (*fwnode_xlate)(struct iio_dev *indio_dev, const struct fwnode_reference_args *args); int (*hwfifo_set_watermark)(struct iio_dev *indio_dev, unsigned int val); };4.iio_chan_specIIO 子系统的核心抽象是**通道**它代表传感器设备的一个独立数据采集维度。一个物理传感器可能包含多个功能单元产生多路相互独立但有内在关联的数据。例如- 三轴加速度计包含 X、Y、Z 三个通道- 8 通道 ADC 芯片提供 8 个模拟输入通道- 温湿度传感器通常包含温度temperature和湿度humidity两个通道。在 Linux 内核中每个通道由 struct iio_chan_spec 结构体描述。该结构体定义了通道的类型、编号、数据类型、是否可读写以及数值转换关系等关键属性。其定义位于内核源码 include/linux/iio/iio.h 文件中。驱动开发者在实现 IIO 驱动时需要为设备的所有通道构造一个 iio_chan_spec 数组并将其赋值给 iio_dev 结构体的 channels 成员同时通过 num_channels 指定通道数量。内核和用户空间将依据这些通道定义来解析、展示和访问多路传感器数据。三.IIO framework (platform)IIO 框架主要用于 ADC 类的传感器比如陀螺仪、加速度计、磁力计、光强度计等这些传感器基本都是 IIC 或者 SPI 接口的。因此IIO 驱动的基础框架就是 IIC 或者 SPI我们可以在 IIC 或 SPI 驱动里面在加上 regmap。当然了有些 SOC 内部的 ADC 也会使用 IIO 框架那么这个时候驱动的基础框架就是 platfrom为使用dev-bus-drv模型MTK的某些STB SoC就是如此。​1.iio_dev 结构体/* 自定义设备结构体 */ struct xxx_dev { struct device *dev; struct iio_dev *indio_dev; void __iomem *xxx int irq; ... };2.通道数组static const struct iio_chan_spec xxx_channels[] { };3.读函数当读取 sysfs 中的文件的时候最终此函数会执行此函数里面会从传感器里面读取各种数据然后上传给应用。static int xxx_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { return 0; }4.写函数static int xxx_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) { return 0; }5.iio_info 结构体变量static const struct iio_info xxx_info { .read_raw xxx_read_raw, .write_raw xxx_write_raw, .write_raw_get_fmt xxx_write_raw_get_fmt, .write_event_value xxx_write_event_value, .read_event_value xxx_read_event_value, .write_event_config xxx_write_event_config, .read_event_config xxx_read_event_config, };6.driver probe func按需实现在dev与drv match成功后调用static int xxx_probe(struct platform_device *pdev) { }7.driver remove func按需实现在设备卸载时调用static int xxx_remove(struct platform_device *pdev) { }
返回列表