device_info.hcs 字段详解

—— HDF 用户态驱动配置清单的编译期生成与运行时消费全链路分析

一、文件作用总览

1.1 一句话定位

device_info.hcs 是 UHDF(用户态硬件驱动框架)的驱动加载总清单:声明设备有哪些用户态驱动进程(host)、每个进程内加载哪些驱动动态库(moduleName)、每个驱动对外发布什么服务(serviceName)、以什么策略发布(policy)、何时加载(preload)。它不直接操作硬件,也不决定编译内容,而是在系统运行时生效,告诉 HDF 管理器“以什么进程、什么身份、什么顺序加载哪些驱动”。

1.2 它生成两样产物,服务两拨消费者

产物

生成工具

安装位置

消费者

内容

hdf_default.hcb

hc-gen(二进制 HCS 序列化)

/vendor/etc/hdfconfig/

hdf_devmgr(设备管理器)

host 清单 + 全部 deviceNode 字段(moduleName/serviceName/policy/preload/permission/deviceMatchAttr 等)

hdf_devhost.cfg

hc-gen 的 StartupCfgGen

/vendor/etc/init/

init 进程

每个 host 一条服务项:name/path/uid/gid/caps/ondemand/secon

 

1.3 编译链(HCS → 两个产物)

device_info.hcs

└─ hdf.hcs 首行 #include "device_info.hcs"   (hdf.hcs 是 hc-gen 的编译入口)

└─ uhdf/BUILD.gn 两个目标:

     hdf_hcb("hdf_default.hcb")  → hc_gen 模板          → 安装到 hdfconfig

     hdf_cfg("hdf_devhost.cfg")  → hc_gen_start_cfg 模板 → 安装到 init

模板定义:drivers/hdf_core/adapter/uhdf2/hcs/hcs.gni

纳入编译:由产品 bundle.json 引用该 hdf_config 目录

产物生成(out/<产品名>/packages/<设备>/):

     vendor/etc/hdfconfig/hdf_default.hcb

     vendor/etc/init/hdf_devhost.cfg

1.4 运行时消费链(两条并行)

① init 链:init 读 hdf_devhost.cfg → fork 出 hdf_devhost 进程

/vendor/bin/hdf_devhost -i <hostId> -n <hostName> [-p 进程优先级] [-s 线程优先级]

(init 同时按 cfg 的 uid/gid/caps/secon 设置进程身份与 SELinux 上下文)

② devmgr 链:hdf_devmgr 读 hdf_default.hcb

HdfAttributeManagerGetHostList(hdf_attribute.c)→ 按 priority 排序 host

→ DevmgrServiceStartDeviceHost(devmgr_service.c)启动 host 进程

→ DevHostServiceClntInstallDriver → DevHostServiceAddDevice(devhost_service.c)

→ GetDriver(moduleName) → dlopen → Bind → Init → PublishService(serviceName)

二、文件结构:三层节点 + 结构节点的作用

2.1 三层节点模型

层级

HCS 写法

语义

在产物/运行时的角色

host

xxx :: host { ... }

一个用户态驱动进程(对应一个 init 服务、一个 SELinux 域)

hdf_devhost.cfg 服务项 + hdf_default.hcb 的 host 节点

device

xxx_device :: device { ... }

驱动分组(逻辑分组,不单独成进程)

hdf_default.hcb 中 host 的子节点,deviceIdx 参与设备号

deviceNode

device0 :: deviceNode { ... }

一个具体驱动模块(一个 .so + 一个服务)

hdf_default.hcb 中 device 的子节点,运行时对应一个 HdfDeviceNode

 

2.2 五个结构节点的作用

(1) root / device_info(match_attr = "hdf_manager")

device_info 节点上的 match_attr = "hdf_manager" 是 hdf_devmgr 在整棵 HCB 配置树中定位本节点的“锚点”。运行时 GetHdfManagerNode() → HcsGetNodeByMatchAttr(node, "hdf_manager"),逐节点比对 match_attr 属性值,命中即返回 device_info 子树,host/device 全从这棵子树读取。

(2) template host / template device / template deviceNode

文件开头的 template 块是字段默认值声明:host 级(hostName/priority/uid/gid/caps/critical),deviceNode 级(policy/priority/preload/permission/moduleName/serviceName/deviceMatchAttr)。hc-gen 侧 TemplateNodeSeparate() 将 NODE_TEMPLATE 类型节点从配置树中分离,不进入产物输出;模板字段默认值通过 HCS 继承语义在解析期合并到各实例节点。运行时还有第二层兜底:hdf_attribute.c 中 HcsGetUint16 各字段的默认参数,仅在字段完全缺失时生效。

(3) 实例节点名:xxx :: host

host 前面的名字(platform、power、xxx……)只是 HCS 节点标识,不参与任何产物。hc-gen 与 devmgr 认的都是 hostName 字段值。

(4) device 组节点名:xxx_device :: device

device 组名不进任何字段,但决定设备号的一部分:运行时按 device 组的出现顺序编号 deviceIdx。设备号 = host 序号 + device 组序号 + node 序号(MK_DEVID),是 devmgr↔devhost 通信句柄。

(5) deviceNode 实例节点名:device0 / device1

device0/device1 这个 HCS 节点名会成为运行时的 deviceName 字段(不是 serviceName),用于 hdf_devmgr 的 ListAllDevice 查询结果展示。

三、host 级字段逐一详解

host 级字段的编译期消费方是 hc-gen 的 startup_cfg_gen.cpp::GetHostInfo(),产出 hdf_devhost.cfg 的服务项;运行时消费方是 init(进程身份/能力/启动顺序)与 hdf_devmgr(hostName/priority 用于匹配与排序)。

参考:hdf_devhost.cfg 服务项示例(开源示例):

{

    "name" : "sample_host",

    "path" : ["/vendor/bin/hdf_devhost", "-i", "0", "-n", "sample_host"],

    "uid" : "root",

    "gid" : ["sample_host"],

    "caps" : [],

    "secon" : "u:r:sample_host:s0"

}

3.1 hostName —— 进程名 / init 服务名 / SELinux 域名

host 的唯一标识,必须是字符串且非空。同一份 HCS 派生的 init 服务名、进程名、SELinux 安全上下文域名全部由它决定。

环节

行为

编译期 hc-gen

Lookup("hostName") → 作为 hostInfoMap 的 key;生成 "secon":"u:r:{hostName}:s0" 与 -n 参数;输出 "name":"{hostName}"

运行时 devmgr

读 hostName,按 hostName 在 HCB 中定位本 host 的 device 子树(devmgr 与 devhost 靠它对齐)

运行时 devhost

解析 -n → 登记本进程

运行时 SELinux

init 读 cfg 的 secon 字段 → setexeccon("u:r:{hostName}:s0") 设置进程域

 

3.2 priority(host 级)—— 启动先后顺序

取值 0~200,数值越小越先启动(不改变进程调度优先级)。

环节

行为

编译期 hc-gen

Lookup("priority") → 按 hostPriority 升序 sort,决定 hdf_devhost.cfg 服务项排列顺序(init 按序拉起)

运行时 devmgr

读 priority,HdfSListAddOrder 按 priority 排序 host 启动顺序;>200 判无效拒绝

 

用来保证依赖方先于被依赖方启动。

3.3 uid —— 进程运行用户

环节

行为

编译期 hc-gen

hostUID 默认 = hostName;Lookup("uid") 非空则覆盖;输出 "uid"

运行时 init

fork 出服务进程后按 uid setuid

 

3.4 gid —— 进程附加组

环节

行为

编译期 hc-gen

三级 fallback:数组格式 → 单字符串格式 → 默认 hostName;输出 "gid" 数组

运行时 init

设置进程 gid 与附加组

 

3.5 caps —— Linux capability 白名单

环节

行为

编译期 hc-gen

解析数组 → 输出 "caps" 数组

运行时 init

按列表保留进程 capability,其余裁剪

运行时 SELinux

caps 必须同时在 SELinux 策略中 allow,否则 enforcing 下会被拒

 

3.6 critical —— 崩溃重启策略

环节

行为

编译期 hc-gen

解析整数数组 → 输出 "critical" 数组

运行时 init

监控服务退出,按 critical 列表连带重启关联服务

 

3.7 processPriority —— 进程优先级值

环节

行为

编译期 hc-gen

Lookup("processPriority") → 把 -p <值> 追加到 path 数组

运行时 devhost

解析 -p → setpriority 设置进程 nice 值

 

3.8 threadPriority —— 线程实时优先级

环节

行为

编译期 hc-gen

Lookup("threadPriority") → 把 -s <值> 追加到 path 数组

运行时 devhost

解析 -s → sched_setscheduler(SCHED_FIFO) 切到实时调度类

 

3.9 sandbox —— 进程沙箱编号

环节

行为

编译期 hc-gen

Lookup("sandbox") → 输出 "sandbox" 字段

运行时 init

把服务放入指定沙箱

 

3.10 initconfig —— 透传任意 init 服务字段

字符串数组,内容原样输出到 hdf_devhost.cfg 对应服务项中,可覆盖/补充任意 init 字段。

3.11 mallocopt —— 进程内存分配器调优

环节

行为

编译期 hc-gen

按 ":" 拆分 key/value → 作为数字参数拼进 path 数组

运行时 devhost

解析参数 → mallopt(key, value)

 

四、deviceNode 级字段

deviceNode 字段的编译期去向:随 HCS 整体序列化进 hdf_default.hcb;运行时由 hdf_devmgr 的 GetDeviceNodeInfo() 逐个读取,填进 HdfDeviceInfo,再经 AddDevice 传给 hdf_devhost。

4.1 policy —— 服务发布策略(枚举定义于公开的 hdf_device_desc.h)

枚举值

常量

语义

0

SERVICE_POLICY_NONE

不发布服务(仅做初始化动作的驱动)

1

SERVICE_POLICY_PUBLIC

对内核级应用发布服务

2

SERVICE_POLICY_CAPACITY

对内核级+用户级应用发布服务(跨进程可见)

3

SERVICE_POLICY_FRIENDLY

不对外发布,但可被订阅

4

SERVICE_POLICY_PRIVATE

仅进程内可见,外部不可订阅

 

运行时行为:policy==NONE 或 serviceName 为空 → 不发布服务;PUBLIC/CAPACITY 才执行 Bind 与 PublishService;PRIVATE 服务只允许同 devId 订阅者。常见实现中 deviceNode 多采用 policy=2(CAPACITY,跨进程可见)。

4.2 priority —— host 内部驱动加载顺序

取值 0~200,同一 host 内数值越小越先加载。

4.3 preload —— 加载时机(枚举定义于公开的 hdf_device_desc.h)

ENABLE=0(host 启动即加载)、ENABLE_STEP2=1(系统启动完成后延迟加载,用于快启)、DISABLE=2(按需动态加载,不随 host 启动)。

环节

行为

编译期 hc-gen

反向消费:若 host 内任一 deviceNode 的 preload 为 0/1,则 host 不输出 "ondemand":true(host 常驻)

运行时 devmgr

preload≠DISABLE → 入 unloadDevInfos(host 启动即 AddDevice);==DISABLE → 入 dynamicDevInfos

运行时 devmgr

LoadDevice 仅允许 preload==DISABLE 的驱动按需加载

运行时 devmgr

LoadLeftDriver:unloadDevInfos 中 preload==ENABLE_STEP2 的驱动延迟到系统启动完成后加载

运行时 devmgr

unloadDevInfos 为空的 host(全是动态驱动)不预起进程,等按需加载时再起

 

4.4 permission —— 服务访问权限位

unix 权限位(如 0660/0664),模板默认 0664。

4.5 moduleName —— 要 dlopen 的驱动动态库

驱动 .so 文件名,必填(校验非空)。运行时 devhost 按 moduleName 取驱动句柄 → dlopen 真正加载。改它就是换驱动实现。

4.6 serviceName —— 服务注册键

驱动对外发布的服务名,必填。客户端按此名获取服务;服务管理器以 serviceName 哈希为 key 注册(同名覆盖);动态加载按 svcName 查找设备;服务名还映射到 SELinux 服务标签(hdf_service_contexts)。

4.7 deviceMatchAttr —— 驱动私有配置定位锚点

指向 HCB 树中 match_attr 相同的节点,运行时把该节点挂到 deviceObject.property 供驱动读取。把通用清单(device_info)与驱动私有配置关联起来。

4.8 两个派生字段:deviceName 与 deviceId(HCS 里没有,运行时生成)

字段

来源

用途

deviceName

= deviceNode 的 HCS 节点名

进入 token->deviceName,用于 devmgr 查询展示

deviceId

= MK_DEVID(hostId, deviceIdx, deviceNodeIdx)

由 host 序号 + device 组序号 + node 序号拼成 16 位设备号,是 devmgr↔devhost 通信句柄

 

五、端到端实例:一个 host 从 HCS 到运行(开源示例)

5.1 HCS 原文(示例)

sample_host :: host {

    hostName = "sample_host";

    priority = 100;

    sample_device :: device {

        device0 :: deviceNode {

            policy = 2;          // CAPACITY:跨进程发布

            priority = 100;

            moduleName = "libsample_driver.z.so";

            serviceName = "sample_driver_service";

        }

    }

}

5.2 每个字段的完整旅程

字段

HCS 值

编译期

运行时

hostName

sample_host

→ cfg 的 name + secon="u:r:sample_host:s0" + -n 参数

→ init 服务名;setexeccon 进 sample_host 域;devmgr 按它匹配 HCB host 节点

priority

100

→ cfg 服务项排序

→ devmgr host 启动顺序

policy

2 (CAPACITY)

随 HCB 序列化

→ Bind + PublishService 被触发

moduleName

libsample_driver.z.so

随 HCB 序列化

→ GetDriver → dlopen

serviceName

sample_driver_service

随 HCB 序列化

→ 服务管理器注册键;上层客户端按此名获取

 

5.3 启动时序

init 读 hdf_devhost.cfg → fork sample_host 服务项

→ setexeccon("u:r:sample_host:s0")(SELinux 域,type.te 声明过 sample_host 才成功)

→ execv /vendor/bin/hdf_devhost -i 0 -n sample_host

 

hdf_devmgr 读 hdf_default.hcb

→ GetHostNode("sample_host") 定位 sample_host :: host 子树

→ 遍历 device 组,preload≠DISABLE 的 node 入 unloadDevInfos(按 priority 排序)

→ DevHostServiceClntInstallDriver → AddDevice

 

hdf_devhost 侧 DevHostServiceAddDevice

→ GetDriver("libsample_driver.z.so") → dlopen

→ HdfDeviceNodeNewInstance(填 servName/permission/policy/deviceMatchAttr)

→ HdfDeviceLaunchNode:Bind → Init → PublishService

→ DevSvcManagerClntAddService:key = "sample_driver_service"

→ DevmgrServiceClntAttachDevice(上报 devmgr,设备上线)

 

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐