開源項目 OpenHarmony是每個人的 OpenHarmony
OpenHarmony系統平臺驅動概述
OpenHarmony系統平臺驅動(PlatformDriver),即平臺設備驅動,它用于驅動平臺設備(PlatformDevice),為系統及外設驅動提供訪接口。這里的平臺設備,泛指I2C/UART等總線、以及GPIO/RTC等SOC片內硬件資源。
OpenHarmony系統平臺驅動框架是OpenHarmony系統驅動框架的重要組成部分,它基于HDF驅動框架、操作系統適配層(OSAL, operating system abstraction layer)以及驅動配置管理機制,為各類平臺設備驅動的實現提供標準模型。
OpenHarmony系統平臺驅動框架為外設提供了標準的平臺設備訪問接口,使其不必關注具體硬件及OS平臺;同時為平臺設備驅動提供統一的適配接口,使其只關注自身硬件的控制。
為實現這個目標,OpenHarmony系統平臺驅動框架滿足如下特性:
統一的平臺設備訪問接口:對平臺設備操作接口進行統一封裝,屏蔽不同SOC平臺硬件差異以及不同OS形態差異。
統一的平臺驅動適配接口:為平臺設備驅動提供統一的適配接口,使其只關注自身硬件的控制,而不必關注設備管理及公共業務流程。
提供設備注冊、管理、訪問控制等與SOC無關的公共能力。
OpenHarmony系統平臺驅動框架目前支持的設備類型包括但不限于:I2C/SPI/UART/MIPI_DSI/SDIO/GPIO/PWM/WATCHDOG/RTC/DMA
OpenHarmony平臺驅動框架介紹
OpenHarmony系統平臺驅動框架組成
OpenHarmony系統平臺驅動框架主要由平臺接口層、平臺核心層以及平臺適配層三個部分組成。
1)平臺接口層以API的形式提供標準的平臺設備訪問接口。
平臺接口層以設備句柄加配套API的形式對外提供統一的、標準的訪問接口。
設備句柄是DevHandle類型的實例,通過不同設備模塊提供的Open/Close方法進行獲取、釋放。成功獲取設備句柄后,即可使用相應的API執行設備操作。例如通過I2cTransfer完成一次I2C數據傳輸。
這是一種代理模式,即接口層API不直接引用實際設備對象,而是通過DevHandle作為代理,間接訪問設備;而所有來自外設驅動的訪問,都建議走接口層,以獲得最佳的穩定性。
不同類型設備的API使用,請參考如下官方文檔的平臺驅動章節:
https://device.OpenHarmony系統.com/cn/docs/develop/drive/oem_drive_hdfdev-0000001051715456
2)平臺核心層提供平臺設備模型及公共業務框架。
提供統一適配接口:定義了標準的設備對象模型,驅動程序僅需關注標準對象模型的適配。
抽取公共業務框架:將不同設備模塊的公共流程、算法加以抽取,使得具體設備驅動更加輕薄。
設備管理:設備注冊、注銷、設備查找、訪問控制。
3)平臺適配層提供特定平臺設備的適配驅動,并遵守核心層約束。
驅動具體平臺設備硬件,并創建對應的設備模型對象,注冊到核心層納入統一管理。
平臺接口層分析
前面說過,在接口層,我們用DevHandle類型的設備句柄表示一個平臺設備對象,然后針對不同類型設備提供一套標準的API方法用于設備訪問。那么設備句柄和真實的設備對象如何關聯呢?
查看DevHandle的定義,發現它就是一個void類型指針:
/**
* @brief Defines the common device handle of the platform driver.
*
* The handle is associated with a specific platform device and is used as the
* first input parameter for all APIs of the platform driver.
*
* @since 1.0
*/
typedef void* DevHandle;
實際上,在內核態,這個指針可以直接指向實際設備對象,但是對于某些類型的平臺設備,我們需要在用戶態提供同樣的DevHandle類型及配套API,而實際設備對象在內核空間,我們無法直接獲取和使用內核空間的地址。
我們的解決辦法是將平臺設備對象實現為一個HDF設備服務,這樣借助HDF DeviceManager的設備服務機制,可以在用戶態、內核態同時獲取到設備服務,而用戶態同內核態通信的問題交由HDF DeviceManager處理。此時,DevHandle只需要關聯到這個設備服務即可,而void*類型保證了足夠的靈活性。
根據DevHandle和設備對象關聯方式的不同,接口層的設計有三種模式,下面將一一講解。
1.獨立服務模式
這種模式用于需要在用戶態和內核態同時提供API的設備類型,DevHandle同設備對象的關聯方式為:
用戶態:關聯到平臺設備對應的設備服務
內核態度:關聯到實際平臺設備對象或其設備服務(在內核態兩者可互相轉換)
這樣,每一個設備對象,會獨立發布一個設備服務,來處理外部訪問,服務收到API的訪問請求之后,通過提取請求參數,并調用實際設備對象的相應內部方法。
這種方式的優點是管理比較簡單,因為它借助了HDF DeviceManager的服務管理能力;但是缺點是需要為每一個設備對象配置設備節點,以便發布設備服務。
這種模式的典型實踐是UART模塊,這在后面介紹驅動適配時會詳細介紹。
2.統一服務模式
有時候,同一類型的設備對象可能會很多,例如I2C模塊,可能同時有十幾個控制器。如果采用獨立服務的模式,每一個控制器,作為一個平臺設備,為其創建一個設備服務,那么將會有十幾個服務被創建,不光要配置很多設備節點,而且這些服務還會占用內存資源。
這時,我們可以為一類設備對象,創建一個平臺設備管理器(PlatformManager)對象,并同時對外發布一個管理器服務,由這個管理器服務來統一處理外部訪問。當用戶需要打開某個設備時,先通過HDF DeviceManager獲取到管理器服務,然后管理器服務根據用戶指定參數查找到指定設備,并返回一個設備描述符,而這個描述符仍然可以由DevHandle類型表示。
這種模式的實踐代表是I2C模塊,PlatformManager實現為I2cManager,而PlatformDevice則是I2cCntlr,感興趣的讀者可以閱讀一下drivers/framework下的i2c_if.c/i2c_core.c一探究竟。
3.無服務模式
這種模式用于不需要在用戶態提供API的設備類型或者沒有用戶態、內核區分的OS系統,其關聯方式是DevHandle直接指向設備對象內核態地址。而PlatformManager的實現比較自由,它不需要實現設備服務,只需做好某種類型的設備管理即可,甚至在C語言中,由于無法進行OOP編程,很多模塊直接將這個功能面向過程化了,使得沒有一個具體的結構體與之對應。但是,我們仍然強調PlatformManager這個概念,也期望隨著后續平臺驅動框架的演進,逐步完善、規范化,形成更加統一的編程風格。
平臺核心層分析
平臺核心層的作用是承上啟下,其主要內容包括:
提供適配接口:為具體的平臺設備驅動提供統一的適配接口
平臺驅動框架為不同設備類型,定義了標準的設備對象模型,具體設備驅動只需要關注標準設備對象的適配即可
提供設備管理:提供設備的注冊、注銷、查找等功能、訪問控制等能力
核心層會提供一系列內部方法,用于設備的注冊、注銷,設備對象的查找、獲取、釋放,以及處理多線程訪問。例如當我們向核心層注冊一個I2C控制器對象時,使用I2cCntlrAdd;當希望獲取一個I2C控制器對象時,通過I2cCntlrGet并指定控制器編號;當不再使用這個對象時,還需要通過I2cCntlrPut釋放。這樣做的好處是將每一個具體的操作步驟高度抽象化,減小同平臺接口層及平臺適配層的耦合面,便于業務解耦、演進。如果后續,我們由于業務需求需要對I2cCntlr對象進行引用計數,那么只需要修改I2cCntlrGet/Put這對方法的實現即可,并不會影響平臺接口層和平臺適配層。
公共業務實現:抽取公共的業務流程、算法
凡是跟特定硬件無關的業務邏輯,都會被抽取到核心層,例如RTC時鐘的時間格式轉換算法,GPIO模塊的線程中斷實現等等。
平臺適配層實現
適配層提供具體平臺硬件設備的驅動,按照核心層定義的模型創建設備對象,并完成對象的初始化(包括必要的成員變量初始化以及鉤子方法掛接,以及相關的硬件初始化操作),最后使用核心層提供的注冊方法將設備對象注冊到核心層納入統一管理。
OpenHarmony系統平臺驅動適配介紹
下面以uart/i2c/gpio三個典型模塊為例介紹平臺驅動適配的一般方法
UART模塊的適配
UART模塊適配的核心環節,是UartHost對象的創建、初始化及注冊。
UART模塊采用的是獨立服務模式,要求每一個UartHost對象關聯一個HDF設備服務,因此:
1).device_info.hcs中為每一個UART控制器配置一個HDF設備節點
device_uart :: device {
device0 :: deviceNode {
policy = 1;
priority = 40;
permission = 0644;
moduleName = "HDF_PLATFORM_UART";
serviceName = "HDF_PLATFORM_UART_0";
deviceMatchAttr = "hisilicon_hi35xx_uart_0";
}
device1 :: deviceNode {
policy = 2;
permission = 0644;
priority = 40;
moduleName = "HDF_PLATFORM_UART";
serviceName = "HDF_PLATFORM_UART_1";
deviceMatchAttr = "hisilicon_hi35xx_uart_1";
}
}
服務policy大于等于1(如需對用戶態可見為2,僅內核態可見為1);
moduleName需要與驅動Entry中moduleName 保持一致;
ServiceName必須要按照HDF_PLATFORM_UART_X的格式,X為UART控制器編號
deviceMatchAttr用于配置控制器私有數據,要與uart_config.hcs中對應控制器保持一致,如不需要則忽略
2).uart_config.hcs中為每一個UART控制器配置私有數據
如果控制器需要配置一些私有數據,例如寄存器基地址,初始化波特率等等,可以在uart_config.hcs中配置,該文件將在產品配置目錄的hdf.hcs中導入,具體路徑可由產品自由配置。
root {
platform {
template uart_controller {
match_attr = "";
num = 0;
baudrate = 115200;
fifoRxEn = 1;
fifoTxEn = 1;
flags = 4;
regPbase = 0x120a0000;
interrupt = 38;
iomemCount = 0x48;
}
controller_0x120a0000 :: uart_controller {
match_attr = "hisilicon_hi35xx_uart_0";
}
controller_0x120a1000 :: uart_controller {
num = 1;
baudrate = 9600;
regPbase = 0x120a1000;
interrupt = 39;
match_attr = "hisilicon_hi35xx_uart_1";
}
}
要注意的一點是每個控制器要獨立配置一個uart_controller節點,并且其match_attr要與device_info.hcs中的deviceMatchAttr一致。
3).驅動的Entry結構需要有Bind方法,用于綁定服務
struct HdfDriverEntry g_hdfUartDevice = {
.moduleVersion = 1,
.moduleName = "HDF_PLATFORM_UART",
.Bind = HdfUartDeviceBind,
.Init = HdfUartDeviceInit,
.Release = HdfUartDeviceRelease,
};
HDF_INIT(g_hdfUartDevice);
注意:在Bind方法中,要使用UartHostCreate創建控制器對象并完成服務綁定。
static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device)
{
HDF_LOGI("%s: entry", __func__);
if (device == NULL) {
return HDF_ERR_INVALID_OBJECT;
}
return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS;
}
4).UartHostCreate的實現屏蔽了一些細節
struct UartHost *UartHostCreate(struct HdfDeviceObject *device)
{
struct UartHost *host = NULL;
host = (struct UartHost *)OsalMemCalloc(sizeof(*host));
host->device = device;
device->service = &(host->service);
host->device->service->Dispatch = UartIoDispatch;
OsalAtomicSet(&host->atom, 0);
host->priv = NULL;
host->method = NULL;
return host;
}
該方法中將UartHost對象同HdfDeviceObject進行了關聯:關鍵環節是為HdfDeviceObject的service成員進行賦值,使其指向UartHost的IDeviceIoService類型的成員對象;同時為service成員的Dispatch方法賦值。這樣:
為HdfDeviceObject對象綁定了IDeviceIoService類型的服務對象
UartHost和其IDeviceIoService類型的成員對象service可以相互轉換
通過UartHost對象即可獲取HdfDeviceObject對象
通過HdfDeviceObject對象即可間接獲取UartHost對象(先獲取service再轉為host)
5).在Init方法中完成UartHost對象的初始化
int32_t HdfUartDeviceInit(struct HdfDeviceObject *device)
{
int32_t ret;
struct UartHost *host = NULL;
host = UartHostFromDevice(device);
ret = Hi35xxAttach(host, device);
host->method = &g_uartHostMethod;
return ret;
}
這里通過UartHostFromDevice從HdfDeviceObject對象獲取之前關聯的UartHost對象,然后調用Hi35xxAttach方法完成host對象的初始化
最后為host對象掛接鉤子方法,這里不再一一分析這些鉤子方法,建議讀者直接去查看源碼。
struct UartHostMethod g_uartHostMethod = {
.Init = Hi35xxInit,
.Deinit = Hi35xxDeinit,
.Read = Hi35xxRead,
.Write = Hi35xxWrite,
.SetBaud = Hi35xxSetBaud,
.GetBaud = Hi35xxGetBaud,
.SetAttribute = Hi35xxSetAttribute,
.GetAttribute = Hi35xxGetAttribute,
.SetTransMode = Hi35xxSetTransMode,
.pollEvent = Hi35xxPollEvent,
};
總結
UART適配的關鍵是要在驅動Entry的Bind方法中創建UartHost對象,而且是使用UartHostCreate創建。這個創建動作同時也是注冊的動作,因為它將UartHost以HDF設備服務的形式同HdfDeviceObject進行綁定,這樣就完成了服務的發布,HDF Manager對設備服務的管理也就是對UartHost的管理,核心層可以通過HDF提供的服務獲取接口來訪問UartHost。
UART適配采用獨立服務模式,每一個UartHost對象同時也是一個設備服務,其優點是可以直接利用HDF Manager進行管理;缺點是需要在device_info.hcs為每一個UartHost對象定義設備節點。
說明
UART模塊適配涉及到的代碼示例片段來自device/hisilicon/drivers/uart/
I2C模塊適配
I2C模塊適配的核心環節是I2cCntlr對象的創建、初始化及注冊。
I2C采用的是統一服務模式,需要一個設備服務來作為I2C模塊的管理器,統一處理外部訪問。
1、device_info.hcs配置
device_i2c :: device {
device0 :: deviceNode {
policy = 2;
priority = 50;
permission = 0644;
moduleName = "HDF_PLATFORM_I2C_MANAGER";
serviceName = "HDF_PLATFORM_I2C_MANAGER";
deviceMatchAttr = "hdf_platform_i2c_manager";
}
device1 :: deviceNode {
policy = 0;
priority = 55;
permission = 0644;
moduleName = "hi35xx_i2c_driver";
serviceName = "HI35XX_I2C_DRIVER";
deviceMatchAttr = "hisilicon_hi35xx_i2c";
}
}
首先第一個設備節點必須是I2C管理器,其各項參數必須如上一樣設置。其中:
policy:這個同UART,具體配置為1或2取決于是否對用戶態可見
moduleName:固定為HDF_PLATFORM_I2C_MANAGER
serviceName:固定為HDF_PLATFORM_I2C_MANAGER
deviceMatchAttr:沒有使用,可忽略
而從第二個設備節點開始,配置具體I2C控制器信息。這里device1 并不表示某一路I2C控制器,而是一個資源性質設備,用于描述一類I2C控制器的信息。
服務policy等于0,不需要發布服務;
moduleName用于指定驅動成語,需要與期望的驅動Entry中的moduleName一致;
ServiceName不需要使用,可忽略;
deviceMatchAttr用于配置控制器私有數據,要與i2c_config.hcs中對應控制器保持
同樣,具體的控制器信息在i2c_config.hcs中,由具體產品在hdf.hcs中導入。
2、i2c_config.hcs中可配置多個控制器信息
root {
platform {
i2c_config {
match_attr = "hisilicon_hi35xx_i2c";
template i2c_controller {
bus = 0;
reg_pbase = 0x120b0000;
reg_size = 0xd1;
irq = 0;
freq = 400000;
clk = 50000000;
}
controller_0x120b0000 :: i2c_controller {
bus = 0;
}
controller_0x120b1000 :: i2c_controller {
bus = 1;
reg_pbase = 0x120b1000;
}
}
}
}
可以看到,這里配置了多個控制器的信息,而這些信息將在驅動程序員進行逐一解析、處理,生成對應的I2cCntlr對象。
3、I2C管理器服務的驅動由核心層實現
struct HdfDriverEntry g_i2cManagerEntry = {
.moduleVersion = 1,
.Bind = I2cManagerBind,
.Init = I2cManagerInit,
.Release = I2cManagerRelease,
.moduleName = "HDF_PLATFORM_I2C_MANAGER",
};
HDF_INIT(g_i2cManagerEntry);
驅動適配人員無需關注這個驅動的實現,有興趣的可以去閱讀源碼。
drivers/framework/support/platform/src/i2c_core.c
4、適配驅動Entry只需要實現Init方法
struct HdfDriverEntry g_i2cDriverEntry = {
.moduleVersion = 1,
.Init = Hi35xxI2cInit,
.Release = Hi35xxI2cRelease,
.moduleName = "hi35xx_i2c_driver",
};
HDF_INIT(g_i2cDriverEntry);
static int32_t Hi35xxI2cInit(struct HdfDeviceObject *device)
{
int32_t ret;
const struct DeviceResourceNode *childNode = NULL;
ret = HDF_SUCCESS;
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
ret = Hi35xxI2cParseAndInit(device, childNode);
if (ret != HDF_SUCCESS) {
break;
}
}
return ret;
}
在Init方法中會將i2c_config.hcs中定義的每一個節點取出,分別進行初始化
5、Hi35xxI2cParseAndInit里面很自由
static int32_t Hi35xxI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
{
int32_t ret;
struct Hi35xxI2cCntlr *hi35xx = NULL;
(void)device;
hi35xx = (struct Hi35xxI2cCntlr *)OsalMemCalloc(sizeof(*hi35xx));
ret = Hi35xxI2cReadDrs(hi35xx, node);
hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize);
Hi35xxI2cCntlrInit(hi35xx);
hi35xx->cntlr.priv = (void *)node;
hi35xx->cntlr.busId = hi35xx->bus;
hi35xx->cntlr.ops = &g_method;
hi35xx->cntlr.lockOps = &g_lockOps;
(void)OsalSpinInit(&hi35xx->spin);
ret = I2cCntlrAdd(&hi35xx->cntlr);
....
}
Hi35xxI2cParseAndInit會處理一個具體的I2C控制器的初始化工作,包括:
1)I2cCntlr對象的分配,Hi35xxI2cCntlr頭部內嵌了一個I2cCntlr,這是一種繼承。
struct Hi35xxI2cCntlr {
struct I2cCntlr cntlr;
OsalSpinlock spin;
volatile unsigned char *regBase;
uint16_t regSize;
int16_t bus;
uint32_t clk;
uint32_t freq;
uint32_t irq;
uint32_t regBasePhy;
};
2)在Hi35xxI2cReadDrs中完成節點屬性的讀取,并填充進hi35xx對象
3)映射寄存器基地址OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize);
4)調用Hi35xxI2cCntlrInit完成控制器的初始化
5)I2cCntlr對象的填充及調用I2cCntlrAdd注冊
這里唯一形式化約束是必須創建一個合法的I2cCntlr并調用I2cCntlrAdd注冊到核心層。其他Hi35xx開頭的函數都是驅動適配者自由封裝的,并無形式化要求。
總結
采用統一服務模式,其優點是不用為每一個I2C控制器定義一個設備節點,控制器對象的創建和注冊比較自由;而缺點是要創建一個I2C管理器服務,以及一個虛擬的資源描述設備。
說明
I2C模塊適配涉及到的代碼示例片段來自device/hisilicon/drivers/i2c/
GPIO模塊適配
GPIO模塊由于目前不向用戶態提供能力,所以不需要發布設備服務。其hcs配置同uart相似,這里不再贅述,唯一不同是device_info.hcs中設備節點的policy為0,表示不發布設備服務。
device_gpio :: device {
device0 :: deviceNode {
policy = 0;
priority = 10;
permission = 0644;
moduleName = "hisi_pl061_driver";
deviceMatchAttr = "hisilicon_hi35xx_pl061";
}
}
同樣的原因,GPIO的驅動Entry不需要實現Bind方法,僅需要在Init方法中創建并初始化一個GpioCntlr,再調用GpioCntlrAdd完成注冊即可。
struct HdfDriverEntry g_gpioDriverEntry = {
.moduleVersion = 1,
.Bind = Pl061GpioBind,
.Init = Pl061GpioInit,
.Release = Pl061GpioRelease,
.moduleName = "hisi_pl061_driver",
};
HDF_INIT(g_gpioDriverEntry);
這里雖然給Bind方法賦值,但其實現為空。
static int32_t Pl061GpioInit(struct HdfDeviceObject *device)
{
int32_t ret;
struct Pl061GpioCntlr *pl061 = &g_pl061;
ret = Pl061GpioReadDrs(pl061, device->property);
pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep);
ret = Pl061GpioInitCntlrMem(pl061);
pl061->cntlr.count = pl061->groupNum * pl061->bitNum;
pl061->cntlr.priv = (void *)device->property;
pl061->cntlr.ops = &g_method;
pl061->cntlr.device = device;
ret = GpioCntlrAdd(&pl061->cntlr);
}
Init方法里面注意是對GpioCntlr的初始化,以及通過GpioCntlrAdd完成注冊。這里Pl061GpioCntlr內嵌了一個GpioCntlr,又是繼承。
GpioCntlr的鉤子方法實現,請感興趣的讀者自己去閱讀源碼,這里僅對每個鉤子方法作用做簡單注釋:
static struct GpioMethod g_method = {
.request = NULL, //暫時不用
.release = NULL, //暫時不用
.write = Pl061GpioWrite, //寫管腳
.read = Pl061GpioRead, //讀管腳
.setDir = Pl061GpioSetDir, //設置管腳方向
.getDir = Pl061GpioGetDir, //獲取管腳方向
.toIrq = NULL, //暫時不用
.setIrq = Pl061GpioSetIrq, //設置管腳中斷段,如不具備此能力可忽略
.unsetIrq = Pl061GpioUnsetIrq, //取消管腳中斷設置,如不具備此能力可忽略
.enableIrq = Pl061GpioEnableIrq, //使能管腳中斷,如不具備此能力可忽略
.disableIrq = Pl061GpioDisableIrq, //禁止管腳中斷,如不具備此能力可忽略
};
總結
GPIO適配在hcs配置及驅動Entry編寫上比I2C/UART都簡單,但是無法向用戶態提供能力支撐。
最后,對于其他模塊的適配,基本都是這三種方式之一,讀者可集合核心層代碼及現有驅動案例對號入座,采取相應的適配方式。
說明:GPIO模塊適配涉及到的代碼示例片段來自device/hisilicon/drivers/gpio/
OpenHarmony系統平臺驅動適配Linux內核
不知道讀者有沒有思考過,如果在Linux內核下,我們如何適配一個平臺設備呢?因為HDF驅動框架是跨平臺的,所以我們仍然可以按照前面介紹的方式進行驅動適配。但是,Linux內核有大量現存驅動,它們一起支撐著大量SOC芯片,如果要將這些驅動按照HDF的方式重新適配,且不說難度,就工作量來說是巨大的。
那么有沒有辦法將這些現存驅動利用起來呢?答案就是適配器模式,它可以將一個類的接口轉換成用戶希望的另外一個接口,通過增加一點點工作量,使得現有對象在新環境中應用起來。在Linux內核,HDF驅動框架時新環境,而Linux原生的設備對象,例如i2c_adapter, spi_dev,gpio_chip等等,都是舊對象,我們需要通過適配器模式,將其適配成HDF平臺驅動框架定義的I2cCntlr、SpiCntlr、GpioCntlr等新對象,從而快速將其應用起來。
具體的適配方式是,針對Linux內核每一種平臺設備,提供一個適配驅動,將Linux原生設備對象封裝成HDF平臺驅動框架定義的設備對象,其他流程同正常硬件驅動適配一樣,由于是直接基于現有對象封裝,硬件初始化工作,甚至hcs配置都可以省略。
以I2C為例,在Linux下,我們將I2cCntlr直接關聯到i2c_adapter,而其鉤子方法的實現也是通過調用i2c_adapter相應的配套方法實現的,其對應關系如下:
HDF平臺驅動 | Linux內核 | 說明 |
I2cCntlr | i2c_adapter | 通過I2cCntlr的priv成員關聯 |
I2cTransfer | i2c_transfer | |
I2cMsg | i2c_msg | 需要進行參數轉換 |
有興趣的讀者,可以通過閱讀下面的源碼,來了解Linux內核下對各類平臺設備的適配。
drivers/adapter_del/khdf/linux/platform
總結與展望
OpenHarmony系統平臺驅動框架基于HDF驅動框架,為外設驅動提供標準的平臺設備訪問接口,同時為平臺設備驅動提供統一的適配接口,使得外設驅動僅需“關心”自身業務,而使設備驅動僅“關心”自身硬件。
為此,平臺驅動框架采用平臺接口層、平臺核心層、平臺適配層的三層結構,并抽象出平臺設備(PlatformDevice)、平臺設備管理器(PlatformManager)、等概念,形成平臺驅動框架特有的編程風格和設計思想。然而有些概念和思想,在框架實現中并不明顯,隨著架構的演進,有些概念將會越來越明確,他們包括:
統一的平臺設備對象模型:目前各類型的設備對象模型,由各模塊自己定義,例如I2cCntlr、UartHost,他們并沒有公共的父類型,而這一公共類型,是必要的。所以,將來會抽象出PlatformDevice這一公共設備對象模型。
設備管理器:每一類設備有自己的管理器,它即充當容器,也充當管理者,在需要用戶態支持的情況下,還充當設備服務。雖然目前沒有明確的數據結構與之對應,但是隨著架構演進,這個概念的體現一定會愈加明確。
引用計數:設備對象需要進行生命周期管理,設備的獲取和釋放需要計數,雖然當前沒有實現這個機制,但是我們要求每一類設備的獲取通過Get/Put方法實現,以便后續擴展引用計數機制
編輯:jq
-
C語言
+關注
關注
180文章
7614瀏覽量
137702 -
編程
+關注
關注
88文章
3637瀏覽量
93980 -
OOP
+關注
關注
0文章
14瀏覽量
8808 -
驅動框架
+關注
關注
0文章
14瀏覽量
4074 -
OpenHarmony
+關注
關注
25文章
3744瀏覽量
16577
原文標題:OpenHarmony HDF 平臺驅動框架介紹及驅動適配指導
文章出處:【微信號:gh_e4f28cfa3159,微信公眾號:OpenAtom OpenHarmony】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論