openharmony中HDF驱动框架关键流程说明-观察者模式

在分析openharmony 5.0的HDF驱动框架时我们会发现用了很多面向对象的思想,例如类继承、接口、单例类等,本来应该是好事情,但使用时对象之间的关系交错复杂,不太符合linux内核分层分模块的思路,导致整体理解起来比较困难,再加上"C/S设计模式"和"观察者设计模式"更导致系统理解起来比较麻烦,本文便针对观察者模式这个问题做梳理。C/S设计模式和单例类可参考

观察者模式

本文主要分析openharmony的HDF的观察者模式的实现方式以及使用方法,如果对观察者模式没有了解的码友建议先看下这篇文章(提供了一个c语言版本的观察者模式的示例)。在对观察者模式有一个简单的了解后可以根据下图的类图来理解一下

从openharmony中变量命名的方式中可以看出开发者更倾向于将观察者模式中的两个对象命名为观察者和订阅者(以前都叫观察者和被观察者)。为了更便于理解我在此处写成了被观察者和订阅者

为了加强对整体的理解,我们从核心代码(hdf_core\framework\core\host\src\hdf_service_observer.c)处中可以看出观察者模式的主要实现逻辑。其中主要涉及了以下四个方法:

方法 描述
bool HdfServiceObserverConstruct(struct HdfServiceObserver *observer) 初始化服务观察者
void HdfServiceObserverDestruct(struct HdfServiceObserver *observer) 清空服务列表
int HdfServiceObserverPublishService(struct HdfServiceObserver *observer, const char *svcName, devid_t deviceId,uint16_t policy, struct HdfObject *service) 发布驱动服务
int HdfDeviceSubscribeService( struct HdfDeviceObject *deviceObject, const char *serviceName, struct SubscriberCallback callback) 订阅驱动服务

驱动服务是HDF驱动设备对外提供能力的对象 ,由HDF框架统一管理。驱动服务管理主要包含驱动服务的发布和获取 。HDF框架定义了驱动对外发布服务的策略,由配置文件中的policy字段来控制,policy字段的取值范围以及含义如下:

c 复制代码
typedef enum {
  /* 驱动不提供服务 */
  SERVICE_POLICY_NONE = 0,
  /* 驱动对内核态发布服务 */
  SERVICE_POLICY_PUBLIC = 1,
  /* 驱动对内核态和用户态都发布服务 */
  SERVICE_POLICY_CAPACITY = 2,
  /* 驱动服务不对外发布服务,但可以被订阅 */
  SERVICE_POLICY_FRIENDLY = 3,
  /* 驱动私有服务不对外发布服务,也不能被订阅 */
  SERVICE_POLICY_PRIVATE = 4,
  /* 错误的服务策略 */
  SERVICE_POLICY_INVALID
} ServicePolicy;

使用场景

当驱动需要以接口的形式对外提供能力时,可以使用HDF框架的驱动服务管理能力。

初始化服务观察者

系统启动时HDF框架会通过DeviceManagerStart函数最终调用设备host服务创建DevHostServiceCreate函数,进而调用初始化服务观察者函数,如下所示:

c 复制代码
struct HdfObject *DevHostServiceCreate(void) #
	|-->DevHostServiceConstruct(devHostService)
    	|-->HdfServiceObserverConstruct(&service->observer)

清空服务列表

设备host服务释放时会清空服务列表,调用过程如下:

c 复制代码
void DevHostServiceRelease(struct HdfObject *object)
    |-->struct DevHostService *devHostService = (struct DevHostService *)object
    |-->DevHostServiceDestruct(devHostService)
        |-->HdfServiceObserverDestruct(&service->observer)

订阅服务

订阅服务,此函数为驱动可以使用的接口函数(hdf_core\interfaces\inner_api\host\shared\hdf_device_desc.h),当驱动需要知到加载时间时可以使用此函数来订阅驱动服务(驱动服务和订阅者必须在同一主机上),在订阅的驱动服务被HDF(硬件驱动框架)加载后,框架会主动向订阅者发布服务接口

c 复制代码
int32_t HdfDeviceSubscribeService(struct HdfDeviceObject *deviceObject, const char *serviceName, struct SubscriberCallback callback)
    |-->HdfServiceObserverSubscribeService(&hostService->observer, serviceName, devNode->devId, callback)

通过HDF提供的订阅机制获取

当内核态驱动服务获取者对驱动(同一个host)加载的时机不能感知时,可以通过HDF框架提供的订阅机制来订阅该驱动服务。当该驱动加载完成时,HDF框架会将被订阅的驱动服务发布给订阅者(驱动服务获取者),实现方式如下所示:

c 复制代码
// 订阅回调函数的编写,当被订阅的驱动加载完成后,HDF框架会将被订阅驱动的服务发布给订阅者,通过这个回调函数给订阅者使用。
// object为订阅者的私有数据,service为被订阅的服务对象。
int32_t TestDriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *service)
{
  const struct ISampleDriverService *sampleService =(const struct ISampleDriverService *)service;
  if (sampleService == NULL) {
    return HDF_FAILURE;
  }
  sampleService->ServiceA();
  sampleService->ServiceB(5);
}

// 订阅过程的实现
int32_t TestDriverInit(struct HdfDeviceObject *deviceObject)
{
  if (deviceObject == NULL) {
    HDF_LOGE("Test driver init failed, deviceObject is null!");
    return HDF_FAILURE;
  }

  struct SubscriberCallback callBack;
  callBack.deviceObject = deviceObject;
  callBack.OnServiceConnected = TestDriverSubCallBack;
  int32_t ret = HdfDeviceSubscribeService(deviceObject, "sample_driver", callBack);
  if (ret != HDF_SUCCESS) {
    HDF_LOGE("Test driver subscribe sample driver failed!");
  }
  return ret;
}

发布服务

在系统启动时HDF框架会通过DeviceManagerStart函数最终调用下图的红色部分,即为发布服务的调用过程:

示例说明

HDF驱动服务管理策略的应用

假设我们正在开发一个名为"MyDriver"的设备驱动,该驱动提供了一些特定的功能,我们希望这些功能能够以服务的形式对外暴露,以便其他模块或应用能够使用

1. 配置服务策略

在MyDriver的配置文件中,我们需要指定服务的发布策略 。例如,如果我们希望MyDriver的服务既对内核态开放,也对用户态开放,我们可以将policy字段设置为SERVICE_POLICY_CAPACITY

配置文件(假设为mydriver.hcs)可能看起来像这样:

hcs 复制代码
root {
    device_mydriver {
        device_type = "platform"
        device_node = "/dev/mydriver"
        policy = 2  // 指定服务策略为对内核态和用户态都开放
        service_list = [
            {
                service_name = "MyService"
                service_func = "MyDriverServiceFunc"  // 指向驱动中提供的服务函数
            }
        ]
    }
}

在这个配置中,policy字段被设置为2,意味着MyDriver提供的服务MyService将对内核态和用户态的应用都可见。

2. 清空服务列表

在MyDriver的源代码中,我们需要实现MyDriverServiceFunc这个函数,该函数将包含服务要执行的具体操作。例如:

c 复制代码
int32_t MyDriverServiceFunc(struct HdfDeviceIoClient *client, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    // 在这里执行服务的具体操作
    // ...
    
    // 假设操作成功,返回HDF_SUCCESS
    return HDF_SUCCESS;
}
3. 发布服务

在MyDriver的初始化代码中,我们需要调用HDF框架提供的接口来发布这个服务。但是,在HDF框架中,服务的发布通常是由框架本身根据配置文件自动完成的,开发者不需要显式调用发布服务的接口。不过,开发者需要确保他们的驱动正确实现了服务函数,并且配置文件中的信息准确无误。

4. 订阅和使用服务

其他模块或应用可以通过调用HdfDeviceSubscribeService接口来订阅MyDriver提供的服务。一旦订阅成功,它们就可以调用服务函数来执行特定的操作了。

例如,一个用户态的应用可能想要使用MyDriver提供的服务,它可以这样做:

c 复制代码
struct HdfDeviceObject *deviceObject; // 假设这个对象已经被正确初始化并指向了MyDriver
const char *serviceName = "MyService";
struct SubscriberCallback callback = {
    .OnServiceConnected = MyServiceConnectedCallback,  // 当服务连接成功时调用的回调函数
    .OnServiceDisconnected = MyServiceDisconnectedCallback  // 当服务断开连接时调用的回调函数
};
 
int ret = HdfDeviceSubscribeService(deviceObject, serviceName, callback);
if (ret != HDF_SUCCESS) {
    // 处理订阅失败的情况
}

在这个例子中,MyServiceConnectedCallbackMyServiceDisconnectedCallback是用户定义的回调函数,它们将在服务连接成功或断开连接时被调用。

相关推荐
敢嗣先锋2 小时前
鸿蒙5.0实战案例:基于ArkUI启动冷启动过程最大连续丢帧数问题分析思路&案例
性能优化·移动开发·多线程·harmonyos·arkui·鸿蒙开发
HarmonyOS_SDK2 小时前
智能网络感知,打造极致流畅的鸿蒙原生版中国移动云盘图文体验
harmonyos
SuperHeroWu72 小时前
【HarmonyOS Next】鸿蒙应用进程和线程详解
华为·线程·进程·harmonyos·鸿蒙
MardaWang3 小时前
HarmonyOS开发,遇到 Object.assign(this, source)报错怎么解决?
typescript·harmonyos
敲代码的鱼哇5 小时前
设备唯一ID获取,支持安卓/iOS/鸿蒙Next(uni-device-id)UTS插件
android·ios·uniapp·harmonyos
别说我什么都不会5 小时前
鸿蒙轻内核M核源码分析系列十 软件定时器Swtmr
操作系统·harmonyos
8931519606 小时前
《鸿蒙开发-答案之书》获取视频第一帧和视频时间
harmonyos·鸿蒙系统·鸿蒙开发·鸿蒙教程·鸿蒙视频第一帧·鸿蒙获取视频时长
林钟雪6 小时前
HarmonyOS全栈开发指南:从入门到精通,构建万物智联的未来生态(二)
华为·harmonyos
程序边界7 小时前
深度探索:DeepSeek与鸿蒙HarmonyOS应用开发的深度融合
华为·harmonyos
SameX8 小时前
HarmonyOS Next人脸活体检测技术深度剖析
harmonyos