Linux内核框架之IIO子系统详解

文章目录

1、preface

1)资料快车

1)Linux设备驱动之IIO子系统--IIO框架

https://www.cnblogs.com/yongleili717/p/10744252.html

2)Linux下IIO子系统驱动

https://www.cnblogs.com/fuzidage/p/18299137

3)Linux下IIO子系统的使用和源码分析

https://blog.csdn.net/tang_vincent/article/details/147337470

2)概述

1、IIO基本概念

1)(Industrial Input/Output 工业输入/输出),专门处理ADC、DAC以及其它工业传感器(加速度计、陀螺仪、磁力计);

2)与input子系统的关系? 框架上比较相近,应用场景不同,另外可以组合使用!

2、IIO子系统解决什么问题?
复制代码
1.用户在固定目录找到自己的设备节点,使用Lib定义的接口获取IIO设备的数据,driver提供给IIO预定义的接口;

2.设备种类繁多,操作也各不相同
1)数据类型 - 单字节、buffer;
2)获取方式 - 单次、连续获取等;
3)通信方式 - I2C\SPI等;
IIO子系统都要实现它们!

3.hal层实现读写算法逻辑,driver提供功能接口

总的来说三点:

复制代码
1)IIO子系统向用户层提供统一的接口:IIO提供了一个标准化的用户API(/sys/bus/iio/devices/*) 和 统一的数据结构(类似parcel);

2)IIO内核能够支持多种设备类型 (接口和数据结构 能 覆盖常用的传感器外设);

3)IIO子系统支持事件和触发机制 (使用对应的API),比如当某个传感器的值超过阈值时触发中断 (支持硬件和软件触发);

2、IIO架构

1)架构1

《Linux Device Drivers Development》--John Madieu

2)架构2

1、重点理解IIO Trigger/IIO Buffer/IIO Event,其它都是通用的Linux机制;

2、IIO device driver一般使用其他子系统 + 调用iio core接口实现;

3)buffer和trigger机制

复制代码
IIO 缓冲区架构:

用户空间                    内核空间
    │                         │
    │  read() / char device   │
    │◄────────────────────────►│
    │                         │
    │    ┌─────────────┐      │
    │    │  /dev/iio:deviceN  │
    │    │  (字符设备)  │      │
    │    └──────┬──────┘      │
    │           │              │
    │           ▼              │
    │    ┌─────────────┐       │
    │    │   iio_buffer   │     │
    │    │  (环形缓冲区)  │     │
    │    │              │      │
    │    │  ┌───┐┌───┐┌───┐   │
    │    │  │scan│scan│scan│...│  ← 扫描元素
    │    │  └───┘└───┘└───┘   │
    │    │              │      │
    │    │  触发源: 中断/timer  │
    │    └─────────────┘       │
    │           │              │
    │           ▼              │
    │    ┌─────────────┐       │
    │    │   设备驱动    │      │
    │    │ (accel/adc/...)│     │
    │    └─────────────┘       │
    │                         │
    
触发模式:
├─ 软件触发:用户空间 write /sys/.../trigger_now
├─ 硬件触发:外部中断、定时器、其他传感器
└─ 连续模式:定时器周期性采样

4)用户空间接口

复制代码
1、以mpu6050为例
sysfs 标准路径:
/sys/bus/iio/devices/iio:deviceN/

通用属性:
├─ name              # 设备名称
├─ dev               # 主次设备号
├─ sampling_frequency # 采样频率
├─ buffer/           # 缓冲区控制
│   ├─ enable        # 使能缓冲区
│   ├─ length        # 缓冲区长度
│   └─ watermark     # 水印级别
├─ scan_elements/    # 扫描元素配置
│   ├─ in_voltage0_en      # 使能通道0
│   ├─ in_voltage1_en      # 使能通道1
│   ├─ in_voltage0_type    # 数据类型
│   └─ in_voltage0_index   # 扫描索引
└─ trigger/          # 触发器
    ├─ current_trigger     # 当前触发器
    └─ ...

通道数据读取:
/sys/bus/iio/devices/iio:device0/
├─ in_accel_x_raw    # X轴原始数据 (accel)
├─ in_accel_y_raw    # Y轴原始数据
├─ in_accel_z_raw    # Z轴原始数据
├─ in_accel_scale    # 缩放因子 (g/LSB)
├─ in_anglvel_x_raw  # X轴角速度 (gyro)
├─ in_magn_x_raw     # X轴磁场 (magnetometer)
├─ in_temp_raw       # 温度原始值
├─ in_voltage0_raw   # ADC 通道0原始值
└─ ...


# 1. 查找 IIO 设备
ls /sys/bus/iio/devices/
# iio:device0  iio:device1  iio_sysfs_trigger

# 2. 查看设备信息
cat /sys/bus/iio/devices/iio:device0/name
# mpu6050

# 3. 读取加速度数据
cat /sys/bus/iio/devices/iio:device0/in_accel_x_raw
cat /sys/bus/iio/devices/iio:device0/in_accel_y_raw
cat /sys/bus/iio/devices/iio:device0/in_accel_z_raw

# 4. 读取缩放因子
cat /sys/bus/iio/devices/iio:device0/in_accel_scale
# 0.000598 (约 0.000598 * 9.8 = 0.00586 m/s²/LSB)

# 5. 计算实际值
# 实际加速度(g) = raw * scale
# 实际加速度(m/s²) = raw * scale * 9.8

# 6. 缓冲区模式 (高速采集)
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_x_en
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_y_en
echo 1 > /sys/bus/iio/devices/iio:device0/scan_elements/in_accel_z_en
echo 100 > /sys/bus/iio/devices/iio:device0/buffer/length
echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable

# 7. 读取缓冲数据
cat /dev/iio:device0 | xxd  # 二进制数据

3、源码分析

1、源码目录

1.IIO core
复制代码
1.源码
1)
/kernel/drivers/iio/*
/kernel/drivers/iio/buffer/*  -- buffer缓冲区框架
/kernel/drivers/iio/trigger/* -- 触发器


/kernel/drivers/iio/industrialio-*
/kernel/drivers/iio/industrialio-core.c  --核心层

2)
/kernel/drivers/iio/common/* --常见厂商sensors驱动模板库(直接使用或二次简单改造) - 比如st_sensors
ST 通用库方式(代码复用)
common/st_sensors/ (共享库)              
│  ├─ st_sensors_i2c.c    ← I2C 读写封装  
│  ├─ st_sensors_spi.c    ← SPI 读写封装  
│  ├─ st_sensors_core.c   ← 寄存器操作
│  │   ├─ 设备识别 (WHO_AM_I)             
│  │   ├─ 量程配置 (FS)                   
│  │   ├─ 输出速率 (ODR)                  
│  │   ├─ 数据读取                        
│  │   └─ 电源管理                        
│  ├─ st_sensors_trigger.c ← 中断触发器    
│  └─ st_sensors_buffer.c  ← 批量采集缓冲 

3)
/kernel/drivers/iio/"传感器"  -- 各式各样的传感器

2.头文件
/kernel/include/linux/iio/*
2.纷繁复杂的传感器
目录 全称 用途 典型设备
accel Accelerometer 加速度计 MPU6050、BMI160、LIS3DH
adc Analog-to-Digital Converter 模数转换器 ADS1015、SAR-ADC (RK3568)
afe Analog Front End 模拟前端 信号调理电路
amplifiers Amplifiers 放大器 仪表放大器、可编程增益
buffer Buffer 缓冲区框架 IIO 数据缓冲核心代码
chemical Chemical Sensors 化学传感器 气体传感器、PH 传感器
common Common Code 通用代码 所有 IIO 驱动共享的代码
counter Counter 计数器 脉冲计数、频率计数
dac Digital-to-Analog Converter 数模转换器 DAC0832、PWM-DAC
dummy Dummy Driver 虚拟/测试驱动 用于测试框架,无真实硬件
frequency Frequency 频率相关 时钟、频率计
gyro Gyroscope 陀螺仪 MPU6050、BMI160
health Health Sensors 健康传感器 心率、血氧、生物传感器
humidity Humidity 湿度传感器 DHT11、SHT30、BME280
imu Inertial Measurement Unit 惯性测量单元 9轴/10轴传感器(accel+gyro+mag)
light Light 光线传感器 BH1750、AP3216C、TSL2561
magnetometer Magnetometer 磁力计 HMC5883L、AK09911、QMC5883
multiplexer Multiplexer 多路复用器 模拟开关、通道切换
orientation Orientation 方向传感器 电子罗盘、倾角传感器
potentiometer Potentiometer 电位器/数字电位器 AD5272、数字可调电阻
potentiostat Potentiostat 恒电位仪 电化学分析仪器
pressure Pressure 压力传感器 BMP280、MS5611、气压计
proximity Proximity 接近传感器 AP3216C、红外接近检测
resolver Resolver 旋转变压器 电机角度传感器(工业)
temperature Temperature 温度传感器 LM75、TMP102、NTC 热敏电阻

2、数据结构

复制代码
1.ii0_dev - iio设备(基类)
/kernel/include/linux/iio/iio.h
struct iio_dev {
    int modes; //Device operating modes
    struct iio_event_interface *event_interface; //事件接口
    struct iio_buffer *buffer; //iio缓冲区
    struct iio_head buffer_list;
    int scan_bytes;
    struct iio_triger *trig;  //iio触发器
    struct iio_poll_func *pollfunc;  //事件查询
    struct iio_poll_func *pollfunc_event;
    struct iio_chan_spec const *channels;  //传感器通道
    int num_channels;
    struct list_head channel_attr_list;
    struct attribute_group chan_attr_group;
    struct iio_info *info;  //constant information about device - 属性group和读写接口
    struct iio_buffer_setup_ops *setup_ops;
    struct cdev chrdev;
    struct dentry *debugfs_entry;
};

1) 设备收集数据方式
/kernel/include/linux/iio/iio.h
/* Device operating modes */ 
#define INDIO_DIRECT_MODE		0x01  //表示设备提供sysfs,用户手动触发收集数据(单次采集)
#define INDIO_BUFFER_TRIGGERED		0x02  //表示设备采用自动持续收集数据,并填充到buffer
#define INDIO_BUFFER_SOFTWARE		0x04  //采用软件FIFO,buffer方式管理采样,要连续数据流但无硬件FIFO场合使用
#define INDIO_BUFFER_HARDWARE		0x08  //设备自身具有FIFO,以硬件buffer方式管理采样
#define INDIO_EVENT_TRIGGERED		0x10  //IIO事件机制(阈值越界、状态变化等),上报"事件"而不是持续样本流
#define INDIO_HARDWARE_TRIGGERED	0x20  //硬件信号直接触发采样,常与INDIO_BUFFER_TRIGGERED联用,时序要求高场合使用

2.iio_event_interface -- chrdev interface for an event line
/kernel/drivers/iio/industrialio-event.c
struct iio_event_interface {
    wait_queue_head_t wait;  //等待队列
    DECLARE_KFIFO(det_events, struct iio_event_data, 16); //声明一个fifo结构存放iio event数据
    struct list_head dev_attr_list;
    struct attribute_group group;
};

3.iio_buffer - 缓冲区
/kernel/include/linux/iio/buffer_impl.h
struct iio_buffer {
    unsigned int length; //缓存区数据单元数量
    size_t bytes_per_datum; //单个数据单元大小
    struct iio_buffer_access_funcs *access; //读写函数
};

4.iio_buffer_access_funcs
/kernel/include/linux/iio/buffer_impl.h
struct iio_buffer_access_funcs {
	int (*store_to)(struct iio_buffer *buffer, const void *data);
	int (*read_first_n)(struct iio_buffer *buffer, size_t n, char __user *buf);
}

5.iio_trigger - 触发器
/kernel/include/linux/iio/trigger.h
struct trigger {
   struct iio_tigger_Ops *ops;
   char *name;
   struct irq_chip subirq_chip;  //中断
   int subirq_base;
   struct iio_subirq subirqs[2];
   unsigned long pool[]
};

6.iio_info - iio属性及操作集
/kernel/include/linux/iio/iio.h
struct iio_info {
    struct attribute_group *event_attrs;
    struct attribute_group *attrs;
    int (*read_raw)(struct iio_dev, struct iio_chan_spec, int *val, int *val2,.);
    int (*read_raw_mulit)();
    int (*read_avail)();
    int (*write_raw)();
    int (*write_raw_get_fmt)();
    int (*read_event_config)(struct iio_dev,struct iio_chan_spec, enum iio_event_type,enum iio_event_direction);
    int (*write_event_config)();
    int (*read_event_value)();
    int (*write_event_value)();
};

7.iio_buffer_setup_ops
/kernel/include/linux/iio/iio.h
struct iio_buffer_setup_ops {
    int (*preenable)(struct iio_dev *);
}


8.iio_dev_opaque
/kernel/include/linux/iio/iio-opaque.h
struct iio_dev_opaque {
    struct iio_dev indio_dev;
    int id;
    struct module *driver_module;
    struct mutex info_exist_lock;
    
    struct iio_event_interface	*event_interface;
    struct list_head buffer_list;
    struct list_head channel_attr_list;
    struct attribute_group		chan_attr_group;
    struct list_head ioctl_handlers;
    
    struct cdev			chrdev;
}

3、关键接口

复制代码
1.iio_push_to_buffers_with_timestamp - int数据存放在buffer中
/kernel/include/linux/iio/buffer.h
iio_push_to_buffers_with_timestamp(str5uct iio_dev *indio_dev, void *data, int64_t timestamp)
/android/common/common14-5.15/common/drivers/iio/industrialio-buffer.c
--iio_push_to_buffers()
----iio_push_to_buffer()
------buffer->access->store_to(buffer, dataout);


2.devm_iio_kfifo_allocate  - 分配buffer
/kernel/include/linux/iio/buffer/kfifo_buf.c
devm_iio_kfifo_allocate(struct device *dev)
--devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL);
--iio_kfifo_allocate();
----kzalloc(sizeof(struct iio_kfifo), GFP_KERNEL);

3.iio_device_attach_buffer
/kernel/include/linux/iio/industrialio-buffer.c
iio_device_attach_buffer(struct iio_dev *indio_dev, struct iio_buffer *buffer){
    new = krealloc(old, sizeof(*new) * cnt, GFP_KERNEL);
    buffer = iio_buffer_get(buffer);
    iio_dev_opaque->attached_buffers[cnt - 1] = buffer;
    iio_dev_opaque->attached_buffers_cnt = cnt;
}

4、IIO框架之saradc

1)preface
复制代码
1、saradc调用IIO core的接口进行初始化和注册,通过简单的rockchip saradc来看IIO子系统
2、重点框架
1)IIO框架之register
2)IIO框架之fops/匿名文件句柄
3)IIO框架之event
4)IIO框架之复杂的sysfs组织
5)IIO框架之triger
2)probe
复制代码
1.IIO probe
/kernel/drivers/iio/adc/rockchip_saradc.c
rockchip_saradc_probe(struct platform_device *pdev)
1) 定义iio设备、传感器设备数据结构
--struct rockchip_saradc *info
--struct iio_dev *indio_dev
--indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
--info = iio_priv(indio_dev);
2)匹配设备树并 获取设备资源(ADC平台定义)
match = of_match_device(rockchip_saradc_match, &pdev->dev);
info->data = match->data;
info->regs = devm_ioremap_resource(&pdev->dev, mem);

2) 申请中断-ADC/DAC转换完成后硬件触发中断
--devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr, 0, dev_name(&pdev->dev),info);
3) 构造indio
--plaform_set_drvdata(pdev, indio_dev);
--indio_dev->name = dev_name(&pdev->dev);
--indio_dev->info = &rockchip_saradc_iio_info; //属性和操作集
--indio_dev->modes = INDIO_DIRECT_MODE;  // 设置工作模式为直接模式
--indio_dev->channels = info->data->channels;  //ADC通道
--indio_dev->num_channels = info->data->num_channels;
4) 注册iio
--devm_iio_device_register(&pdev->dev, indio_dev);


2.
iio读实现
static const struct iio_info rockchip_saradc_iio_info = {
	.read_raw = rockchip_saradc_read_raw,
};

iio通道定义 - ADC的详细定义
struct iio_chan_spec rockchip_saradc_iio_channels[] = {
	ADC_CHANNEL(0, "adc0"),
	ADC_CHANNEL(1, "adc1"),
	ADC_CHANNEL(2, "adc2"),
};


3.devm_iio_device_register - 根据设备驱动中给的信息进行构造sysfs、event、trigger等等
/kernel/drivers/iio/industrialio-core.c
__devm_iio_device_register((dev), (indio_dev), THIS_MODULE)
--devres_alloc(struct iio_dev)
--__iio_device_register(indio_dev, this_mod);
----iio_device_register_debugfs(indio_dev); //创建debugfs
----iio_device_register_sysfs(indio_dev); //根据iio info创建/sys/bus/iio/devices/iio:deviceN/*各路sysfs属性
----iio_device_register_eventset(indio_dev); //IIO事件,常见事件包括阈值事件、数据就绪事件、状态变化事件等等
//IIO 子系统利用 FIFO 队列缓存检测到的事件数据,等待队列实现事件通知机制以唤醒监听者,通过链表动态维护事件相关属性并通过 sysfs 暴露给用户空间以便配置和监控
------INIT_LIST_HEAD(&indio_dev->event_interface->dev_attr_list);
------iio_setup_ev_int(struct iio_event_interface); //初始化等待队列头
----iio_device_register_trigger_consumer(indio_dev); //触发器,函数注册触发消费者,支持设备响应外部触发信号
----indio_dev->setup_ops = &noop_ring_setup_ops;//提供默认的缓冲区操作函数
----cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);  //创建字符设备
----cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
--devres_add(dev, ptr);
3)fops/匿名文件句柄
复制代码
1.匿名文件(anon_inode_getfd)
1)匿名文件描述符它是一种特殊的文件描述符,它并不与实际的文件系统路径或设备节点关联,而是通过内核动态创建的一种虚拟文件。匿名的文件描述符 fd 也有着他自己的文件操作集,所以我们可以通过 ioctl 创建虚拟文件描述符,从而为用户提供额外的系统调用接口,与内核中的某些功能或数据进行交互,而无需依赖传统的文件系统;
2)总的来说,匿名的文件句柄,实现了两级ioctl,并且不会污染文件系统的效果;

2.顶层file ops
struct file_operations iio_buffer_fileops = {
    .read = iio_buffer_read_first_n_outer_addr,  //读,在probe时挂接进来
    .open = iio_chrdev_open,
    .compat_ioctl = iio_ctrl,
};


3.iio_buffer_read_frist_n_outer_addr
/kernel/drivers/iio/industrialio-buffer.c
iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, size_t n, loff_t *f_ps)
--DEFINE_WAIT_FUNC(wait, woken_wake_function); //定义一个等待队列的等待函数,用于处理阻塞读取
--add_wait_queue(&rb->pollq, &wait);
--iio_buffer_ready(); 等待数据就绪
--wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
--rb->access->read_first_n(rb, n, buf); //读取数据放入Buffer

3.iio_ctrl
/kernel/drivers/iio/industrialio-core.c
iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/kernel/drivers/iio/industrialio-event.c
--iio_event_getfs(indio_dev);
----struct iio_event_interface *ev_int = indio_dev->event_interface;
----anon_inode_getfd("iio:event", &iio_event_chrdev_fileops, indio_dev,..)
--copy_to_user()

4.匿名file ops
static const struct file_operations iio_event_chrdev_fileops = {
	.read =  iio_event_chrdev_read,
	.poll =  iio_event_poll,
};

1) iio_event_chrdev_read() -阻塞IO
--do while循环
----kfifo_is_empty(iio_event_interface->det_events);
----wait_event_interruptible(iio_event_interface->wait,...);
----kfifo_to_user();

2) iio_event_poll() - 非阻塞IO
/kernel/drivers/iio/industrialio-event.c
iio_event_poll(struct file,strcut poll_table_struct)
--poll_wait(file, &iio_event_interface->wait, struct poll_table_struct)
--kfifo_is_empty(&iio_event_interface->det_events)
4)复杂的iio sysfs
复制代码
1.面对繁杂的传感器设备,sysfs要管理很多属性文件,因此iio sysfs对属性文件进行规范约束,以方便上层统一接口

2.iio_device_register_sysfs - 填充sysfs数据结构内容
/kernel/drivers/iio/industrialio-core.c
iio_device_register_sysfs(struct iio_dev *indio_dev)
--iio_device_add_channel_sysfs(indio_dev, chan); //遍历所有通道并将sysfs属性添加到系统中
按照iio_shared_by属性的共享类型 依次填充info, info会在probe时构造传入
----iio_device_add_info_mask_type(indio_dev, chan, IIO_SEPARATE, &chan->info_mask_separate);
------__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_add_info_mask_type_avail(indio_dev, chan,IIO_SEPARATE, &chan->info_mask_separate_available);

1)iio 属性共享类型
/kernel/include/linux/iio/iio.h
enum iio_shared_by {
	IIO_SEPARATE,//独立属性,仅属于某个特定通道。
	IIO_SHARED_BY_TYPE, //按类型共享,相同类型的通道共享属性
	IIO_SHARED_BY_DIR, //按方向(in 或 out)共享,相同方向的通道共享属性
	IIO_SHARED_BY_ALL //全局共享,整个设备的所有通道共享属性
};

2)规范命名
1、后缀
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",
    ...
}
2、type
static const char * const iio_chan_type_name_spec[] = {
	[IIO_VOLTAGE] = "voltage",
	[IIO_CURRENT] = "current",
	[IIO_POWER] = "power",
	[IIO_ACCEL] = "accel",
	[IIO_ANGL_VEL] = "anglvel",
    ...
}
3、修饰符
static const char * const iio_modifier_names[] = {
	[IIO_MOD_X] = "x",
	[IIO_MOD_Y] = "y",
	[IIO_MOD_Z] = "z",

3. cdev_device_add  - 实施创建
cdev_device_add()
--cdev_add(cdev, dev->devt, 1)
--device_add(dev); // 注册设备对象 dev,使其在 sysfs 中可见,并创建 /dev 设备节点
----device_add_attrs(struct device *dev) //对属性文件进行了创建
5)trigger sysfs
复制代码
1.概况
1)trigger暴露给用户空间接口
2)config需要配置SYSFS_trigger
3)源码
/kernel/drivers/iio/trigger/iio-trig-sysfs.c

2.iio_sysfs_trig_init
/kernel/drivers/iio/trigger/iio-trig-sysfs.c
__init iio_sysfs_trig_init()
--device_initialize(&iio_sysfs_trig_dev);
--dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger");
--device_add(&iio_sysfs_trig_dev);

3.iio_sysfs_trigger_probe
/kernel/drivers/iio/trigger/iio-trig-sysfs.c
iio_sysfs_trigger_probe()
--iio_trigger_alloc("sysfstrig%d", id); //id name
//构造tirg
--t->trig->dev.groups = iio_sysfs_trigger_attr_groups;
--t->trig->ops = &iio_sysfs_trigger_ops;
--t->trig->dev.parent = &iio_sysfs_trig_dev;
//注册
--iio_trigger_register(t->trig);

4.用户空间接口
static struct attribute *iio_sysfs_trig_attrs[] = {
	&dev_attr_add_trigger.attr,  //对应iio_sysfs_trig_add
	&dev_attr_remove_trigger.attr, //对应iio_sysfs_trig_remove
	NULL,
};

5、light sensor IIO场景分析

复制代码
1.IIC设备
/kernel/drivers/iio/light/tsl4531.c
static struct i2c_driver tsl4531_driver = {
    	.probe  = tsl4531_probe,
}

2.tsl4531_probe
tsl4531_probe()
//分配IIO
--devm_iio_device_alloc(&client->dev, sizeof(*data));
--data = iio_priv(indio_dev);
--i2c_set_clientdata(client, indio_dev);
//配置光感传感器
--i2c_smbus_write_byte_data(data->client, TSL4531_CONTROL,
		TSL4531_MODE_NORMAL);
--i2c_smbus_write_byte_data(data->client, TSL4531_CONFIG,
		TSL4531_TCNTRL_400MS);
//构造iio设备
--indio_dev->dev.parent = &client->dev;
  indio_dev->info = &tsl4531_info;
  indio_dev->channels = tsl4531_channels;
  indio_dev->num_channels = ARRAY_SIZE(tsl4531_channels);
  indio_dev->name = TSL4531_DRV_NAME;
  indio_dev->modes = INDIO_DIRECT_MODE;
--iio_device_register(indio_dev);

3.iio info构造
/kernel/drivers/iio/light/tsl4531.c
static const struct iio_info tsl4531_info = {
	.read_raw = tsl4531_read_raw,
	.write_raw = tsl4531_write_raw,
	.attrs = &tsl4531_attribute_group,
};

6、rockchip_adc_key场景分析

复制代码
1.设备树
/kernel/arch/arm64/boot/dts/rockchip/rk3568-evb.dtsi
adc_keys: adc-keys {
	compatible = "adc-keys";
	io-channels = <&saradc 0>;
	io-channel-names = "buttons";
	keyup-threshold-microvolt = <1800000>;
	poll-interval = <100>;

	vol-up-key {
		label = "volume up";
		linux,code = <KEY_VOLUMEUP>;
		press-threshold-microvolt = <1750>;
	};


2.platform平台设备
/kernel/drivers/input/keyboard/adc-keys.c
platform_driver __refdata adc_keys_driver{}

3.adc_keys_probe
--struct adc_keys_state *st;
--enum iio_chan_type type;  //adc类型(电压)
--st->channel = devm_iio_channel_get(dev, "buttons"); //获取设备树信息adc通道信息
--iio_get_channel_type(st->channel &type);
--struct input_polled_dev->poll = adc_keys_poll;  //按键读取方法


1) adc_keys_state
struct adc_keys_state {
    struct iio_channel *channel;
    u32 num_keys;
    u32 last_key;
    u32 keyup_voltage;
    u32 voltage;
    u32 keycode;
};

2) adc_keys_poll - 读取接口
adc_keys_poll
/kernel/drivers/iio/inkern.c
--iio_read_channel_processed(st->channel, &value);
----iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
------chan->indio_dev->info->read_raw(chan->indio_dev,chan->channel, val, val2, info);
--input_report_key(dev->input, st->last_key, 0);
--input_sync(dev->input);