Android系統(tǒng)對(duì)硬件設(shè)備的支持是分兩層的焦除。一層實(shí)現(xiàn)在內(nèi)核空間中(只有內(nèi)核空間才有特權(quán)操作硬件設(shè)備)尽楔,另一層實(shí)現(xiàn)在用戶空間中繁涂。用戶空間中的這一層就是HAL層盹靴,也就是Hardware Abstract Layer層炸茧。而傳統(tǒng)的Linux系統(tǒng)中,對(duì)硬件的支持是完全實(shí)現(xiàn)在內(nèi)核空間中的稿静,即把對(duì)硬件的支持完全實(shí)現(xiàn)在硬件驅(qū)動(dòng)模塊中梭冠。
之所以這么設(shè)計(jì),是為了保護(hù)硬件廠商的利益改备。因?yàn)長(zhǎng)inux內(nèi)核源碼是遵循GPL協(xié)議的控漠,如果硬件廠商把對(duì)硬件的支持完全實(shí)現(xiàn)在硬件驅(qū)動(dòng)模塊中,那么就必須將硬件驅(qū)動(dòng)模塊的源代碼全部公開(kāi)悬钳,這相當(dāng)于暴露了硬件的實(shí)現(xiàn)細(xì)節(jié)和參數(shù)盐捷。
因此Android的做法是在內(nèi)核空間以硬件驅(qū)動(dòng)模塊的形式來(lái)僅僅提供簡(jiǎn)單的硬件訪問(wèn)通道,而用戶空間以硬件抽象模塊的形式封裝硬件的實(shí)現(xiàn)細(xì)節(jié)和參數(shù)默勾。我的理解是碉渡,驅(qū)動(dòng)層僅僅提供硬件寄存器的讀寫操作,如getValue()和setValue()母剥。而究竟按什么順序讀寫滞诺,讀寫什么內(nèi)容形导,則是放在硬件抽象層的。
除了上述的作用外习霹,硬件抽象層的模塊接口還定義了統(tǒng)一的規(guī)范朵耕。使得上層系統(tǒng)能夠以一致的方式獲取到硬件抽象模塊的句柄hw_module_t以及虛擬硬件設(shè)備句柄hw_device_t。
舉個(gè)例子
我們以一個(gè)具有一個(gè)4字節(jié)大小寄存器的虛擬字符硬件設(shè)備為例淋叶,觀察下虛擬字符設(shè)備定義阎曹,內(nèi)核驅(qū)動(dòng)編寫,硬件抽象層模塊編寫到實(shí)際的加載與使用的整個(gè)過(guò)程爸吮。我們將該設(shè)備命名為fake_reg芬膝,對(duì)應(yīng)的驅(qū)動(dòng)程序名為freg望门。
1形娇、開(kāi)發(fā)內(nèi)核空間驅(qū)動(dòng)層
freg.h
...
struct fake_reg_dev {
? ? int val;? //虛擬寄存器
? ? struct semaphore sem;? //信號(hào)量,用來(lái)同步訪問(wèn)虛擬寄存器val
? ? struct cdev dev; //標(biāo)準(zhǔn)的Linux字符設(shè)備結(jié)構(gòu)體變量
}
...
freg.c
...
//設(shè)備文件操作方法
static int freg_open(struct inode* inode, struct file* filp);
static int reg_release(struct inode* inode, struct file* filp);
static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loft_t * f_pos);
static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loft_t * f_pos);
//設(shè)備文件操作方法表
static struct file_operations freg_fops = {
? ? .owner = THIS_MODULE,
? ? .open = freg_open,
? ? .release = freg_release,
? ? .read = freg_read,
? ? ?.write = freg_write,
};
//freg_open的主要作用就是自定義設(shè)備結(jié)構(gòu)體保存在文件指針的私有數(shù)據(jù)域中筹误,方便之后訪問(wèn)設(shè)備時(shí)可
//以直接拿來(lái)用桐早。而文件句柄fd和filp是綁定的。因此獲取打開(kāi)的文件句柄厨剪,就可以訪問(wèn)到fake_reg_dev
static int freg_open(struct inode* inode, struct file* filp) {
? ? struct fake_reg_dev* dev;
? ? dev = container_of(inode->i_cdev, struct fake_reg_dev, dev);
? ? filp->private_data = dev;
? ?return 0;
}
static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loft_t* f_pos) {
...
? ? struct fake_reg_dev* dev = filp->private-data;
? ? copy_to_user(buf, &(dev->val), sizeof(dev->val));
...
}
static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loft_t* f_pos) {
...
? ? struct fake_reg_dev* dev = filp->private-data;
? ? copy_from_user(&(dev->val), buf, count)
...
}
...
在初始化硬件設(shè)備的時(shí)候哄酝,將freg_fops賦值給fake_reg_dev的cdev dev中的ops變量。那么之后在用戶空間調(diào)用
fd = open("/dev/freg", O_RDWR);
read(fd, &val, sizeof(val));
write(fd, &val, sizeof(val));
時(shí)祷膳,則會(huì)觸發(fā)內(nèi)核空間中的freg_open, freg_read以及freg_write方法被調(diào)用陶衅。從而完成硬件設(shè)備的訪問(wèn)。
硬件設(shè)備初始化
static int __freg_setup_dev(struct fake_reg_dev* dev) {
...
? ? memeset(dev, 0, sizeof(struct fake_reg_dev));
? ? cdev_init(&(dev->dev), &freg_fops);
? ? dev->dev.owner = THIS_MODULE;
? //將設(shè)備文件操作方法表賦給標(biāo)準(zhǔn)字符設(shè)備結(jié)構(gòu)體的ops變量
? ? dev->dev.ops = &freg_fops;? ?
? ? init_MUTEXT($(dev->sem));
? ? dev->val = 0;
...
}
忽略掉各種細(xì)節(jié)以后直晨,以上就是一個(gè)虛擬硬件設(shè)備的完整定義和驅(qū)動(dòng)搀军。將代碼編譯打包進(jìn)Android內(nèi)核鏡像文件后就可以使用了。
2勇皇、開(kāi)發(fā)硬件抽象層模塊
在第一節(jié)中我們定義了一個(gè)虛擬字符設(shè)備fake_reg罩句,以及相應(yīng)的驅(qū)動(dòng)程序freg。我們已經(jīng)可以通過(guò)read和write指令去讀寫其虛擬寄存器val的值敛摘。但是用這個(gè)寄存器存取什么值门烂,這個(gè)值用來(lái)做什么,我們還沒(méi)有定義兄淫。這就是我們?cè)谟布橄髮幽K中需要做的屯远。硬件抽象層模塊最終被會(huì)編譯成.so文件,通過(guò)加載該文件捕虽,我們就能夠獲取到硬件抽象層模塊的句柄氓润,也就是后文提到的hw_module_t的地址。
硬件抽象層模塊名稱與其對(duì)應(yīng)的.so文件位置有對(duì)應(yīng)的關(guān)系薯鳍,因此我們僅需要知道名稱就能打開(kāi)一個(gè)設(shè)備咖气。
2.1 硬件抽象層模塊編寫規(guī)范
硬件抽象層規(guī)范有兩個(gè)重要的結(jié)構(gòu)體:hw_module_t以及hw_device_t挨措,分別對(duì)應(yīng)硬件抽象層模塊和其包含的硬件設(shè)備。一個(gè)hw_module_t可以包含多個(gè)hw_device_t崩溪。
#define MAKE_TAG_CONSTANT(A,B,C,D) ((A) << 24) | ((B) << 16) | ((C) << 8) | (D))
#define? ? HARDWARE_MODULE_TAG? ? MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')
//每個(gè)硬件抽象層模塊的定義都必須包含一個(gè)名為HAL_MODULE_INFO_SYM的數(shù)據(jù)結(jié)構(gòu)浅役,并且該數(shù)據(jù)結(jié)構(gòu)第一個(gè)成員變量的類型必須是hw_module_t。在打開(kāi)模塊對(duì)應(yīng)的.so文件后就可以調(diào)用dlsym函數(shù)通過(guò)HAL_MODULE_INFO_SYM的字面值找到該結(jié)構(gòu)體的地址伶唯。由于該結(jié)構(gòu)體的第一個(gè)元素必須是hw_module_t觉既,于是我們其實(shí)也就拿到了hw_module_t的地址。
#define HAL_MODULE_INFO_SYM HMI
typedef struct hw_modult_t {
...
? ? uint32 tag;? //必須被初始化為HARDWARE_MODULE_TAG
? ? const char *id;? //硬件抽象層模塊的id
? ? const char *name; //硬件抽象層模塊的名字
? ?struct hw_module_methods_t *methods;? //模塊提供的方法
? ?void* dso;? //調(diào)用dlopen函數(shù)打開(kāi)此模塊對(duì)應(yīng)的.so文件時(shí)獲得的句柄乳幸,關(guān)閉的時(shí)候使用
...
} hw_module_t;
typedf struct hw_module_methods_t {
//打開(kāi)module模塊中對(duì)應(yīng)id值的硬件設(shè)備瞪讼,device為輸出參數(shù),用來(lái)描述一個(gè)已經(jīng)打開(kāi)的硬件設(shè)備
? ? int (*open) (const struct hw_module_t* module, const char* id, struct hw_device_t** device);
} hw_module_methods_t;
#define? ?HARDWARE_DEVICE_TAG? ?MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')
//每個(gè)硬件設(shè)備都必須定義一個(gè)硬件設(shè)備結(jié)構(gòu)體粹断,且第一個(gè)成員變量的類型必須為hw_device_t
typedef struct hw_device_t {
...
uint32_t tag;? //必須被初始化為HARDWARE_DEVICE_TAG
struct hw_module_t* module;? //硬件設(shè)備所屬于的硬件抽象層模塊
int (*close) (struct hw_device_t* device);? //硬件設(shè)備的打開(kāi)由hw_module_methods_t的open完成符欠,而關(guān)閉是由硬件設(shè)備自身完成的。
...
} hw_device_t;
以上就是硬件抽象層模塊以及硬件設(shè)備的編寫規(guī)范瓶埋∠J粒總結(jié)一下就是必須定義兩個(gè)自定義結(jié)構(gòu)體以及一個(gè)hw_module_methods_t結(jié)構(gòu)體。
一個(gè)自定義結(jié)構(gòu)體名字固定為HAL_MODULE_INFO_SYM养筒,且其第一個(gè)元素的類型必須是hw_module_t類型曾撤。
一個(gè)自定義結(jié)構(gòu)體名字不指定,但是其第一個(gè)元素的類型必須是hw_device_t類型晕粪。
接下來(lái)我們按照規(guī)范編寫之前定義的fake_reg設(shè)備的硬件抽象層模塊接口挤悉。
2.2 編寫硬件抽象層模塊接口
freg_module.h
...
#define FREG_HARDWARE_MODULE_ID "freg"? //模塊id
#define FREG_HARDWARE_DEVICE_ID "freg" //設(shè)備id
//聲明自定義模塊結(jié)構(gòu)體,后面會(huì)看到freg_module_t的實(shí)例名字為HAL_MODULE_INFO_SYM?
struct freg_module_t {
? ? struct hw_module_t common;? //第一個(gè)元素必須為hw_module_t類型
}
//自定義設(shè)備結(jié)構(gòu)體
struct freg_device_t {
? ? struct hw_device_t common;
? ? int fd;? ?//虛擬硬件設(shè)備的文件描述符
? ?int (*set_val) (struct freg_device_t* dev, int val);? ?//初始化時(shí)會(huì)被賦值為下文中定義的freg_set_val
? ?int (*get_val) (struct freg_device_t* dev, int* val); //初始化時(shí)會(huì)被賦值為下文中定義的freg_get_val
}
...
freg_module.cpp
...
#define DEVICE_NAME "/dev/freg/"
#define MODULE_NAME "Freg"
static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
static int freg_device_close(struct hw_device_t* device);
static int freg_get_val(struct freg_device_t* dev, int* val);
static int freg_set_val(struct freg_device_t* dev, int val);
static struct hw_module_methods_t freg_module_methods = {
? ? open:freg_device_open
}
//自定義模塊結(jié)構(gòu)體巫湘,且名字必須為HAL_MODULE_INFO_SYM
struct freg_module_t HAL_MODULE_INFO_SYM = {
? ? common: {
? ? ? ? ?tag: HARDWARE_MODULE_TAG,? ?//固定值
? ? ? ? ?version_major: 1,? //主版本號(hào)
? ? ? ? ?version_minor: 0, //次版本號(hào)
? ? ? ? ?id: FREG_HARDWARE_MODULE_ID,? //值為"freg"
? ? ? ? ?name: MODULE_NAME; //值為"Freg"
? ? ? ? ?methods: &freg_module_methods;
? ? }
};
...
以上定義了一個(gè)名為HAL_MODULE_INFO装悲,類型為自定義freg_module_t類型的結(jié)構(gòu)體,其第一個(gè)元素common為hw_module_t類型剩膘,hw_module_methods_t成員變量定義了freg_device_open方法衅斩。
定義了一個(gè)freg_device_t結(jié)構(gòu)體,其第一個(gè)元素common為hw_device_t類型怠褐。fd是該設(shè)備對(duì)應(yīng)的文件句柄畏梆。set_val以及get_val函數(shù)指針對(duì)應(yīng)著freg_get_val以及freg_set_val方法。
這樣奈懒,當(dāng)我們通過(guò)dlopen函數(shù)和dlsym函數(shù)獲取到hw_module_t指針后奠涌,就可以調(diào)用其open函數(shù)打開(kāi)對(duì)應(yīng)的freg_device_t設(shè)備,從而調(diào)用set_val和get_val函數(shù)磷杏。完成對(duì)硬件設(shè)備的操作溜畅。
接下來(lái),我們看下freg_device_open, freg_set_val,? freg_get_val的具體實(shí)現(xiàn)极祸。
static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device) {
...
? ? struct freg_device_t* dev;?
? ? dev->common.tag = HARDWARE_DEVICE_TAG;? //給hw_device_t類型的common的成員變量賦值
? ? dev->common.version = 0;
? ? dev->common.module = (hw_module_t*) module;
? ? dev->common.close = freg_device_close;
? ?dev->set_val = freg_set_val;
? ?dev->get_val = freg_get_val;
? ?dev->fd = open(DEVICE_NAME, O_RDWR);? ?//這里會(huì)調(diào)用第1節(jié)中定義在內(nèi)核空間的驅(qū)動(dòng)層的freg_fops的freg_open函數(shù)
? ?*device = &(dev->common);? //將賦好值的freg_device_t的common成員賦給輸出參數(shù)device.
? ?return 0;
...
}
static int freg_get_val(struct freg_device_t* dev, int* val) {
...
? ? read(dev->fd, val, sizeof(*val));? ?//會(huì)觸發(fā)第1節(jié)中定義在內(nèi)核空間的驅(qū)動(dòng)層的freg_fops的freg_read函數(shù)
...
}
static int freg_set_val(struct freg_device_t* dev, int val) {\
...
? ? write(dev->fd, &val, sizeof(val));?//會(huì)觸發(fā)第1節(jié)中定義在內(nèi)核空間的驅(qū)動(dòng)層的freg_fops的freg_write函數(shù)
...
}
由上可見(jiàn)慈格,獲取到hw_module_t句柄后怠晴,便可以通過(guò)其成員hw_module_methods_t中定義的freg_device_open打開(kāi)對(duì)應(yīng)的硬件設(shè)備,獲得freg_device_t句柄浴捆。從而通過(guò)其set_val和get_val來(lái)觸發(fā)之前在內(nèi)核空間中編寫的驅(qū)動(dòng)程序來(lái)訪問(wèn)硬件設(shè)備蒜田。
而其實(shí)質(zhì)還是通過(guò)硬件設(shè)備文件描述符dev->fd進(jìn)行read和write操作。freg_device_t的主要作用就是持有這個(gè)句柄选泻。
那么接下來(lái)冲粤,關(guān)鍵就是如何加載硬件驅(qū)動(dòng)層模塊并獲取到這個(gè)hw_module_t句柄。
2.3 硬件抽象層模塊的加載
由于硬件抽象層模塊對(duì)應(yīng)的.so文件的路徑是固定的页眯。因此只需要提供模塊ID梯捕,就能夠找到對(duì)應(yīng)的.so文件。其函數(shù)原型為
int hw_get_module(const char* id, const struct hw_module_t **module) {
...
? ? load(id, path, module);? //path是根據(jù)id按照一定的規(guī)則生成的路徑窝撵。一個(gè)典型值為/system/lib/hw/freg.goldfish.so
....
}
static int load(const char* id, const char* path, const struct hw_module_t **pHmi)? {
...
? ? void *handle;
? ? struct hw_module_t *hmi;
? ?handle = dlopen(path, RTLD_NOW);
? ?const char *sym = HAL_MODULE_INFO_SYM_AS_STR;? ?//在2.1節(jié)硬件抽象層模塊規(guī)范中定義了宏HAL_MODULE_INFO_SYM? HMI;
? ?hmi = (struct hw_module_t *)dlsym(handle,? sym); //HAL_MODULE_INFO結(jié)構(gòu)體的第一個(gè)成員必須是hw_module_t類型傀顾,因此可以直接轉(zhuǎn)型
? ?hmi->dso = handle;
? ?*pHmi = hmi;
...
}
于是通過(guò)hw_get_module函數(shù),我們就成功的獲取到了指定硬件抽象層模塊的hw_module_t的句柄忿族,從而可以調(diào)用freg_device_open方法打開(kāi)指定的硬件設(shè)備獲得freg_device_t句柄锣笨,從而可以以freg_device_t為參數(shù)調(diào)用set_val以及get_val函數(shù)對(duì)硬件設(shè)備進(jìn)行訪問(wèn)蝌矛。道批、
總結(jié)
1、Android對(duì)于硬件設(shè)備的支持分為兩部分入撒。
一是內(nèi)核空間層的驅(qū)動(dòng)部分隆豹,封裝了對(duì)硬件設(shè)備的簡(jiǎn)單存取邏輯。其中驅(qū)動(dòng)的編寫的步驟為
(1) 為fake_reg_dev結(jié)構(gòu)體的cdev標(biāo)準(zhǔn)字符設(shè)備結(jié)構(gòu)體的ops變量賦值茅逮。其類型為file_operations類型璃赡。
(2) 實(shí)現(xiàn)file_operations中的open, release, read, write指向的函數(shù)體,其中read, write的實(shí)現(xiàn)主要依靠copy_from_user以及copy_to_user兩個(gè)函數(shù)献雅,來(lái)實(shí)現(xiàn)用戶空間和內(nèi)核空間之間的數(shù)值傳遞碉考。
(3)調(diào)用文件操作函數(shù)open, read以及write時(shí),則會(huì)觸發(fā)我們?cè)?2)中指向的函數(shù)挺身。
二是硬件抽象層模塊部分侯谁,封裝了對(duì)于硬件設(shè)備的業(yè)務(wù)操作邏輯部分。其關(guān)鍵步驟為
(1)按照規(guī)范自定義硬件抽象層模塊結(jié)構(gòu)體章钾,其名字必須為HAL_MODULE_INFO_SYM墙贱,其第一個(gè)成員變量必須為hw_module_t類型。
(2)實(shí)現(xiàn)hw_module_t中hw_module_methods_t成員變量的open函數(shù)贱傀。打開(kāi)指定id的硬件設(shè)備惨撇。
(3)按照規(guī)范自定義硬件設(shè)備結(jié)構(gòu)體,其第一個(gè)成員變量必須是hw_device_t類型府寒。緊跟著硬件設(shè)備操作的函數(shù)
(4)調(diào)用hw_get_module函數(shù)獲取到指定id的硬件抽象層模塊的hw_module_t句柄魁衙,調(diào)用open打開(kāi)指定id的硬件設(shè)備报腔,獲得freg_device_t句柄。調(diào)用freg_device_t中定義的業(yè)務(wù)邏輯方法剖淀。
至此榄笙,整個(gè)硬件加載和訪問(wèn)流程就結(jié)束了。
這里有個(gè)需要注意的點(diǎn)是:freg_device_t和hw_device_t的地址祷蝌,以及freg_module_t和hw_module_t的地址是一樣的茅撞。所以freg_device_t和hw_device_t是可以互相強(qiáng)制轉(zhuǎn)型的,freg_module_t和hw_module_t也是如此巨朦。
本文的代碼示例都來(lái)源于老羅的《Android系統(tǒng)源代碼情景分析》第二章硬件抽象層米丘,只截取了關(guān)鍵代碼,初始化以及錯(cuò)誤處理部分進(jìn)行了省略糊啡。如需進(jìn)一步了解詳情可以查看該書(shū)相關(guān)章節(jié)拄查。