海思的device_info.hcs 文件理解
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,设备上线)
更多推荐

所有评论(0)