品讀鴻蒙HDF架構(三)|解讀鴻蒙源碼

品讀鴻蒙HDF架構(三)

侯亮


現在我們繼續研究鴻蒙HDF架構,上回書說到經由HdfDeviceAttach(),HdfDevice節點不但添加進了DevHostService的devices列表,而且還和一個DeviceNodeExt聯繫起來了,呈現的示意圖大致如下:

接着,HdfDeviceAttach()最後會調用nodeIf->LaunchNode(),這一步實際上調用的是HdfDeviceLaunchNode(),代碼截選如下:
【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

int HdfDeviceLaunchNode(struct HdfDeviceNode *devNode, struct IHdfDevice *devInst)
{
    struct HdfDevice *device = (struct HdfDevice *)devInst;
    . . . . . .
    struct HdfDriverEntry *driverEntry = devNode->driverEntry;
    const struct HdfDeviceInfo *deviceInfo = devNode->deviceInfo;
    struct IHdfDeviceToken *deviceToken = NULL;

    . . . . . .
    int ret = driverEntry->Init(&devNode->deviceObject);
    . . . . . .
    ret = HdfDeviceNodePublishService(devNode, deviceInfo, devInst);
    . . . . . .
    deviceToken = devNode->token;
    ret = DevmgrServiceClntAttachDevice(deviceInfo, deviceToken);
    . . . . . .
    return ret;
}

“Launch”本就是啓動之意,在這裏就是指啓動與HdfDevice對應的驅動服務。所以此處會先調用一下driverEntry->Init(),並傳入參數&devNode->deviceObject。這意味着要求驅動程序在初始化時,回填一下deviceObject裏的service域。

接着,主要執行了兩個動作:
1)發佈驅動服務
2)掛接設備
我們會分兩小節來闡述。

1 發佈驅動服務

初始化完成後,就可以“發佈”這個驅動服務了,HdfDeviceNodePublishService()的代碼截選如下:
【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

static int HdfDeviceNodePublishService(struct HdfDeviceNode *devNode, 
                                       const struct HdfDeviceInfo *deviceInfo, 
                                       struct IHdfDevice *device)
{
    int status = HDF_SUCCESS;
    . . . . . .
    struct IDeviceNode *nodeIf = &devNode->super;
    if ((deviceInfo->policy == SERVICE_POLICY_PUBLIC) ||
        (deviceInfo->policy == SERVICE_POLICY_CAPACITY)) {
        if (nodeIf->PublishService != NULL) {
            // 其實調用的是 DeviceNodeExtPublishService()
            status = nodeIf->PublishService(devNode, deviceInfo->svcName);
        }
    }
    if (status == HDF_SUCCESS) {
        status = HdfDeviceNodePublishLocalService(devNode, deviceInfo);
    }
    return status;
}

從字面上理解,如果一個設備是“公共型”或“功能型”的,則會調用nodeIf->PublishService()發佈對應的驅動服務。而我們以前研究過,在HdfDeviceNode構造之時,我們可以看到爲PublishService域設定了DeviceNodeExtPublishService()函數指針,因此上面代碼中調用PublishService的地方,其實就是在調用這個函數。

【drivers/hdf/lite/manager/src/Hdf_device_node_ext.c】

static int DeviceNodeExtPublishService(struct HdfDeviceNode *inst, const char *serviceName)
{
    const struct HdfDeviceInfo *deviceInfo = NULL;
    struct HdfDeviceObject *deviceObject = NULL;
    struct DeviceNodeExt *devNodeExt = (struct DeviceNodeExt *)inst;
    . . . . . .
    int ret = HdfDeviceNodePublishPublicService(inst, serviceName);
    . . . . . .
    deviceInfo = inst->deviceInfo;
    deviceObject = &devNodeExt->super.deviceObject;
    . . . . . .
    if (deviceInfo->policy == SERVICE_POLICY_CAPACITY) {
        devNodeExt->ioService = HdfIoServiceBind(serviceName, deviceInfo->permission);
        if (devNodeExt->ioService != NULL) {
            devNodeExt->ioService->target = (struct HdfObject*)(&inst->deviceObject);
            static struct HdfIoDispatcher dispatcher = {
                .Dispatch = DeviceNodeExtDispatch
            };
            devNodeExt->ioService->dispatcher = &dispatcher;
        } else {
            . . . . . .
        }
    }
    return HDF_SUCCESS;
}

這樣說來,發佈服務時其實分了兩個部分,一個是發佈public部分,一個是發佈local部分。分別對應HdfDeviceNodePublishPublicService()和HdfDeviceNodePublishLocalService()。

1.1 發佈Public Service部分

【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

int HdfDeviceNodePublishPublicService(struct HdfDeviceNode *devNode, const char *svcName)
{
    if ((devNode == NULL) || (devNode->deviceObject.service == NULL)) {
        HDF_LOGE("device method is null");
        return HDF_FAILURE;
    }
    return DevSvcManagerClntAddService(svcName, &devNode->deviceObject);
}

【drivers/hdf/frameworks/core/host/src/Devsvc_manager_clnt.c】

int DevSvcManagerClntAddService(const char *svcName, struct HdfDeviceObject *service)
{
    struct DevSvcManagerClnt *devSvcMgrClnt = DevSvcManagerClntGetInstance();
    . . . . . .
    struct IDevSvcManager *serviceManager = devSvcMgrClnt->devSvcMgrIf;
    . . . . . .
    // 其實調用的是 DevSvcManagerAddService()
    return serviceManager->AddService(serviceManager, svcName, service);
}

【drivers/hdf/frameworks/core/host/src/Devsvc_manager_clnt.c】

struct DevSvcManagerClnt *DevSvcManagerClntGetInstance()
{
    static struct DevSvcManagerClnt *instance = NULL;
    if (instance == NULL) {
        static struct DevSvcManagerClnt singletonInstance;
        DevSvcManagerClntConstruct(&singletonInstance);
        instance = &singletonInstance;
    }
    return instance;
}

從代碼看,系統中有一個“設備服務管理器”(DevSvcManager),那些功能型設備都會把自己註冊進它。這個倒有點兒像Android裏的SMS(Service Manager Service),所有系統核心服務都會向SMS裏註冊自己,以便其他應用可以從SMS查詢並獲取服務代理。實際上,鴻蒙系統在不少方面倒的確和Android有一定類比性,這個以後我們再對比看看,目前先放下不談。

註冊設備服務的那句serviceManager->AddService()實際上調用的是DevSvcManagerAddService(),該函數會嘗試向“設備服務管理器”裏添加一個管理節點(DevSvcRecord):
【drivers/hdf/frameworks/core/manager/src/Devsvc_manager.c】

int DevSvcManagerAddService(struct IDevSvcManager *inst, const char *svcName, 
struct HdfDeviceObject *service)
{
    struct DevSvcManager *devSvcManager = (struct DevSvcManager *)inst;
    . . . . . .
    struct DevSvcRecord *record = DevSvcRecordNewInstance();
    . . . . . .
    record->key = HdfStringMakeHashKey(svcName, 0);
    record->value = service;
    OsalMutexLock(&devSvcManager->mutex);
    HdfSListAdd(&devSvcManager->services, &record->entry);
    OsalMutexUnlock(&devSvcManager->mutex);
    return HdfServiceObserverPublishService(&devSvcManager->observer, svcName, 0, SERVICE_POLICY_PUBLIC, (struct HdfObject *)service->service);
}

【drivers/hdf/frameworks/core/host/src/Hdf_service_observer.c】

int HdfServiceObserverPublishService(struct HdfServiceObserver *observer,
    const char *svcName, uint32_t matchId, uint16_t policy, struct HdfObject *service)
{
    struct HdfServiceObserverRecord *serviceRecord = NULL;
    uint32_t serviceKey = HdfStringMakeHashKey(svcName, 0);
    . . . . . .
    serviceRecord = (struct HdfServiceObserverRecord *)HdfSListSearch(&observer->services, serviceKey, HdfServiceObserverRecordCompare);
    if (serviceRecord == NULL) {
        serviceRecord = HdfServiceObserverRecordObtain(serviceKey);
        if (serviceRecord == NULL) {
            HDF_LOGE("PublishService failed, serviceRecord is null");
            return HDF_FAILURE;
        }
        serviceRecord->publisher = service;
        serviceRecord->matchId = matchId;
        serviceRecord->policy = policy;
        HdfSListAdd(&observer->services, &serviceRecord->entry);
    } else {
        serviceRecord->publisher = service;
        HdfServiceObserverRecordNotifySubscribers(serviceRecord, matchId, policy);
    }
    return HDF_SUCCESS;
}

對於“設備服務管理器”而言,當它要管理一個設備服務時,主要需要兩個Record:
1)DevSvcRecord:每個服務對應一個DevSvcRecord,這個節點會插入DevSvcManager內部的services鏈表。如果發佈服務時,發現對應的DevSvcRecord已經存在了,則會向所有訂閱者發出通知。
2)HdfServiceObserverRecord:每個服務對應一個HdfServiceObserverRecord,這個節點會插入DevSvcManager內的observer部分(內部的services鏈表)裏。每個HdfServiceObserverRecord負責維護一個“訂閱者”鏈表,記錄所有對該服務感興趣的訂閱者。

DevSvcRecord的value域是個HdfDeviceObject *指針,其實指向的就是和設備對應的DeviceNodeExt節點的deviceObject部分,根據我們以前儲備的知識,我們知道這個deviceObject部分的service域指向的就是設備驅動實現的IDeviceIoService接口。

另外,從前面代碼的serviceRecord->publisher = service一句,可以看到HdfServiceObserverRecord的publisher域,其實也是指向設備驅動實現的IDeviceIoService接口的。這樣我們可以繪製如下的示意圖:

當然,一開始HdfServiceObserverRecord裏是“訂閱者”鏈表爲空啦,不過日後如果有其他服務註冊爲訂閱者了,HdfServiceObserverRecordNotifySubscribers()就可以向它們發送通知了。發通知函數的代碼如下:
【drivers/hdf/frameworks/core/host/src/Hdf_observer_record.c】

void HdfServiceObserverRecordNotifySubscribers(struct HdfServiceObserverRecord *record, 
uint32_t matchId, uint16_t policy)
{
    struct HdfSListIterator it;
    . . . . . .
    OsalMutexLock(&record->obsRecMutex);
    HdfSListIteratorInit(&it, &record->subscribers);
    while (HdfSListIteratorHasNext(&it)) {
        struct HdfServiceSubscriber *subscriber = (struct HdfServiceSubscriber *)HdfSListIteratorNext(&it);
        if ((matchId == subscriber->matchId) || (policy != SERVICE_POLICY_PRIVATE)) {
            subscriber->state = HDF_SUBSCRIBER_STATE_READY;
            if (subscriber->callback.OnServiceConnected != NULL) {
                subscriber->callback.OnServiceConnected(subscriber->callback.deviceObject, record->publisher);
            }
        }
    }
    OsalMutexUnlock(&record->obsRecMutex);
}

其實就是在遍歷訂閱者鏈表,回調其callback部分的OnServiceConnected()函數。

訂閱者鏈表裏的每個節點是一個HdfServiceSubscriber,示意圖如下:

以上這些其實都體現了鴻蒙系統裏的一個觀念,那就是“設備”其實可以被理解爲“服務”。在單機系統裏,一個設備的驅動程序可以被理解爲一種特殊的庫,上層軟件通過類似函數調用的方式來調用庫,從而操作這個設備。但如果要跨機器地操作設備,那麼就不能直接調用函數了。一種較好地方式是將目標設備包裝成一個邏輯上的服務,然後供大家使用。所以就必須把“驅動層次”和“服務層次”關聯起來,這纔有了前文所說的那麼多數據機構。現在我們畫一張大一點的示意圖,把以上概念串一下:

圖中畫出了體現“設備(驅動)層次”和“服務層次”的兩大管理者——DevmgrService和DevSvcManager,可供大家參考。

1.2 發佈Local Service部分

看完了發佈Public Service的部分,我們接着看HdfDeviceNodePublishService()裏發佈Local Service的部分。此時調用的是HdfDeviceNodePublishLocalService()。
【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

static int HdfDeviceNodePublishLocalService(
    struct HdfDeviceNode *devNode, const struct HdfDeviceInfo *deviceInfo)
{
    uint32_t matchId;
    . . . . . .
    struct DevHostService *hostService = devNode->hostService;
    . . . . . .
    matchId = HdfMakeHardwareId(deviceInfo->hostId, deviceInfo->deviceId);
    return HdfServiceObserverPublishService(&hostService->observer, deviceInfo->svcName,
        matchId, deviceInfo->policy, (struct HdfObject *)devNode->deviceObject.service);
}

請注意,雖然也是在調用HdfServiceObserverPublishService(),但傳入的第一個參數是&hostService->observer。也就是說,Public Service對應的監聽部分,記錄在DevSvcManager裏,而Local Service對應的監聽部分,則記錄在其所屬的DevHostService裏。

現在我們可以畫一張發佈驅動的調用關係圖:


2 掛接設備

我們回過頭繼續說前文的HdfDeviceLaunchNode()部分。該函數在調用完HdfDeviceNodePublishService()之後,接着就會調用DevmgrServiceClntAttachDevice()。

【drivers/hdf/frameworks/core/host/src/Devmgr_service_clnt.c】

int DevmgrServiceClntAttachDevice(const struct HdfDeviceInfo *deviceInfo, 
struct IHdfDeviceToken *deviceToken)
{
    struct IDevmgrService *devMgrSvcIf = NULL;
    struct DevmgrServiceClnt *inst = DevmgrServiceClntGetInstance();
    . . . . . .
    devMgrSvcIf = inst->devMgrSvcIf;
    . . . . . .
    // 實際調用的是 DevmgrServiceAttachDevice()
    return devMgrSvcIf->AttachDevice(devMgrSvcIf, deviceInfo, deviceToken);
}

此處調用了DevmgrServiceAttachDevice():
【drivers/hdf/frameworks/core/manager/src/Devmgr_service.c】

static int DevmgrServiceAttachDevice(struct IDevmgrService *inst, const struct HdfDeviceInfo *deviceInfo, struct IHdfDeviceToken *token)
{
    . . . . . .
    struct DevHostServiceClnt *hostClnt = DevmgrServiceFindDeviceHost(inst, deviceInfo->hostId);
    . . . . . .
    struct IDevHostService *hostService = hostClnt->hostService;
    . . . . . .
    struct DeviceTokenClnt *tokenClnt = DeviceTokenClntNewInstance(token);
    . . . . . .
    tokenClnt->deviceInfo = deviceInfo;
    HdfSListAdd(&hostClnt->devices, &tokenClnt->node);
    return HDF_SUCCESS;
}

主要就是向對應的DevHostServiceClnt的devices鏈表裏,添加一個DeviceTokenClnt節點。簡單地說就是,一個DevHostServiceClnt和一個DevHostService對應,每當向DevHostService裏添加一個HdfDevice節點,相應地就需要在DevHostServiceClnt裏添加一個DeviceTokenClnt節點。該節點的tokenIf域記錄的IHdfDeviceToken指針,來自於DeviceNodeExt的token域。

說起來,DeviceNodeExt的token其實在DeviceNodeExt構造之時就創建了:
【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

void HdfDeviceNodeConstruct(struct HdfDeviceNode *devNode)
{
    if (devNode != NULL) {
        struct IDeviceNode *nodeIf = &devNode->super;
        HdfDeviceObjectConstruct(&devNode->deviceObject);
        devNode->token = HdfDeviceTokenNewInstance();
        nodeIf->LaunchNode = HdfDeviceLaunchNode;
        nodeIf->PublishService = HdfDeviceNodePublishPublicService;
    }
}

其中創建token時,調用的是HdfDeviceTokenNewInstance()。

【drivers/hdf/frameworks/core/host/src/Hdf_device_token.c】

struct IHdfDeviceToken *HdfDeviceTokenNewInstance()
{
    return (struct IHdfDeviceToken *)HdfObjectManagerGetObject(HDF_OBJECT_ID_DEVICE_TOKEN);
}

【drivers/hdf/frameworks/core/host/src/Hdf_device_token.c】

struct HdfObject *HdfDeviceTokenCreate()
{
    struct HdfDeviceToken *token =
        (struct HdfDeviceToken *)OsalMemCalloc(sizeof(struct HdfDeviceToken));
    if (token != NULL) {
        HdfDeviceTokenConstruct(token);
    }
    return (struct HdfObject *)token;
}

其中HdfDeviceToken的定義如下:

struct HdfDeviceToken {
    struct HdfSListNode node;
    struct IHdfDeviceToken super;
};

咦,怎麼又有bug的味道,HdfDeviceToken裏應該把super放到第一個吧,否則怎麼強制轉化成struct HdfObject*呢?好在這個bug的危害不太大,後續版本可以調整一下。

現在我們可以再畫一張圖看看:

3 小結

經過以上分析,我們頭腦中已經可以形成一套比較清楚的HDF邏輯結構了。總之就是將“設備(驅動)層次”和“服務層次”聯繫起來,該加的observer機制加上。好了,這次就先寫到這兒,以後我們再補充其他內容。

(本文參與了「解讀鴻蒙源碼」技術徵文,歡迎正在閱讀的你也加入。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章