iio子系统框架分析

article/2025/9/25 7:02:45

学习目的

iio子系统主要提供对ADC、DAC相关的设备驱动框架。面向的设备包括:

1.ADC芯片;2.DAC芯片;3.温度传感器;4.光感器;5.陀螺仪;6.加速度传感器;7.CDCs;​8.IMUs;9.压力传感器等

公司以前的sensor驱动以及sensorHAL都是使用input子系统,而纵观其他厂商以及google,都已经不再使用这个框架,iio这个子系统框架才是目前的主流。因此有必要弄清楚这个子系统通路,了解驱动如何实现以及数据如何读取。

全文主要分两个部分,分别是驱动层实现和用户空间接口。

驱动层主要囊括相关的数据结构以及接口,然后以inv_mpu6050驱动为例进行分析。

用户层以节点的使用为主,以android的invensense hal层为例进行分析。

参考博客:

lickylin的专栏_jerry_chg_CSDN博客-Linux 内核,linux 网络,数据结构与算法领域博主

https://blog.csdn.net/u014078917/category_8417773.html

iio子系统驱动层

iio驱动实际属于字符设备驱动的一种,其核心代码位于kernel/drivers/iio目录下。下图是框架图:

驱动框架中,核心部分就是iio_ring、iio_core和iio_trigger这三部分。其中iio_core是整个iio子系统的核心组合,iio_ring是连续数据的buffer核心,iio_trigger是触发器的核心,这三个部分与用户空间交互主要是通过字符设备驱动节点与sysfs,iio_core由于是整个的核心,因此交互部分不能绕过它,而iio_ring的读取主要是通过字符设备驱动节点挂钩,而iio_trigger则是通过sysfs来进行控制。

简介

1.对于连续数据采集相关功能,主要由iio buffer实现(iio_ring); ​ 2.连续数据采集的触发机制,主要由iio trigger实现; ​ 3.iio device的事件触发机制,主要由iio event实现; ​ 4.提供单次原始数据的采集功能,主要通过syfs属性文件实现。

主要的数据结构:

struct iio_dev,描述一个iio device ​ struct iio_event_interface,描述iio device的事件触发模块的数据结构; ​

struct iio_buffer,描述iio device连续数据采集功能相关的数据结构; ​

struct iio_trigger,描述iio device的trigger机制相关的数据结构 ​

struct iio_chan_spec,描述iio device的一个通道的属性信息; ​

struct iio_info,描述iio device各通道的原始数据读取接口、event使能与event参数读写相关接口、trigger有效性检测接口、设备树节点解析等接口; ​

struct iio_buffer_setup_ops,描述iio buffer使能与否的接口(建立iio buffer与iio tigger的关联,从而保证iio trigger触发后,可将数据刷新到对应的iio buffer中); ​

struct iio_chan_spec_ext_info,描述一个channel扩展属性相关的信息,包括属性名称、读写接口等 ​

struct iio_trigger_ops,表示iio trigger的操作接口,包括设置trigger的状态(使能与否)、重新使能trigger、设备有效性判断等接口; ​

struct iio_buffer_access_funcs,描述iio buffer的access接口,包括数据写入到iio buffer的缓存、缓存数据是否有效、从缓存中读取数据等等接口。

数据结构

1.struct iio_dev

描述一个iio device

507 struct iio_dev {
508     int             id;
509
510     int             modes;
511     int             currentmode;
512     struct device           dev;
513
514     struct iio_event_interface  *event_interface;
515
516     struct iio_buffer       *buffer;
517     struct list_head        buffer_list;
518     int             scan_bytes;
519     struct mutex            mlock;
520
521     const unsigned long     *available_scan_masks;
522     unsigned            masklength;
523     const unsigned long     *active_scan_mask;
524     bool                scan_timestamp;
525     unsigned            scan_index_timestamp;
526     struct iio_trigger      *trig;
527     bool                trig_readonly;
528     struct iio_poll_func        *pollfunc;
529     struct iio_poll_func        *pollfunc_event;
530
531     struct iio_chan_spec const  *channels;
532     int             num_channels;
533
534     struct list_head        channel_attr_list;
535     struct attribute_group      chan_attr_group;
536     const char          *name;
537     const struct iio_info       *info;
538     clockid_t           clock_id;
539     struct mutex            info_exist_lock;
540     const struct iio_buffer_setup_ops   *setup_ops;
541     struct cdev         chrdev;
542 #define IIO_MAX_GROUPS 6
543     const struct attribute_group    *groups[IIO_MAX_GROUPS + 1];
544     int             groupcounter;
545
546     unsigned long           flags;
547 #if defined(CONFIG_DEBUG_FS)
548     struct dentry           *debugfs_dentry;
549     unsigned            cached_reg_addr;
550 #endif
551 };

1.modes与currentmode表示该iio device支持的模式以及当前所处的模式,目前支持5种模式

319 #define INDIO_DIRECT_MODE       0x01
320 #define INDIO_BUFFER_TRIGGERED      0x02
321 #define INDIO_BUFFER_SOFTWARE       0x04
322 #define INDIO_BUFFER_HARDWARE       0x08
323 #define INDIO_EVENT_TRIGGERED       0x10

INDIO_DIRECT_MODE表示不对采集数据进行缓存,可直接读取单次的数据(可通过访问sysfs下的属性节点文件读取)。

INDIO_BUFFER_XXX表示支持对iio device采集数据进行缓存的模式,可理解为采集连续的数据(这些数据则需要通过访问字符设备文件进行读取,即通过iio_ring部分)。

INDIO_EVENT_TRIGGERED则主要表示事件触发功能,如针对温度传感器可监控当前是否超过温度告警上限或下限,当出现温度告警后则向SOC发送中断信号,

2.dev主要借助系统的设备驱动模型,实现对iio device的引用计数,并绑定至iio总线上,同时借助设备驱动模型可在sysfs目录下创建该设备的目录和属性文件。

3.event_interface表示event事件相关的数据结构,该数据结构内部包含一个kfifo,存储iio device push的event信息。

4.buffer表示该iio device对应的iio_buffer,针对连续采集数据模式,若不支持,则无需创建。

5.buffer_list则在一个iio buffer enable时,将active iio buffer加入到该链表中(目前基本上只将iio_dev->buffer添加到该链表上)。

6.scan_bytes表示单次采集数据的长度,该值主要根据当前的active channel的个数,每一个通道采集数据的长度计算而得。

7.available_scan_masks表示当前iio device可使用的channel的掩码,如当前有8个channel,仅前4个channel可用,则可以设置available_scan_masks值为0x0F。

8.active_scan_mask则表示当前已使能的channel掩码,这个mask是available_scan_masks的子集,不能超出其范围。

scan_bytes、available_scan_masks、active_scan_mask主要由iio buffer使用,scan_bytes是单次采集数据的长度,因此通过字符设备文件读取buffer采集数据时,传递的内存长度至少应为scan_bytes。

9.scan_timestamp、scan_index_timestamp主要对于通过buffer采集的数据是否需要时间戳,如果需要对采集的数据增加时间戳,则增加IIO_TIMESTAMP类型的虚拟channel,使采集的数据增加时间戳。

10.trig表示一个trigger,针对event、buffer而言,均需要结合trigger机制作为数据采集的信号,一般在trigger中将event信息、数据信息刷新到event的kfifo或者buffer中去。不过目前event信息一般没有使用trigger机制,大多数event信息均是在event irq的中断处理函数中push到event的kfifo中,虽然iio子系统设计上期望通过trigger将数据push到buffer或event上去,但event信息一般不是连续事件,且trigger内部又实现了虚拟的irq chip,而在虚拟irq的中断函数中实现数据push到buffer或者event的kfifo中。显然对于event信息处理而言,若使用trigger机制,则多了一个虚拟中断的触发与处理操作,完全没有必要,因此现有的系统中基本上没有使用trigger机制将event信息push到event kfifo的驱动。

11.pollfunc、pollfunc_event则为buffer、event的中断处理函数(使用的中断即为trigger中的virtual irq chip注册时irq)。

12.channels是该iio device所有channel的相关参数信息,iio_chan_spec中详细说明。

13.channel_attr_list包含了iio系统为所有channel创建的动态属性,类似于hwmon子系统,在sysfs下创建属性文件实现与iio device的通信,而channel_attr_list则主要是channel相关的属性集合。

14.groups中包含了所有的group指针,包括channel、event’buffer子模块创建的group,而在调用device_add将该iio device对应的struct device泪习惯变量注册到驱动模型子系统中,会遍历该数组,创建属性文件或目录。

15.setup_ops则主要是创建buffer与trigger的关联,在该ops中的enable接口中,主要是申请trigger的virtual irq chip提供的中断以及中断处理函数没在disable接口中释放中断。

设备中主要填充iio_dev的字段有以下:

1.modes; 2.dev; 3.event_interface;4.buffer; 5.available_scan_masks;6.trig; 7.pollfunc/pollfunc_event; 8.channels; 9.setup_ops;

2.struct iio_event_interface

对event子模块的定义。

 37 struct iio_event_interface {38     wait_queue_head_t   wait;39     DECLARE_KFIFO(det_events, struct iio_event_data, 16);4041     struct list_head    dev_attr_list;42     unsigned long       flags;43     struct attribute_group  group;44     struct mutex        read_lock;45 };

1.wait等待队列,当应用程序读取触发事件信息时,若当前无数据可读,则将当前进程加入到该等待队列中,待调用iio_push_event将触发事件信息加到到kfifo后,则wakeup该队列中的进程。

2.定义kfifo,存储所有触发的时间信息,供应用程序获取。

3,将event子模块动态定义的event attribute均添加到dev_attr_list中。

4.flags标记该event是否已经使能,即应用程序是否通过ioctl调用创建一个匿名方式,若使能则置位IIO_BUSY_BIT_POS。

3.struct iio_buffer

描述存储连续采集数据的缓存。

 98 struct iio_buffer {99     unsigned int                length;
100     size_t                  bytes_per_datum;
101     struct attribute_group          *scan_el_attrs;
102     long                    *scan_mask;
103     bool                    scan_timestamp;
104     const struct iio_buffer_access_funcs    *access;
105     struct list_head            scan_el_dev_attr_list;
106     struct attribute_group          buffer_group;
107     struct attribute_group          scan_el_group;
108     wait_queue_head_t           pollq;
109     bool                    stufftoread;
110     const struct attribute          **attrs;
111     struct list_head            demux_list;
112     void                    *demux_bounce;
113     struct list_head            buffer_list;
114     struct kref             ref;
115     unsigned int                watermark;
116 };

1.length, iio_buffer缓存数据的个数。

2.bytes_per_datum,iio_buffer每一次采集数据的长度,bytes_per_datum * length即kfifo存储数据的内存大小。

3.scan_el_attrs存储各设备驱动自行定义的静态属性,该变量定义的属性文件在scan_elements子目录下。

4.scan_el_dev_attr_list主要用于将所有iio_buffer子模块创建的属性变量集合在一起。

5.attrs也是村纯设备驱动自行定义的静态属性,属性文件在buffer子目录下。

6.buffer_group、scan_el_group包含iio buffer子模块下所以的属性。

7.pollq为等待队列,主要为iio device的字符设备文件使用,该字符设备文件对应的读接口和poll接口使用,当buffer不存在数据时则sleep在该等待队列中。

8.watermark为缓存多少个数据后唤醒pollq。

9.iio_buffer_access_funcs是iio_buffer对应的缓存空间访问接口,目前使用kfifo缓存数据,其访问方法为iio_store_to_kfifo、iio_read_first_n_kfifo等,主要是将数据从kfifo中存储和取出。

4. struct iio_trigger和struct iio_trigger_ops

 63 struct iio_trigger {64     const struct iio_trigger_ops    *ops;65     int             id;66     const char          *name;67     struct device           dev;6869     struct list_head        list;70     struct list_head        alloc_list;71     atomic_t            use_count;7273     struct irq_chip         subirq_chip;74     int             subirq_base;7576     struct iio_subirq subirqs[CONFIG_IIO_CONSUMERS_PER_TRIGGER];77     unsigned long pool[BITS_TO_LONGS(CONFIG_IIO_CONSUMERS_PER_TRIGGER)];78     struct mutex            pool_lock;79     bool                attached_own_device;80 };36 struct iio_trigger_ops {37     struct module *owner;38     int (*set_trigger_state)(struct iio_trigger *trig, bool state);39     int (*try_reenable)(struct iio_trigger *trig);40     int (*validate_device)(struct iio_trigger *trig,41                    struct iio_dev *indio_dev);42 };

1.id表示trigger的id,name为名称。

2.dev,iio trigger也使用device变量加入到iio总线上,iio trigger与iio device均注册到iio总线上,因此它们在sysfs目录下是同级的。

3.list用于将struct iio_trigger添加到全局链表iio_trigger_list中。

4.alloc_list主要用于同一类的trigger可注册多个trigger实例的请求。

5.use_count,引用计数。

6.而subirq_chip、subirq_base、subirqs、pool则主要用于创建虚拟的irq chip,在trigger内部,当多个trigger consumer注册时,则trigger内部会为其分配一个虚拟的irq,并根据trigger consumer提供给pollfunc,为该irq注册中断处理函数,这样当该trigger触发后,则会遍历所有该trigger上已注册的虚拟irq,调用其中断处理函数从而执行trigger consumer提供的处理函数。

7.iio_trigger_ops是trigger操作接口。

5. struct iio_chan_spec

257 struct iio_chan_spec {
258     enum iio_chan_type  type;
259     int         channel;
260     int         channel2;
261     unsigned long       address;
262     int         scan_index;
263     struct {
264         char    sign;
265         u8  realbits;
266         u8  storagebits;
267         u8  shift;
268         u8  repeat;
269         enum iio_endian endianness;
270     } scan_type;
271     long            info_mask_separate;
272     long            info_mask_shared_by_type;
273     long            info_mask_shared_by_dir;
274     long            info_mask_shared_by_all;
275     const struct iio_event_spec *event_spec;
276     unsigned int        num_event_specs;
277     const struct iio_chan_spec_ext_info *ext_info;
278     const char      *extend_name;
279     const char      *datasheet_name;
280     unsigned        modified:1;
281     unsigned        indexed:1;
282     unsigned        output:1;
283     unsigned        differential:1;
284 };

1.type,该channel的类型,如IIO_TEMP、IIO_ACCEL等。

2.channel表示该channel的index,当indexed为1时,才使用该index表示channel里的属性参数。

3.channel2表示channel的别沉,当modified为1时,才则使用该index对应的string藐视channel。如针对三轴陀螺仪而言,如果还是用channel0、channel1进行识别的话不好区分,可使用channelX,channelY、channelZ等。

4.info_mask_separate表示channel的某一个属性为channel专属的。

5.info_mask_shared_by_type则表示该iio device下所有相同类型的channel所共享的属性。

6.info_mask_shared_by_dir表示该iio device下所有相同方向channel所共享的属性。

7.info_mask_shared_by_all表示该iio device下所有channel所共享的属性。

8.scan_index、scan_type表示采集数据的index以及数据的类型等。

9.event_spac定义event相关的信息。

6. struct iio_info

382 struct iio_info {
383     struct module           *driver_module;
384     struct attribute_group      *event_attrs;
385     const struct attribute_group    *attrs;
386
387     int (*read_raw)(struct iio_dev *indio_dev,
388             struct iio_chan_spec const *chan,
389             int *val,
390             int *val2,
391             long mask);
392
393     int (*read_raw_multi)(struct iio_dev *indio_dev,
394             struct iio_chan_spec const *chan,
395             int max_len,
396             int *vals,
397             int *val_len,
398             long mask);
399
400     int (*write_raw)(struct iio_dev *indio_dev,
401              struct iio_chan_spec const *chan,
402              int val,
403              int val2,
404              long mask);
405
406     int (*write_raw_get_fmt)(struct iio_dev *indio_dev,
407              struct iio_chan_spec const *chan,
408              long mask);
409
410     int (*read_event_config)(struct iio_dev *indio_dev,
411                  const struct iio_chan_spec *chan,
412                  enum iio_event_type type,
413                  enum iio_event_direction dir);
414
415     int (*write_event_config)(struct iio_dev *indio_dev,
416                   const struct iio_chan_spec *chan,
417                   enum iio_event_type type,
418                   enum iio_event_direction dir,
419                   int state);
420
421     int (*read_event_value)(struct iio_dev *indio_dev,
422                 const struct iio_chan_spec *chan,
423                 enum iio_event_type type,
424                 enum iio_event_direction dir,
425                 enum iio_event_info info, int *val, int *val2);
426
427     int (*write_event_value)(struct iio_dev *indio_dev,
428                  const struct iio_chan_spec *chan,
429                  enum iio_event_type type,
430                  enum iio_event_direction dir,
431                  enum iio_event_info info, int val, int val2);
432
433     int (*validate_trigger)(struct iio_dev *indio_dev,
434                 struct iio_trigger *trig);
435     int (*update_scan_mode)(struct iio_dev *indio_dev,
436                 const unsigned long *scan_mask);
437     int (*debugfs_reg_access)(struct iio_dev *indio_dev,
438                   unsigned reg, unsigned writeval,
439                   unsigned *readval);
440     int (*of_xlate)(struct iio_dev *indio_dev,
441             const struct of_phandle_args *iiospec);
442     int (*hwfifo_set_watermark)(struct iio_dev *indio_dev, unsigned val);
443     int (*hwfifo_flush_to_buffer)(struct iio_dev *indio_dev,
444                       unsigned count);
445 };

该数据结构主要定义了通过syfs读写channel属性的接口,其中read_raw、write_raw可用于读取通道的raw数据等;而write_event_value则主要用于event事件触发的阈值参数的设置与读取等、而read_event_config、write_event_config则可以用于实现event的使能与否;而event_attrs、attrs则主要用于设备驱动自定义的属性参数(包括event属性参数以及iio device相关的属性参数)

下面我们来看看inv_mpu6050这个驱动是怎么实现的。

inv_mpu6050驱动分析

整个驱动我们主要挑了以下几个方面来看:

1.重要的数据结构是怎么填充以及所调用的接口。

2.数据是如何采集上报的。

probe中的数据结构填充

我们来看i2c设备下的probe,源码在:drivers/iio/imu/inv_mpu6050目录下。我们只关注与iio相关的代码

 92 static int inv_mpu_probe(struct i2c_client *client,93              const struct i2c_device_id *id)94 {95     struct inv_mpu6050_state *st;96     int result, chip_type;97     struct regmap *regmap;98     const char *name;……
122     result = inv_mpu_core_probe(regmap, client->irq, name,
123                     NULL, chip_type);
124     if (result < 0)
125         return result;
126
127     st = iio_priv(dev_get_drvdata(&client->dev));……
144
145     return 0;
152 }

inv_mpu_probe方法与iio相关的操作都在inv_mpu_core_probe中,iio_priv方法是从iio_dev地址上获取到私有数据。

846 int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
847         int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type)
848 {
849     struct inv_mpu6050_state *st;
850     struct iio_dev *indio_dev;
851     struct inv_mpu6050_platform_data *pdata;
852     struct device *dev = regmap_get_device(regmap);
853     int result;
854
855     indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
856     if (!indio_dev)
857         return -ENOMEM;
858……
899     indio_dev->dev.parent = dev;
900     /* name will be NULL when enumerated via ACPI */
901     if (name)
902         indio_dev->name = name;
903     else
904         indio_dev->name = dev_name(dev);
905     indio_dev->channels = inv_mpu_channels;
906     indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
907
908     indio_dev->info = &mpu_info;
909     indio_dev->modes = INDIO_BUFFER_TRIGGERED;
910
911     result = iio_triggered_buffer_setup(indio_dev,
912                         inv_mpu6050_irq_handler,
913                         inv_mpu6050_read_fifo,
914                         NULL);
915     if (result) {
916         dev_err(dev, "configure buffer fail %d\n", result);
917         return result;
918     }
919     result = inv_mpu6050_probe_trigger(indio_dev);
920     if (result) {
921         dev_err(dev, "trigger probe fail %d\n", result);
922         goto out_unreg_ring;
923     }
924
925     INIT_KFIFO(st->timestamps);
926     spin_lock_init(&st->time_stamp_lock);
927     result = iio_device_register(indio_dev);
928     if (result) {
929         dev_err(dev, "IIO register fail %d\n", result);
930         goto out_remove_trigger;
931     }
932
933     return 0;
934
935 out_remove_trigger:
936     inv_mpu6050_remove_trigger(st);
937 out_unreg_ring:
938     iio_triggered_buffer_cleanup(indio_dev);
939     return result;
940 }

1.devm_iio_device_alloc,用于创建构造iio_dev变量,同时初始化了iio_dev->dev变量,设备的名字是"iio:device%d"格式。

2.indio_dev->dev.parent = dev,此处dev是i2c子设备的device。

3.indio_dev->channels = inv_mpu_channels; && indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); iio_chan_spec的填充如下:

714 #define INV_MPU6050_CHAN(_type, _channel2, _index)                    \
715     {                                                             \
716         .type = _type,                                        \
717         .modified = 1,                                        \
718         .channel2 = _channel2,                                \
719         .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
720         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |        \
721                       BIT(IIO_CHAN_INFO_CALIBBIAS),   \
722         .scan_index = _index,                                 \
723         .scan_type = {                                        \
724                 .sign = 's',                          \
725                 .realbits = 16,                       \
726                 .storagebits = 16,                    \
727                 .shift = 0,                           \
728                 .endianness = IIO_BE,                 \
729                  },                                       \
730         .ext_info = inv_ext_info,                             \
731     }
732
733 static const struct iio_chan_spec inv_mpu_channels[] = {
734     IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
735     /*
736      * Note that temperature should only be via polled reading only,
737      * not the final scan elements output.
738      */
739     {
740         .type = IIO_TEMP,
741         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
742                 | BIT(IIO_CHAN_INFO_OFFSET)
743                 | BIT(IIO_CHAN_INFO_SCALE),
744         .scan_index = -1,
745     },
746     INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
747     INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
748     INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
749
750     INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
751     INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
752     INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
753 };

共有8个channel,分别是IIO_TIMESTAMP(表示虚拟通道-时间戳)、IIO_TEMP(温度)、3个IIO_ANGL_VEL(角度传感器3个轴)和3个IIO_ACCEL(加速度传感器3个轴)。6轴的channel使用宏INV_MPU6050_CHAN来定义,表明他们的参数基本一致。

4.indio_dev->info = &mpu_info;

784 static const struct iio_info mpu_info = {
785     .driver_module = THIS_MODULE,
786     .read_raw = &inv_mpu6050_read_raw,
787     .write_raw = &inv_mpu6050_write_raw,
788     .write_raw_get_fmt = &inv_write_raw_get_fmt,
789     .attrs = &inv_attribute_group,
790     .validate_trigger = inv_mpu6050_validate_trigger,
791 };

5.indio_dev->modes = INDIO_BUFFER_TRIGGERED;inv_mpu6050只使用了buffer trigger这种模式。

6.iio_triggered_buffer_setup,创建kfifo和iio_buffer_setup_ops,是iio的标准接口。

 44 int iio_triggered_buffer_setup(struct iio_dev *indio_dev,45     irqreturn_t (*h)(int irq, void *p),46     irqreturn_t (*thread)(int irq, void *p),47     const struct iio_buffer_setup_ops *setup_ops)48 {49     struct iio_buffer *buffer;50     int ret;5152     buffer = iio_kfifo_allocate();53     if (!buffer) {54         ret = -ENOMEM;55         goto error_ret;56     }5758     iio_device_attach_buffer(indio_dev, buffer);5960     indio_dev->pollfunc = iio_alloc_pollfunc(h,61                          thread,62                          IRQF_ONESHOT,63                          indio_dev,64                          "%s_consumer%d",65                          indio_dev->name,66                          indio_dev->id);67     if (indio_dev->pollfunc == NULL) {68         ret = -ENOMEM;69         goto error_kfifo_free;70     }7172     /* Ring buffer functions - here trigger setup related */73     if (setup_ops)74         indio_dev->setup_ops = setup_ops;75     else76         indio_dev->setup_ops = &iio_triggered_buffer_setup_ops;7778     /* Flag that polled ring buffering is possible */79     indio_dev->modes |= INDIO_BUFFER_TRIGGERED;8081     return 0;8283 error_kfifo_free:84     iio_kfifo_free(indio_dev->buffer);85 error_ret:86     return ret;87 }

iio_kfifo_allocate创建了iio_buffer,并未iio_buffer填充了access和length字段,length默认为2。iio_device_attach_buffer将创建的iio_buffer与iio_dev关联起来。函数参数h和thread分别是irq的上半部和下半部,并调用iio_alloc_pollfunc生成pollfunc。iio_dev的setup_ops,如果传参为NULL,则使用默认的参数&iio_triggered_buffer_setup_ops。

7.inv_mpu6050_probe_trigger,调用devm_iio_trigger_alloc申请注册trigger的virtual irq,并调用iio_trigger_register注册trigger。

8.完成以上准备操作后,注册iio_dev,调用iio_device_register进行注册。

以上是iio设备驱动probe所做的事情,这样看起来probe做的事情并不多,实际上大部分的sysfs节点和chrdev的注册都是有iio core实现的。下面我们主要分析iio的核心框架是怎么做的。

iio_buffer

iio_device_register方法为iio设备驱动创建了buffer和channel的sysfs。

1478 int iio_device_register(struct iio_dev *indio_dev)
1479 {
1480     int ret;
1481
1482     /* If the calling driver did not initialize of_node, do it here */
1483     if (!indio_dev->dev.of_node && indio_dev->dev.parent)
1484         indio_dev->dev.of_node = indio_dev->dev.parent->of_node;
1485     // channels scan_index不能有重复
1486     ret = iio_check_unique_scan_index(indio_dev);
1487     if (ret < 0)
1488         return ret;
1489
1490     /* configure elements for the chrdev */
1491     indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
1492
1493     ret = iio_device_register_debugfs(indio_dev);
1494     if (ret) {
1495         dev_err(indio_dev->dev.parent,
1496             "Failed to register debugfs interfaces\n");
1497         return ret;
1498     }
1499     // 创建buffer和scan_elements下的属性节点
1500     ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
1501     if (ret) {
1502         dev_err(indio_dev->dev.parent,
1503             "Failed to create buffer sysfs interfaces\n");
1504         goto error_unreg_debugfs;
1505     }
1506    // 创建channel的属性节点。
1507     ret = iio_device_register_sysfs(indio_dev);
1508     if (ret) {
1509         dev_err(indio_dev->dev.parent,
1510             "Failed to register sysfs interfaces\n");
1511         goto error_buffer_free_sysfs;
1512     }
1513     ret = iio_device_register_eventset(indio_dev);  //创建events的属性节点
1514     if (ret) {
1515         dev_err(indio_dev->dev.parent,
1516             "Failed to register event set\n");
1517         goto error_free_sysfs;
1518     }
1519     if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
1520         iio_device_register_trigger_consumer(indio_dev);  //创建trigger属性节点
1521
1522     if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
1523         indio_dev->setup_ops == NULL)
1524         indio_dev->setup_ops = &noop_ring_setup_ops;
1525    // 创建字符设备节点,fops操作绑定iio_buffer
1526     cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
1527     indio_dev->chrdev.owner = indio_dev->info->driver_module;
1528     indio_dev->chrdev.kobj.parent = &indio_dev->dev.kobj;
1529     ret = cdev_add(&indio_dev->chrdev, indio_dev->dev.devt, 1);
1530     if (ret < 0)
1531         goto error_unreg_eventset;
1532
1533     ret = device_add(&indio_dev->dev);
1534     if (ret < 0)
1535         goto error_cdev_del;
1536
1537     return 0;
1538 error_cdev_del:
1539     cdev_del(&indio_dev->chrdev);
1540 error_unreg_eventset:
1541     iio_device_unregister_eventset(indio_dev);
1542 error_free_sysfs:
1543     iio_device_unregister_sysfs(indio_dev);
1544 error_buffer_free_sysfs:
1545     iio_buffer_free_sysfs_and_mask(indio_dev);
1546 error_unreg_debugfs:
1547     iio_device_unregister_debugfs(indio_dev);
1548     return ret;
1549 }

iio_device_register过程中,为iio_dev创建了其所需的sysfs节点,主要包括buffer、channels、debug、event和trigger等模块的节点。我们主要分析一下buffer这个核心模块的sysfs节点和iio_buffer的创建过程,了解使能/失能 buffer/enable时iio框架究竟做了些什么。

1064 int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
1065 {……
1090     attr = kcalloc(attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1,
1091                sizeof(struct attribute *), GFP_KERNEL);
1092     if (!attr)
1093         return -ENOMEM;
1094
1095     memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs));
1096     if (!buffer->access->set_length)
1097         attr[0] = &dev_attr_length_ro.attr;
1098
1099     if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK)
1100         attr[2] = &dev_attr_watermark_ro.attr;
1101
1102     if (buffer->attrs)
1103         memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs,
1104                sizeof(struct attribute *) * attrcount);
1105
1106     attr[attrcount + ARRAY_SIZE(iio_buffer_attrs)] = NULL;
1107
1108     buffer->buffer_group.name = "buffer";
1109     buffer->buffer_group.attrs = attr;……
1174 }

kfifo的创建/释放

buffer的enable/length/watermark是常规必备的attr,enable节点所对应的show/store方法分别对应了iio_buffer_show_enable/iio_buffer_store_enable,show方法我们无需关注,主要关注store方法:

 966 static ssize_t iio_buffer_store_enable(struct device *dev,967                        struct device_attribute *attr,968                        const char *buf,969                        size_t len)970 {971     int ret;972     bool requested_state;973     struct iio_dev *indio_dev = dev_to_iio_dev(dev);974     bool inlist;975976     ret = strtobool(buf, &requested_state);977     if (ret < 0)978         return ret;979980     mutex_lock(&indio_dev->mlock);981982     /* Find out if it is in the list */983     inlist = iio_buffer_is_active(indio_dev->buffer);984     /* Already in desired state */985     if (inlist == requested_state)986         goto done;987    // 当enable为1时,insert_buffer为indio_dev->buffer,表示将这个buffer插入到列表中,否则就移除它988     if (requested_state)989         ret = __iio_update_buffers(indio_dev,990                      indio_dev->buffer, NULL);991     else992         ret = __iio_update_buffers(indio_dev,993                      NULL, indio_dev->buffer);994995 done:996     mutex_unlock(&indio_dev->mlock);997     return (ret < 0) ? ret : len;998 }
 869 static int __iio_update_buffers(struct iio_dev *indio_dev,870                struct iio_buffer *insert_buffer,871                struct iio_buffer *remove_buffer)872 {873     struct iio_device_config new_config;874     int ret;875876     ret = iio_verify_update(indio_dev, insert_buffer, remove_buffer,877         &new_config);878     if (ret)879         return ret;880    // 申请创建kfifo,计算kfifo的容量与每次数据的大小881     if (insert_buffer) {882         ret = iio_buffer_request_update(indio_dev, insert_buffer);883         if (ret)884             goto err_free_config;885     }886887     ret = iio_disable_buffers(indio_dev);888     if (ret)889         goto err_deactivate_all;890    // 移除/添加buffer列表,目前看来,buffer_list只有一个buffer891     if (remove_buffer)892         iio_buffer_deactivate(remove_buffer);893     if (insert_buffer)894         iio_buffer_activate(indio_dev, insert_buffer);895896     /* If no buffers in list, we are done */897     if (list_empty(&indio_dev->buffer_list))898         return 0;899    // 更新 iio_buffer的配置信息。900     ret = iio_enable_buffers(indio_dev, &new_config);901     if (ret)902         goto err_deactivate_all;903904     return 0;

iio_buffer_request_update是创建看kfifo的重心,这里进行了两个操作:①iio_buffer_update_bytes_per_datum,计算每次数据上报的大小。②申请kfifo的空间。

 545 static int iio_compute_scan_bytes(struct iio_dev *indio_dev,546                 const unsigned long *mask, bool timestamp)547 {548     unsigned bytes = 0;549     int length, i;550551     /* How much space will the demuxed element take? */552     for_each_set_bit(i, mask,553              indio_dev->masklength) {554         length = iio_storage_bytes_for_si(indio_dev, i);555         bytes = ALIGN(bytes, length);556         bytes += length;557     }558559     if (timestamp) {560         length = iio_storage_bytes_for_timestamp(indio_dev);561         bytes = ALIGN(bytes, length);562         bytes += length;563     }564     return bytes;565 }

每次上报的数据大小并不是完全根据channels中指定的storagebits来计算,而是还是数据的对齐。ALIGN是一个宏,展开后核心如下:

(((x) + (mask)) & ~(mask))

x就是第一个参数,mask是第二个参数-1。例如一次使能了4个channels,前三个channel的storagebits为2bytes,第四个channel为4bytes。那么最后bytes的值就是12.

计算过程如下:

channel0:length = 2; bytes = (0 + 2) &(~1) + 2 = 2
channel1:length = 2; bytes = (2 + 2) & (~1) + 2 = 4
channel2:length = 2; bytes = (4 + 2) & (~1) + 2 = 6
channel3:length = 4; bytes = (6 + 4) & (~3) + 4 = 12

这个值最后被设置到iio_buffer的bytes_per_datum字段中。然后在iio_buffer的request_update操作中进行kfifo的申请。

 38 static int iio_request_update_kfifo(struct iio_buffer *r)39 {40     int ret = 0;41     struct iio_kfifo *buf = iio_to_kfifo(r);4243     mutex_lock(&buf->user_lock);44     if (buf->update_needed) {45         kfifo_free(&buf->kf);46         ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,47                    buf->buffer.length);48         if (ret >= 0)49             buf->update_needed = false;50     } else {51         kfifo_reset_out(&buf->kf);52     }53     mutex_unlock(&buf->user_lock);5455     return ret;56 }38 int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,39         size_t esize, gfp_t gfp_mask)40 {41     /*42      * round down to the next power of 2, since our 'let the indices43      * wrap' technique works only in this case.44      */45     size = roundup_pow_of_two(size);4647     fifo->in = 0;48     fifo->out = 0;49     fifo->esize = esize;5051     if (size < 2) {52         fifo->data = NULL;53         fifo->mask = 0;54         return -EINVAL;55     }5657     fifo->data = kmalloc(size * esize, gfp_mask);5859     if (!fifo->data) {60         fifo->mask = 0;61         return -ENOMEM;62     }63     fifo->mask = size - 1;6465     return 0;66 }

kfifo的esize就是iio_buffer的bytes_per_datum。fifo的data内存大小就是iio_buffer->length * iio_buffer->bytes_per_datum.

而当enable为0时,则简单得多了,只需要释放kfifo:

 574 static void iio_buffer_deactivate(struct iio_buffer *buffer)575 {576     list_del_init(&buffer->buffer_list);577     wake_up_interruptible(&buffer->pollq);578     iio_buffer_put(buffer);579 }69 void __kfifo_free(struct __kfifo *fifo)70 {71     kfree(fifo->data);72     fifo->in = 0;73     fifo->out = 0;74     fifo->esize = 0;75     fifo->data = NULL;76     fifo->mask = 0;77 }

kfifo的in/out

前面说完kfifo的创建,而kfifo的数据写入和读取我们在inv_mpu6050驱动中再展开分析。

数据上报/读取

inv_mpu6050的trigger是通过中断来触发的。当mpu数据采集完成后,触发irq,调用iio_trigger_generic_data_rdy_poll这个中断,iio trigger触发其创建的虚拟中断来回调iio_dev的pollfunc,并进入到我们定义的pollfunc中采集数据,并通过iio_push_to_buffer接口将数据store到kfifo中。

本节的重点是iio trigger的触发过程和iio_push_to_buffer将数据store到kfifo的过程细节。

trigger的触发

trigger默认是没有打开的,trigger的使能/关闭都与buffer的使能/关闭挂钩。在iio_buffer小节中,iio_buffer的使能/失能会调用iio_enable_buffers/iio_disable_buffers去,在这两个操作中,会顺带控制了trigger的使能/关闭。

 755 static int iio_enable_buffers(struct iio_dev *indio_dev,756     struct iio_device_config *config)757 {……801     if (indio_dev->setup_ops->postenable) {802         ret = indio_dev->setup_ops->postenable(indio_dev);803         if (ret) {804             dev_dbg(&indio_dev->dev,805                    "Buffer not started: postenable failed (%d)\n", ret);806             goto err_disable_buffers;807         }808     }
 826 static int iio_disable_buffers(struct iio_dev *indio_dev)827 { ……857     if (indio_dev->setup_ops->postdisable) {858         ret2 = indio_dev->setup_ops->postdisable(indio_dev);859         if (ret2 && !ret)860             ret = ret2;861     }

setup_ops中默认是iio_triggered_buffer_setup_ops,这个也是mpu6050中所使用的。

 19 static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {20     .postenable = &iio_triggered_buffer_postenable,21     .predisable = &iio_triggered_buffer_predisable,22 };
245 static int iio_trigger_attach_poll_func(struct iio_trigger *trig,
246                     struct iio_poll_func *pf)
247 {
248     int ret = 0;
249     bool notinuse
250         = bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
251
252     /* Prevent the module from being removed whilst attached to a trigger */
253     __module_get(pf->indio_dev->info->driver_module);
254
255     /* Get irq number */
256     pf->irq = iio_trigger_get_irq(trig);
257     if (pf->irq < 0)
258         goto out_put_module;
259
260     /* Request irq */
261     ret = request_threaded_irq(pf->irq, pf->h, pf->thread,
262                    pf->type, pf->name,
263                    pf);
264     if (ret < 0)
265         goto out_put_irq;
266
267     /* Enable trigger in driver */
268     if (trig->ops->set_trigger_state && notinuse) {
269         ret = trig->ops->set_trigger_state(trig, true);
270         if (ret < 0)
271             goto out_free_irq;
272     }

iio_trigger_get_irq返回trigger所使用的虚拟中断号,调用request_threaded_irq向系统注册这个虚拟中断,pf就是probe中的inv_mpu6050_irq_handler/inv_mpu6050_read_fifo.

predisable则更简单,直接free_irq。

虚拟中断已经创建好,那么剩下的就是软件触发,而iio_trigger_poll就是触发这个中断的接口。

166 void iio_trigger_poll(struct iio_trigger *trig)
167 {
168     int i;
169
170     if (!atomic_read(&trig->use_count)) {
171         atomic_set(&trig->use_count, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
172
173         for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
174             if (trig->subirqs[i].enabled)
175                 generic_handle_irq(trig->subirq_base + i);
176             else
177                 iio_trigger_notify_done(trig);
178         }
179     }
180 }

generic_handle_irq是irq子系统的接口,在这里不再展开。此时就可以回调到我们设备驱动中的回调。

iio_push_to_buffer

iio_push_to_buffer是往kfifo写入数据:

1245 static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data)
1246 {
1247     const void *dataout = iio_demux(buffer, data);
1248     int ret;
1249
1250     ret = buffer->access->store_to(buffer, dataout);
1251     if (ret)
1252         return ret;
1253
1254     /*
1255      * We can't just test for watermark to decide if we wake the poll queue
1256      * because read may request less samples than the watermark.
1257      */
1258     wake_up_interruptible_poll(&buffer->pollq, POLLIN | POLLRDNORM);
1259     return 0;
1260 }

store_to是kfifo的iio_store_to_kfifo:

 86 static int iio_store_to_kfifo(struct iio_buffer *r,87                   const void *data)88 {89     int ret;90     struct iio_kfifo *kf = iio_to_kfifo(r);91     ret = kfifo_in(&kf->kf, data, 1);92     if (ret != 1)93         return -EBUSY;94     return 0;95 }
126 unsigned int __kfifo_in(struct __kfifo *fifo,
127         const void *buf, unsigned int len)
128 {
129     unsigned int l;
130
131     l = kfifo_unused(fifo);
132     if (len > l)
133         len = l;
134
135     kfifo_copy_in(fifo, buf, len, fifo->in);
136     fifo->in += len;
137     return len;
138 }
102 static void kfifo_copy_in(struct __kfifo *fifo, const void *src,
103         unsigned int len, unsigned int off)
104 {
105     unsigned int size = fifo->mask + 1;
106     unsigned int esize = fifo->esize;
107     unsigned int l;
108
109     off &= fifo->mask;
110     if (esize != 1) {
111         off *= esize;
112         size *= esize;
113         len *= esize;
114     }
115     l = min(len, size - off);
116
117     memcpy(fifo->data + off, src, l);
118     memcpy(fifo->data, src + l, len - l);
119     /*
120      * make sure that the data in the fifo is up to date before
121      * incrementing the fifo->in index counter
122      */
123     smp_wmb();
124 }

kfifo_copy_in每次copy的字节数是esize,因此iio_push_to_buffer接口参数并没有传入大小,只传入了指针,因为每次store_to都是固定的大小,因此data所指向的数据必须不能小于esize的大小,esize就是前面kfifo的创建中根据channel的storagebits的大小并对齐后的大小。

store_to之后就是唤醒poll的等待.

数据读取

这里的数据读取是指通过chrdev节点读取的方式。在前面iio_device_register方法中可以看到,chrdev的fops是iio_buffer_fileops,里面提供了read和poll的接口方法。我们这里只看一下read接口:

 105 ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,106                       size_t n, loff_t *f_ps)107 {108     struct iio_dev *indio_dev = filp->private_data;109     struct iio_buffer *rb = indio_dev->buffer;110     DEFINE_WAIT_FUNC(wait, woken_wake_function);111     size_t datum_size;112     size_t to_wait;113     int ret = 0;114115     if (!indio_dev->info)116         return -ENODEV;117118     if (!rb || !rb->access->read_first_n)119         return -EINVAL;120121     datum_size = rb->bytes_per_datum;122123     /*124      * If datum_size is 0 there will never be anything to read from the125      * buffer, so signal end of file now.126      */127     if (!datum_size)128         return 0;129130     if (filp->f_flags & O_NONBLOCK)131         to_wait = 0;132     else133         to_wait = min_t(size_t, n / datum_size, rb->watermark);134135     add_wait_queue(&rb->pollq, &wait);136     do {137         if (!indio_dev->info) {138             ret = -ENODEV;139             break;140         }141142         if (!iio_buffer_ready(indio_dev, rb, to_wait, n / datum_size)) {143             if (signal_pending(current)) {144                 ret = -ERESTARTSYS;145                 break;146             }147148             wait_woken(&wait, TASK_INTERRUPTIBLE,149                    MAX_SCHEDULE_TIMEOUT);150             continue;151         }152153         ret = rb->access->read_first_n(rb, n, buf);154         if (ret == 0 && (filp->f_flags & O_NONBLOCK))155             ret = -EAGAIN;156     } while (ret == 0);157     remove_wait_queue(&rb->pollq, &wait);158159     return ret;160 }

如果open是传入的参数是O_NONBLOCK,则不用等待,否则会根据read的size/bytes_per_datum和watermark的最小值来判断返回时机。当有足够数据足以返回时,则调用kfifo的read_first_n接口取读取数据:

 97 static int iio_read_first_n_kfifo(struct iio_buffer *r,98                size_t n, char __user *buf)99 {
100     int ret, copied;
101     struct iio_kfifo *kf = iio_to_kfifo(r);
102
103     if (mutex_lock_interruptible(&kf->user_lock))
104         return -ERESTARTSYS;
105
106     if (!kfifo_initialized(&kf->kf) || n < kfifo_esize(&kf->kf))
107         ret = -EINVAL;
108     else
109         ret = kfifo_to_user(&kf->kf, buf, n, &copied);
110     mutex_unlock(&kf->user_lock);
111     if (ret < 0)
112         return ret;
113
114     return copied;
115 }

这里要注意一点,当read的size小于esize,那么read就会被返回-EINVAL,由于iio认为每次读取数据都必须大于一个完整的数据。

283 int __kfifo_to_user(struct __kfifo *fifo, void __user *to,
284         unsigned long len, unsigned int *copied)
285 {
286     unsigned int l;
287     unsigned long ret;
288     unsigned int esize = fifo->esize;
289     int err;
290
291     if (esize != 1)
292         len /= esize;
293
294     l = fifo->in - fifo->out;
295     if (len > l)
296         len = l;
297     ret = kfifo_copy_to_user(fifo, to, len, fifo->out, copied);
298     if (unlikely(ret)) {
299         len -= ret;
300         err = -EFAULT;
301     } else
302         err = 0;
303     fifo->out += len;
304     return err;
305 }
249 static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to,
250         unsigned int len, unsigned int off, unsigned int *copied)
251 {
252     unsigned int l;
253     unsigned long ret;
254     unsigned int size = fifo->mask + 1;
255     unsigned int esize = fifo->esize;
256
257     off &= fifo->mask;
258     if (esize != 1) {
259         off *= esize;
260         size *= esize;
261         len *= esize;
262     }
263     l = min(len, size - off);
264
265     ret = copy_to_user(to, fifo->data + off, l);
266     if (unlikely(ret))
267         ret = DIV_ROUND_UP(ret + len - l, esize);
268     else {
269         ret = copy_to_user(to + l, fifo->data, len - l);
270         if (unlikely(ret))
271             ret = DIV_ROUND_UP(ret, esize);
272     }
273     /*
274      * make sure that the data is copied before
275      * incrementing the fifo->out index counter
276      */
277     smp_wmb();
278     *copied = len - ret * esize;
279     /* return the number of elements which are not copied */
280     return ret;
281 }

就这样,用户空间就可以完成数据的读取。

iio子系统用户空间

iio子系统提供了很多的sysfs节点供用户去配置/操作iio设备。操作iio设备主要关注一个目录和一个节点:

目录就是/sys/bus/iio/devices/iio:deviceX,这个是iio的sysfs节点所在的目录,节点就是/dev/iio:deviceX,这个是iio字符设备驱动节点。

一般操作步骤:

1.或者iio设备驱动的信息,如channel的index、type(数据的类型/大小/偏移等信息),这部分在scan_elements子目录下;in_xxxx_scale信息,各个设备的scale信息,这个关乎数据计算;sampling_frequency,这个名字不固定,一般都会提供一个设置频率的节点。

1.设置channel使能,节点名一般是in_xxxx_x_en,在scan_elements子目录下。

2.设置频率。

3.使能buffer,节点在buffer/enable下,也可以设置buffer的length和watermark,但一般无需设置。

4.读取/dev/iio:deviceX节点,如cat /dev/iio:device0 | xxd -c 2

5.根据channel信息来获取数据,并根据scale来换算单位。

iio还提供单独单次获取一个channel的数据,如in_xxxx_x_raw节点,cat这个节点即可获取这个channel的数据。

另外,iio为了数据的安全,所有的设置都必须在buffer/enable为0的状态下进行设置。


http://chatgpt.dhexx.cn/article/IgYvQN8o.shtml

相关文章

Linux设备驱动之IIO子系统——IIO框架及IIO数据结构

由于需要对ADC进行驱动设计,因此学习了一下Linux驱动的IIO子系统。本文翻译自《Linux Device Drivers Development 》--John Madieu,本人水平有限,若有错误请大家指出。 IIO Framework 工业I / O(IIO)是专用于模数转换器(ADC)和数模转换器(DAC)的内核子系统。随着越来…

Linux IIO 子系统简介

IIO 子系统系统框架 而在IIO子系统内部&#xff0c;则主要包括如下四部分的内容&#xff1a; iio buffer用于处理需要进行连续采集的数据&#xff0c;当一个IIO device的各通道数据支持连续采集时&#xff0c;则调用iio buffer模块提供的接口&#xff0c;创建iio buffer用于存…

LINUX IIO子系统分析之五IIO BUFFER子模块实现分析

上一章我们介绍了iio子系统中的iio event模块&#xff0c;本章我们将介绍iio buffer模块&#xff0c;iio buffer主要用于连续数据采集与缓存功能。IIO buffer模块借助IIO DEVICE字符设备文件与应用程序通信&#xff0c;同时借助iio trigger模块与iio device进行交互&#xff0c…

kudu教程(一)——简介

##kudu教程&#xff08;一&#xff09;——简介 学习kudu先从kudu官网开始&#xff0c;进入主页https://kudu.apache.org/ 我们看到的第一句话就是 A new addition to the open source Apache Hadoop ecosystem, Apache Kudu completes Hadoop’s storage layer to enable fas…

kudu介绍:为什么要使用?

前言 近两年&#xff0c;KUDU 在大数据平台的应用越来越广泛。在阿里、小米、网易等公司的大数据架构中&#xff0c;KUDU 都有着不可替代的地位。本文通过分析 KUDU 的设计&#xff0c; 试图解释为什么 KUDU 会被广泛应用于大数据领域&#xff0c;因为还没有研究过 KUDU 的代码…

Kudu 的架构概述

Kudu 的架构概述 目录 Kudu 的架构概述 体系结构概述 Kudu 的特点 Kudu 的概念和术语 体系结构概述 下图显示了一个Kudu集群&#xff0c;其中有三个主机和多个 tablet servers&#xff0c;每个tablet server 都服务于多个tablet Kudu 的特点 特点一&#xff1a;主从架构…

Outline Of Sodoku

第一步&#xff1a;确定所需要实现的目标 1.数独程序的界面&#xff1a; 1.1初始界面边框 1.1.1边框上的Sodoku标题 1.2初始界面背景图片 1.3初始界面开始键 1.4初始界面难度设置 \\数独界面始终为99的矩阵&#xff0c;可以通过改变初始给定格数来改变难度 1.5响应键盘和鼠标指…

Apache Kudu架构

目录 1. Kudu架构 1. Kudu架构 下图显示了一个具有三个master和多个tablet server的Kudu集群 Kudu采用Raft一致性算法&#xff0c;当写入一条数据&#xff0c;被至少一半replica保存&#xff0c;就可以让客户端访问该条数据。但所有的replica的数据会最终一致 Table&#xff…

kudu的相关介绍

目录 前言 背景 概览 数据模型 核心 API 一致性模型 架构 整体架构 数据分区策略 存储 存储设计目标 存储方式 存储实现 读写过程 应用案例 前言 近两年&#xff0c;KUDU 在大数据平台的应用越来越广泛。在阿里、小米、网易等公司的大数据架构中&#xff0c;KUD…

大数据入门-什么是Kudu

目录 一、概念 二、架构 1.Master Server 2.Tablet Server 3.Table 4.Tablet 三、特性 1.重要性 2.易用性 3.优势 4.与传统关系型数据库比较 5.与其他大数据组件比较 四、常用语句 1.建表 1.建普通表 2.建分区表 2.删除表 3.查询数据 4.添加数据 5.更新数据…

Kudu初入门

目录 介绍&#xff1a; 基础架构&#xff1a; 关于Tablet&#xff1a; Kudu与Impala集成 安装Kudu 配置Impala支持Kudu&#xff1a; 使用案例&#xff1a; 创建表&#xff1a; 查询Impala中现有的Kudu表 使用CREATE TABLE AS SELECT语句查询Impala中的任何其他表或来…

kudu-- 分布式数据库

一、前言 近两年&#xff0c;KUDU 在大数据平台的应用越来越广泛。在阿里、小米、网易等公司的大数据架构中&#xff0c;KUDU 都有着不可替代的地位。本文通过分析 KUDU 的设计&#xff0c; 试图解释为什么 KUDU 会被广泛应用于大数据领域&#xff0c;因为还没有研究过 KUDU 的…

Apache Kudu的介绍

一、Apache Kudu的介绍 1.1、背景介绍 在kudu之前&#xff0c;大数据主要以两种方式存储&#xff1b; &#xff08;1&#xff09;静态数据 &#xff1a; 以HDFS引擎作为存储&#xff0c;适用于高吞吐量的离线大数据分析场景。 这类存储的局限性是数据无法进行随机读写。 &…

kudu compaction操作

与hbase相同&#xff0c;kudu也需要定期进行compaction操作。kudu中的compaction操作有两种&#xff0c;一是合并delta文件。二是将一个tablet中的多个diskRowset进行重排。下面分别对这两者进行介绍。 之前在kudu的update操作中讲到&#xff0c;update操作的数据会先写入delt…

Apache Kudo: 1.0版和未来 [session]

Strata Data Conference北京站大会还有一个月即将召开&#xff0c; 有需求的同学还请抓紧时间&#xff0c; 点击二维码即可登录会议官网报名。 Apache Kudo: 1.0版和未来 讲师&#xff1a;Hao Hao (Cloudera) 14:50–15:30 Friday, 2017-07-14 数据工程和架构 (Data engin…

kudu架构

课程链接&#xff1a; http://edu.51cto.com/course/15174.html 特点&#xff1a; High availability&#xff08;高可用性&#xff09;。Tablet server 和 Master 使用 Raft Consensus Algorithm 来保证节点的高可用&#xff0c;确保只要有一半以上的副本可用&#xff0c;该 …

Apache Kudu

前言    在Kudu出现前&#xff0c;由于传统存储系统的局限性&#xff0c;对于数据的快速输入和分析还没有一个完美的解决方案&#xff0c;要么以缓慢的数据输入为代价实现快速分析&#xff0c;要么以缓慢的分析为代价实现数据快速输入。随着快速输入和分析场景越来越多&…

Kudu的介绍及使用

前文&#xff1a; 过往采用Hive的离线处理时效性低&#xff0c;计算任务过于集中&#xff0c;查询效率低。SparkStreamingHive的数据清洗线使得多套数据流过于复杂。未来的数据仓库场景越来越趋向于实时数仓。 一、引入 二、架构图 2、架构及数据量 3、文件结构 4、目录结构 5…

Kudu简单使用

环境版本&#xff1a;CDH 6.3.2 | Impala 3.2.0 | Hive 2.1.1 | Hue 4.2.0 | kudu 1.10.0 # 创建kudu表&#xff0c;需指定主键、分区 CREATE TABLE kudu_table (id BIGINT,name STRING,PRIMARY KEY(id) ) PARTITION BY HASH PARTITIONS 16 STORED AS KUDU;# 创建impala外部表映…

Kurento

java相关代码:https://codeload.github.com/Kurento/kurento-tutorial-java/zip/refs/heads/master node相关代码&#xff1a;https://github.com/Kurento/kurento-tutorial-node WebRTC之Kurento:直播、视频通话、视频会议我都行&#xff01; 前言 前段时间做rtsp无插件网页…