IIO介绍
IIO 全称是 Industrial I/O,翻译过来就是工业 I/O,大家不要看到“工业”两个字就觉得 IIO是只用于工业领域的。大家一般在搜索 IIO 子系统的时候,会发现大多数讲的都是 ADC,这是因为 IIO 就是为 ADC 类传感器准备的,当然了 DAC 也是可以的。
1、 iio_dev 结构体
IIO 子系统使用结构体 iio_dev 来描述一个具体 IIO 设备,此设备结构体定义在include/linux/iio/iio.h 文件中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 474 struct iio_dev { 475 int id; 476 477 int modes;
478 int currentmode; 479 struct device dev; 480 481 struct iio_event_interface *event_interface; 482 483 struct iio_buffer *buffer; 484 struct list_head buffer_list; 485 int scan_bytes; 486 struct mutex mlock; 487 488 const unsigned long *available_scan_masks; 489 unsigned masklength; 490 const unsigned long *active_scan_mask; 491 bool scan_timestamp; 492 unsigned scan_index_timestamp; 493 struct iio_trigger *trig; 494 struct iio_poll_func *pollfunc; 495 496 struct iio_chan_spec const *channels; 497 int num_channels; 498 499 struct list_head channel_attr_list; 500 struct attribute_group chan_attr_group; 501 const char *name; 502 const struct iio_info *info; 503 struct mutex info_exist_lock; 504 const struct iio_buffer_setup_ops *setup_ops; 505 struct cdev chrdev; ...... 515 };
|
操作集合iio_buffer_setup_ops
第 504 行, 为 iio_buffer_setup_ops 结构体类型,内容如下:
1 2 3 4 5 6 7 8
| 427 struct iio_buffer_setup_ops { 428 int (*preenable)(struct iio_dev *); 429 int (*postenable)(struct iio_dev *); 430 int (*predisable)(struct iio_dev *); 431 int (*postdisable)(struct iio_dev *); 432 bool (*validate_scan_mask)(struct iio_dev *indio_dev, 433 const unsigned long *scan_mask); 434 };
|
iio_dev 申请与释放
在使用之前要先申请 iio_dev,申请函数为 iio_device_alloc,函数原型如下:
1
| struct iio_dev *iio_device_alloc(int sizeof_priv)
|
sizeof_priv: 私有数据内存空间大小,一般我们会将自己定义的设备结构体变量作为 iio_dev的私有数据,这样可以直接通过 iio_device_alloc 函数同时完成 iio_dev 和设备结构体变量的内存申请。 申请成功以后使用 iio_priv 函数来得到自定义的设备结构体变量首地址。
返回值:如果申请成功就返回 iio_dev 首地址,如果失败就返回 NULL。
一般 iio_device_alloc 和 iio_priv 之间的配合使用如下所示:
1 2 3 4 5 6 7 8 9 10
| struct icm20608_dev *dev; struct iio_dev *indio_dev;
indio_dev = iio_device_alloc(sizeof(*dev)); if (!indio_dev) return -ENOMEM;
c dev = iio_priv(indio_dev);
|
如果要释放 iio_dev,需要使用 iio_device_free 函数,函数原型如下:
1
| void iio_device_free(struct iio_dev *indio_dev)
|
iio_dev 注册与注销
前面分配好 iio_dev 以后就要初始化各种成员变量,初始化完成以后就需要将 iio_dev 注册到内核中,需要用到 iio_device_register 函数,函数原型如下:
1
| int iio_device_register(struct iio_dev *indio_dev)
|
返回值: 0,成功;其他值,失败。
如果要注销 iio_dev 使用 iio_device_unregister 函数,函数原型如下:
1
| void iio_device_unregister(struct iio_dev *indio_dev)
|
返回值: 0,成功;其他值,失败
iio_info
iio_dev 有个成员变量: info,为 iio_info 结构体指针变量,这个是我们在编写 IIO 驱动的时候需要着重去实现的,因为用户空间对设备的具体操作最终都会反映到 iio_info 里面。 iio_info结构体定义在 include/linux/iio/iio.h 中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 352 struct iio_info { 353 struct module *driver_module; 354 struct attribute_group *event_attrs; 355 const struct attribute_group *attrs; 356 357 int (*read_raw)(struct iio_dev *indio_dev, 358 struct iio_chan_spec const *chan, 359 int *val, 360 int *val2, 361 long mask); ...... 369 370 int (*write_raw)(struct iio_dev *indio_dev, 371 struct iio_chan_spec const *chan, 372 int val, 373 int val2, 374 long mask); 375 376 int (*write_raw_get_fmt)(struct iio_dev *indio_dev, 377 struct iio_chan_spec const *chan, 378 long mask); ...... 415 };
|
Linux 内核使用 iio_chan_spec 结构体来描述通道,定义在 include/linux/iio/iio.h 文件中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 223 struct iio_chan_spec { 224 enum iio_chan_type type; 225 int channel; 226 int channel2; 227 unsigned long address; 228 int scan_index; 229 struct { 230 char sign; 231 u8 realbits; 232 u8 storagebits; 233 u8 shift; 234 u8 repeat; 235 enum iio_endian endianness; 236 } scan_type; 237 long info_mask_separate; 238 long info_mask_shared_by_type; 239 long info_mask_shared_by_dir; 240 long info_mask_shared_by_all; 241 const struct iio_event_spec *event_spec; 242 unsigned int num_event_specs; 243 const struct iio_chan_spec_ext_info *ext_info; 244 const char *extend_name; 245 const char *datasheet_name; 246 unsigned modified:1; 247 unsigned indexed:1; 248 unsigned output:1; 249 unsigned differential:1; 250 };
|
来看一下 iio_chan_spec 结构体中一些比较重要的成员变量:
第 224 行, type 为通道类型, iio_chan_type 是一个枚举类型,列举出了可以选择的通道类型,定义在 include/uapi/linux/iio/types.h 文件里面,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 13 enum iio_chan_type { 14 IIO_VOLTAGE, 15 IIO_CURRENT, 16 IIO_POWER, 17 IIO_ACCEL, 18 IIO_ANGL_VEL, 19 IIO_MAGN, 20 IIO_LIGHT, 21 IIO_INTENSITY, 22 IIO_PROXIMITY, 23 IIO_TEMP, 24 IIO_INCLI, 25 IIO_ROT, 26 IIO_ANGL, 27 IIO_TIMESTAMP, 28 IIO_CAPACITANCE, 29 IIO_ALTVOLTAGE, 30 IIO_CCT, 31 IIO_PRESSURE, 32 IIO_HUMIDITYRELATIVE, 33 IIO_ACTIVITY, 34 IIO_STEPS, 35 IIO_ENERGY, 36 IIO_DISTANCE, 37 IIO_VELOCITY, 38 };
|
目前 Linux 内核支持的传感器类型非常丰富,而且支持类型也会不断的增加。如果是 ADC,那就是 IIO_VOLTAGE 类型。如果是 ICM20608 这样的多轴传感器,那么就是复合类型了,陀螺仪部分是 IIO_ANGL_VEL 类型,加速度计部分是IIO_ACCEL 类型,温度部分是IIO_TEMP。
设备驱动框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
| struct xxx_dev { struct spi_device *spi; struct regmap *regmap; struct regmap_config regmap_config; struct mutex lock; };
static const struct iio_chan_spec xxx_channels[] = {
};
static int xxx_read_raw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int *val, int *val2, long mask) { return 0; }
static int xxx_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { return 0; }
static int xxx_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) { return 0; }
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, };
static int xxx_probe(struct spi_device *spi) { int ret; struct xxx_dev *data; struct iio_dev *indio_dev;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM;
data = iio_priv(indio_dev); data->spi = spi; spi_set_drvdata(spi, indio_dev); mutex_init(&data->lock);
indio_dev->dev.parent = &spi->dev; indio_dev->info = &xxx_info; indio_dev->name = "xxx"; indio_dev->modes = INDIO_DIRECT_MODE;
return 0; }
static int xxx_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct xxx_dev *data; data = iio_priv(indio_dev); ;
iio_device_unregister(indio_dev);
return 0; }
|