英特尔集成传感器 Hub (ISH)

传感器 Hub 能够将传感器轮询和算法处理卸载到专用的低功耗协处理器。这使得核心处理器可以更频繁地进入低功耗模式,从而延长电池寿命。

许多供应商提供符合 HID 传感器使用表标准的外部传感器 Hub。这些 Hub 可以在平板电脑、二合一可转换笔记本电脑和嵌入式产品中找到。Linux 自 Linux 3.9 版本以来就支持此功能。

英特尔® 从 Cherry Trail 开始引入了集成传感器 Hub 作为 SoC 的一部分,现在在多代 CPU 封装中都支持。许多商业设备已经搭载了集成传感器 Hub (ISH)。这些 ISH 也符合 HID 传感器规范,但不同之处在于通信使用的传输协议。当前的外部传感器 Hub 主要使用 HID over I2C 或 USB。但是 ISH 不使用 I2C 或 USB。

概述

与 usbhid 实现进行类比,ISH 遵循类似的模型进行高速通信

-----------------               ----------------------
|    USB HID    |       -->     |    ISH HID         |
-----------------               ----------------------
-----------------               ----------------------
|  USB protocol |       -->     |    ISH Transport   |
-----------------               ----------------------
-----------------               ----------------------
|  EHCI/XHCI    |       -->     |    ISH IPC         |
-----------------               ----------------------
      PCI                                PCI
-----------------               ----------------------
|Host controller|       -->     |    ISH processor   |
-----------------               ----------------------
     USB Link
-----------------               ----------------------
| USB End points|       -->     |    ISH Clients     |
-----------------               ----------------------

就像 USB 协议提供设备枚举、链接管理和用户数据封装的方法一样,ISH 也提供类似的服务。但是它非常轻量级,专门用于管理和与固件中实现的 ISH 客户端应用程序进行通信。

ISH 允许在固件中执行多个传感器管理应用程序。就像 USB 端点一样,消息可以往返于客户端。作为枚举过程的一部分,会识别这些客户端。这些客户端可以是简单的 HID 传感器应用程序、传感器校准应用程序或传感器固件更新应用程序。

实现模型类似,就像 USB 总线一样,ISH 传输也被实现为总线。在 ISH 处理器中执行的每个客户端应用程序都在此总线上注册为设备。绑定每个设备的驱动程序(ISH HID 驱动程序)识别设备类型并向 HID 核心注册。

ISH 实现:框图

       ---------------------------
      |  User Space Applications  |
       ---------------------------

----------------IIO ABI----------------
       --------------------------
      |  IIO Sensor Drivers     |
       --------------------------
       --------------------------
      |        IIO core         |
       --------------------------
       --------------------------
      |   HID Sensor Hub MFD    |
       --------------------------
       --------------------------
      |       HID Core          |
       --------------------------
       --------------------------
      |   HID over ISH Client   |
       --------------------------
       --------------------------
      |   ISH Transport (ISHTP) |
       --------------------------
       --------------------------
      |      IPC Drivers        |
       --------------------------
OS
---------------- PCI -----------------
Hardware + Firmware
       ----------------------------
      | ISH Hardware/Firmware(FW) |
       ----------------------------

以上框中的高级处理

硬件接口

ISH 以“非 VGA 未分类 PCI 设备”的形式暴露给主机。PCI 产品和供应商 ID 会因不同代的处理器而异。因此,枚举驱动程序的源代码需要逐代更新。

处理器间通信 (IPC) 驱动程序

位置:drivers/hid/intel-ish-hid/ipc

IPC 消息使用内存映射 I/O。寄存器在 hw-ish-regs.h 中定义。

IPC/FW 消息类型

有两种类型的消息,一种用于管理链接,另一种用于往返于传输层的消息。

传输消息的 TX 和 RX

一组内存映射寄存器支持多字节消息 TX 和 RX(例如,IPC_REG_ISH2HOST_MSG、IPC_REG_HOST2ISH_MSG)。IPC 层维护内部队列以对消息进行排序,并按顺序将其发送到固件。可选地,调用者可以注册处理程序以获取完成通知。消息传递中使用门铃机制来触发主机和客户端固件侧的处理。当调用 ISH 中断处理程序时,主机驱动程序使用 ISH2HOST 门铃寄存器来确定中断是否用于 ISH。

每一侧都有 32 个 32 位消息寄存器和一个 32 位门铃。门铃寄存器具有以下格式

Bits 0..6: fragment length (7 bits are used)
Bits 10..13: encapsulated protocol
Bits 16..19: management command (for IPC management protocol)
Bit 31: doorbell trigger (signal H/W interrupt to the other side)
Other bits are reserved, should be 0.

传输层接口

为了抽象化 HW 级别 IPC 通信,注册了一组回调。传输层使用它们来发送和接收消息。有关回调,请参阅 struct ishtp_hw_ops。

ISH 传输层

位置:drivers/hid/intel-ish-hid/ishtp/

通用传输层

传输层是一种双向协议,它定义: - 一组用于启动、停止、连接、断开连接和流控制的命令(有关详细信息,请参阅 ishtp/hbm.h) - 一种避免缓冲区溢出的流控制机制

此协议类似于以下文档中描述的总线消息:http://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/dcmi-hi-1-0-spec.pdf “第 7 章:总线消息层”

连接和流控制机制

每个 FW 客户端和协议都由 UUID 标识。为了与 FW 客户端通信,必须使用连接请求和响应总线消息建立连接。如果成功,则一对 (host_client_id 和 fw_client_id) 将标识连接。

建立连接后,对等方会彼此独立发送流控制总线消息。每个对等方只有在之前收到流控制信用时才能发送消息。一旦发送了一条消息,在收到下一个流控制信用之前,它可能不会发送另一条消息。任何一方都可以发送断开连接请求总线消息来结束通信。如果发生重大 FW 重置,链接也会断开。

对等数据传输

对等数据传输可以在使用或不使用 DMA 的情况下发生。根据传感器带宽要求,可以通过在 intel_ishtp 下使用模块参数 ishtp_use_dma 来启用 DMA。

每一侧(主机和 FW)都独立管理其 DMA 传输内存。当来自主机或 FW 侧的 ISHTP 客户端想要发送某些内容时,它会决定是通过 IPC 还是通过 DMA 发送;对于每次传输,该决定都是独立的。当消息位于各自的主机缓冲区中时(主机客户端发送时为 TX,FW 客户端发送时为 RX),发送方会发送 DMA_XFER 消息。DMA 消息的接收者会回复 DMA_XFER_ACK,指示发送者该消息的内存区域可以重复使用。

DMA 初始化从主机发送 DMA_ALLOC_NOTIFY 总线消息(包括 RX 缓冲区)开始,FW 回复 DMA_ALLOC_NOTIFY_ACK。除了 DMA 地址通信之外,此序列还会检查功能:如果主机不支持 DMA,则它不会发送 DMA 分配,因此 FW 也无法发送 DMA;如果 FW 不支持 DMA,则它不会回复 DMA_ALLOC_NOTIFY_ACK,在这种情况下,主机将不使用 DMA 传输。在这里,ISH 充当总线主 DMA 控制器。因此,当主机发送 DMA_XFER 时,它是执行主机 -> ISH DMA 传输的请求;当 FW 发送 DMA_XFER 时,这意味着它已经完成了 DMA 并且该消息驻留在主机上。因此,DMA_XFER 和 DMA_XFER_ACK 充当所有权指示符。

在初始状态下,所有传出的内存都属于发送者(TX 到主机,RX 到 FW),DMA_XFER 将包含 ISHTP 消息的区域的所有权转移到接收方,DMA_XFER_ACK 将所有权返回给发送者。发送者不必等待先前的 DMA_XFER 被确认,只要其所有权中的剩余连续内存足够,就可以发送另一条消息。原则上,可以一次发送多个 DMA_XFER 和 DMA_XFER_ACK 消息(最多为 IPC MTU),从而允许中断限制。当前,如果 ISHTP 消息超过 3 个 IPC 片段,则 ISH FW 决定通过 DMA 发送,否则通过 IPC 发送。

环形缓冲区

当客户端启动连接时,会分配一个 RX 和 TX 缓冲区环。环的大小可以由客户端指定。HID 客户端分别将 TX 和 RX 缓冲区设置为 16 和 32。在客户端的发送请求中,要发送的数据被复制到发送环形缓冲区之一,并计划使用总线消息协议发送。需要这些缓冲区是因为 FW 可能尚未处理上一条消息,并且可能没有足够的流控制信用发送。在接收端也一样,需要流控制。

主机枚举

主机枚举总线命令允许发现 FW 中存在的客户端。可以有多个传感器客户端和用于校准功能的客户端。

为了简化实现并允许独立驱动程序处理每个客户端,此传输层利用了 Linux 总线驱动程序模型。每个客户端都在传输总线(ishtp 总线)上注册为设备。

消息枚举序列

  • 主机发送 HOST_START_REQ_CMD,表示主机 ISHTP 层已启动。

  • FW 回复 HOST_START_RES_CMD

  • 主机发送 HOST_ENUM_REQ_CMD(枚举 FW 客户端)

  • FW 回复 HOST_ENUM_RES_CMD,其中包含可用 FW 客户端 ID 的位图

  • 对于该位图中找到的每个 FW ID,主机发送 HOST_CLIENT_PROPERTIES_REQ_CMD

  • FW 回复 HOST_CLIENT_PROPERTIES_RES_CMD。属性包括 UUID、最大 ISHTP 消息大小等。

  • 一旦主机接收到最后一个发现的客户端的属性,它就会认为 ISHTP 设备已完全正常工作(并分配 DMA 缓冲区)

ISH 上的 HID 客户端

位置:drivers/hid/intel-ish-hid

ISHTP 客户端驱动程序负责

  • 枚举 FW ISH 客户端下的 HID 设备

  • 获取报告描述符

  • 以 LL 驱动程序的形式向 HID 核心注册

  • 处理获取/设置功能请求

  • 获取输入报告

HID 传感器 Hub MFD 和 IIO 传感器驱动程序

这些驱动程序中的功能与外部传感器 Hub 相同。有关 IIO ABI 到用户空间的信息,请参阅 HID 传感器框架 以获取 HID 传感器文档/ABI/测试/sysfs-bus-iio。

端到端 HID 传输顺序图

HID-ISH-CLN                    ISHTP                    IPC                             HW
        |                        |                       |                               |
        |                        |                       |-----WAKE UP------------------>|
        |                        |                       |                               |
        |                        |                       |-----HOST READY--------------->|
        |                        |                       |                               |
        |                        |                       |<----MNG_RESET_NOTIFY_ACK----- |
        |                        |                       |                               |
        |                        |<----ISHTP_START------ |                               |
        |                        |                       |                               |
        |                        |<-----------------HOST_START_RES_CMD-------------------|
        |                        |                       |                               |
        |                        |------------------QUERY_SUBSCRIBER-------------------->|
        |                        |                       |                               |
        |                        |------------------HOST_ENUM_REQ_CMD------------------->|
        |                        |                       |                               |
        |                        |<-----------------HOST_ENUM_RES_CMD--------------------|
        |                        |                       |                               |
        |                        |------------------HOST_CLIENT_PROPERTIES_REQ_CMD------>|
        |                        |                       |                               |
        |                        |<-----------------HOST_CLIENT_PROPERTIES_RES_CMD-------|
        |       Create new device on in ishtp bus        |                               |
        |                        |                       |                               |
        |                        |------------------HOST_CLIENT_PROPERTIES_REQ_CMD------>|
        |                        |                       |                               |
        |                        |<-----------------HOST_CLIENT_PROPERTIES_RES_CMD-------|
        |       Create new device on in ishtp bus        |                               |
        |                        |                       |                               |
        |                        |--Repeat HOST_CLIENT_PROPERTIES_REQ_CMD-till last one--|
        |                        |                       |                               |
     probed()
        |----ishtp_cl_connect--->|----------------- CLIENT_CONNECT_REQ_CMD-------------->|
        |                        |                       |                               |
        |                        |<----------------CLIENT_CONNECT_RES_CMD----------------|
        |                        |                       |                               |
        |register event callback |                       |                               |
        |                        |                       |                               |
        |ishtp_cl_send(
        HOSTIF_DM_ENUM_DEVICES)  |----------fill ishtp_msg_hdr struct write to HW-----  >|
        |                        |                       |                               |
        |                        |                       |<-----IRQ(IPC_PROTOCOL_ISHTP---|
        |                        |                       |                               |
        |<--ENUM_DEVICE RSP------|                       |                               |
        |                        |                       |                               |
for each enumerated device
        |ishtp_cl_send(
        HOSTIF_GET_HID_DESCRIPTOR|----------fill ishtp_msg_hdr struct write to HW-----  >|
        |                        |                       |                               |
        ...Response
        |                        |                       |                               |
for each enumerated device
        |ishtp_cl_send(
     HOSTIF_GET_REPORT_DESCRIPTOR|--------------fill ishtp_msg_hdr struct write to HW-- >|
        |                        |                       |                               |
        |                        |                       |                               |
 hid_allocate_device
        |                        |                       |                               |
 hid_add_device                  |                       |                               |
        |                        |                       |                               |

从主机加载 ISH 固件流程

从 Lunar Lake 一代开始,ISH 固件已分为两个组件,以实现更好的空间优化和更高的灵活性。这些组件包括一个集成到 BIOS 中的引导加载程序,以及一个存储在操作系统文件系统中的主固件。

该过程的工作原理如下

  • 最初,ISHTP 驱动程序会向 ISH 引导加载程序发送一个命令,即 HOST_START_REQ_CMD。作为响应,引导加载程序会发回一个 HOST_START_RES_CMD。此响应包含 ISHTP_SUPPORT_CAP_LOADER 位。随后,ISHTP 驱动程序会检查此位是否已设置。如果已设置,则从主机开始固件加载过程。

  • 在此过程中,ISHTP 驱动程序首先调用 request_firmware() 函数,然后发送一个 LOADER_CMD_XFER_QUERY 命令。在收到来自引导加载程序的响应后,ISHTP 驱动程序会发送一个 LOADER_CMD_XFER_FRAGMENT 命令。在收到另一个响应后,ISHTP 驱动程序会发送一个 LOADER_CMD_START 命令。引导加载程序会响应,然后继续执行主固件。

  • 过程结束后,ISHTP 驱动程序会调用 release_firmware() 函数。

有关更多详细信息,请参阅下面提供的流程描述。

+---------------+                                                    +-----------------+
| ISHTP Driver  |                                                    | ISH Bootloader  |
+---------------+                                                    +-----------------+
        |                                                                     |
        |~~~Send HOST_START_REQ_CMD~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>|
        |                                                                     |
        |<--Send HOST_START_RES_CMD(Includes ISHTP_SUPPORT_CAP_LOADER bit)----|
        |                                                                     |
****************************************************************************************
* if ISHTP_SUPPORT_CAP_LOADER bit is set                                               *
****************************************************************************************
        |                                                                     |
        |~~~start loading firmware from host process~~~+                      |
        |                                              |                      |
        |<---------------------------------------------+                      |
        |                                                                     |
---------------------------                                                   |
| Call request_firmware() |                                                   |
---------------------------                                                   |
        |                                                                     |
        |~~~Send LOADER_CMD_XFER_QUERY~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>|
        |                                                                     |
        |<--Send response-----------------------------------------------------|
        |                                                                     |
        |~~~Send LOADER_CMD_XFER_FRAGMENT~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>|
        |                                                                     |
        |<--Send response-----------------------------------------------------|
        |                                                                     |
        |~~~Send LOADER_CMD_START~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>|
        |                                                                     |
        |<--Send response-----------------------------------------------------|
        |                                                                     |
        |                                                                     |~~~Jump to Main Firmware~~~+
        |                                                                     |                           |
        |                                                                     |<--------------------------+
        |                                                                     |
---------------------------                                                   |
| Call release_firmware() |                                                   |
---------------------------                                                   |
        |                                                                     |
****************************************************************************************
* end if                                                                               *
****************************************************************************************
        |                                                                     |
+---------------+                                                    +-----------------+
| ISHTP Driver  |                                                    | ISH Bootloader  |
+---------------+                                                    +-----------------+

供应商自定义固件加载

在 ISH 内部运行的固件可以由英特尔提供,也可以由供应商使用英特尔提供的固件开发工具包 (FDK) 开发。英特尔会将英特尔构建的固件上游到 linux-firmware.git 存储库,位于路径 intel/ish/ 下。对于 Lunar Lake 平台,英特尔构建的 ISH 固件将命名为 ish_lnlm.bin。希望上游其自定义固件的供应商应遵循以下固件文件命名指南。

  • 固件文件名应使用以下模式之一:

    • ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_NAME_CRC32}_${PRODUCT_SKU_CRC32}.bin

    • ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_SKU_CRC32}.bin

    • ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_NAME_CRC32}.bin

    • ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}.bin

  • ${intel_plat_gen} 表示英特尔平台世代(例如,Lunar Lake 为 lnlm),并且长度不得超过 8 个字符。

  • ${SYS_VENDOR_CRC32} 是来自 DMI 字段 DMI_SYS_VENDORsys_vendor 值的 CRC32 校验和。

  • ${PRODUCT_NAME_CRC32} 是来自 DMI 字段 DMI_PRODUCT_NAMEproduct_name 值的 CRC32 校验和。

  • ${PRODUCT_SKU_CRC32} 是来自 DMI 字段 DMI_PRODUCT_SKUproduct_sku 值的 CRC32 校验和。

在系统启动期间,ISH Linux 驱动程序将尝试按以下顺序加载固件,优先加载具有更精确匹配模式的自定义固件。

  1. intel/ish/ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_NAME_CRC32}_${PRODUCT_SKU_CRC32}.bin

  2. intel/ish/ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_SKU_CRC32}.bin

  3. intel/ish/ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_NAME_CRC32}.bin

  4. intel/ish/ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}.bin

  5. intel/ish/ish_${intel_plat_gen}.bin

驱动程序将加载第一个匹配的固件,并跳过其余的固件。如果找不到匹配的固件,它将按照指定的顺序继续查找下一个模式。如果所有搜索都失败,则将加载默认的英特尔固件,该固件在上述顺序中列在最后。

ISH 调试

为了调试 ISH,使用了事件跟踪机制。要启用调试日志

echo 1 > /sys/kernel/tracing/events/intel_ish/enable
cat /sys/kernel/tracing/trace

Lenovo Thinkpad Yoga 260 上的 ISH IIO sysfs 示例

root@otcpl-ThinkPad-Yoga-260:~# tree -l /sys/bus/iio/devices/
/sys/bus/iio/devices/
├── iio:device0 -> ../../../devices/0044:8086:22D8.0001/HID-SENSOR-200073.9.auto/iio:device0
│   ├── buffer
│   │   ├── enable
│   │   ├── length
│   │   └── watermark
...
│   ├── in_accel_hysteresis
│   ├── in_accel_offset
│   ├── in_accel_sampling_frequency
│   ├── in_accel_scale
│   ├── in_accel_x_raw
│   ├── in_accel_y_raw
│   ├── in_accel_z_raw
│   ├── name
│   ├── scan_elements
│   │   ├── in_accel_x_en
│   │   ├── in_accel_x_index
│   │   ├── in_accel_x_type
│   │   ├── in_accel_y_en
│   │   ├── in_accel_y_index
│   │   ├── in_accel_y_type
│   │   ├── in_accel_z_en
│   │   ├── in_accel_z_index
│   │   └── in_accel_z_type
...
│   │   ├── devices
│   │   │   │   ├── buffer
│   │   │   │   │   ├── enable
│   │   │   │   │   ├── length
│   │   │   │   │   └── watermark
│   │   │   │   ├── dev
│   │   │   │   ├── in_intensity_both_raw
│   │   │   │   ├── in_intensity_hysteresis
│   │   │   │   ├── in_intensity_offset
│   │   │   │   ├── in_intensity_sampling_frequency
│   │   │   │   ├── in_intensity_scale
│   │   │   │   ├── name
│   │   │   │   ├── scan_elements
│   │   │   │   │   ├── in_intensity_both_en
│   │   │   │   │   ├── in_intensity_both_index
│   │   │   │   │   └── in_intensity_both_type
│   │   │   │   ├── trigger
│   │   │   │   │   └── current_trigger
...
│   │   │   │   ├── buffer
│   │   │   │   │   ├── enable
│   │   │   │   │   ├── length
│   │   │   │   │   └── watermark
│   │   │   │   ├── dev
│   │   │   │   ├── in_magn_hysteresis
│   │   │   │   ├── in_magn_offset
│   │   │   │   ├── in_magn_sampling_frequency
│   │   │   │   ├── in_magn_scale
│   │   │   │   ├── in_magn_x_raw
│   │   │   │   ├── in_magn_y_raw
│   │   │   │   ├── in_magn_z_raw
│   │   │   │   ├── in_rot_from_north_magnetic_tilt_comp_raw
│   │   │   │   ├── in_rot_hysteresis
│   │   │   │   ├── in_rot_offset
│   │   │   │   ├── in_rot_sampling_frequency
│   │   │   │   ├── in_rot_scale
│   │   │   │   ├── name
...
│   │   │   │   ├── scan_elements
│   │   │   │   │   ├── in_magn_x_en
│   │   │   │   │   ├── in_magn_x_index
│   │   │   │   │   ├── in_magn_x_type
│   │   │   │   │   ├── in_magn_y_en
│   │   │   │   │   ├── in_magn_y_index
│   │   │   │   │   ├── in_magn_y_type
│   │   │   │   │   ├── in_magn_z_en
│   │   │   │   │   ├── in_magn_z_index
│   │   │   │   │   ├── in_magn_z_type
│   │   │   │   │   ├── in_rot_from_north_magnetic_tilt_comp_en
│   │   │   │   │   ├── in_rot_from_north_magnetic_tilt_comp_index
│   │   │   │   │   └── in_rot_from_north_magnetic_tilt_comp_type
│   │   │   │   ├── trigger
│   │   │   │   │   └── current_trigger
...
│   │   │   │   ├── buffer
│   │   │   │   │   ├── enable
│   │   │   │   │   ├── length
│   │   │   │   │   └── watermark
│   │   │   │   ├── dev
│   │   │   │   ├── in_anglvel_hysteresis
│   │   │   │   ├── in_anglvel_offset
│   │   │   │   ├── in_anglvel_sampling_frequency
│   │   │   │   ├── in_anglvel_scale
│   │   │   │   ├── in_anglvel_x_raw
│   │   │   │   ├── in_anglvel_y_raw
│   │   │   │   ├── in_anglvel_z_raw
│   │   │   │   ├── name
│   │   │   │   ├── scan_elements
│   │   │   │   │   ├── in_anglvel_x_en
│   │   │   │   │   ├── in_anglvel_x_index
│   │   │   │   │   ├── in_anglvel_x_type
│   │   │   │   │   ├── in_anglvel_y_en
│   │   │   │   │   ├── in_anglvel_y_index
│   │   │   │   │   ├── in_anglvel_y_type
│   │   │   │   │   ├── in_anglvel_z_en
│   │   │   │   │   ├── in_anglvel_z_index
│   │   │   │   │   └── in_anglvel_z_type
│   │   │   │   ├── trigger
│   │   │   │   │   └── current_trigger
...
│   │   │   │   ├── buffer
│   │   │   │   │   ├── enable
│   │   │   │   │   ├── length
│   │   │   │   │   └── watermark
│   │   │   │   ├── dev
│   │   │   │   ├── in_anglvel_hysteresis
│   │   │   │   ├── in_anglvel_offset
│   │   │   │   ├── in_anglvel_sampling_frequency
│   │   │   │   ├── in_anglvel_scale
│   │   │   │   ├── in_anglvel_x_raw
│   │   │   │   ├── in_anglvel_y_raw
│   │   │   │   ├── in_anglvel_z_raw
│   │   │   │   ├── name
│   │   │   │   ├── scan_elements
│   │   │   │   │   ├── in_anglvel_x_en
│   │   │   │   │   ├── in_anglvel_x_index
│   │   │   │   │   ├── in_anglvel_x_type
│   │   │   │   │   ├── in_anglvel_y_en
│   │   │   │   │   ├── in_anglvel_y_index
│   │   │   │   │   ├── in_anglvel_y_type
│   │   │   │   │   ├── in_anglvel_z_en
│   │   │   │   │   ├── in_anglvel_z_index
│   │   │   │   │   └── in_anglvel_z_type
│   │   │   │   ├── trigger
│   │   │   │   │   └── current_trigger
...