在Ubuntu上為Android增加硬體抽象層(HAL)模組訪問Linux核心驅動程式(老羅學習筆記3),ubuntuandroid
在Android硬體抽象層(HAL)概要介紹和學習計劃一文中,我們簡要介紹了在Android系統為為硬體編寫驅動程式的方法。簡單來說,硬體驅動程式一方面分布在Linux核心中,另一方面分布在使用者空間的硬體抽象層中。接著,在Ubuntu上為Android系統編寫Linux核心驅動程式一文中舉例子說明了如何在Linux核心編寫驅動程式。在這一篇文章中,我們將繼續介紹Android系統硬體驅動程式的另一方面實現,即如何在硬體抽象層中增加硬體模組來和核心驅動程式互動。在這篇文章中,我們還將學習到如何在Android系統建立裝置檔案時用類似Linux的udev規則修改裝置檔案模式的方法。
一. 參照在Ubuntu上為Android系統編寫Linux核心驅動程式一文所示,準備好樣本核心驅動序。完成這個核心驅動程式後,便可以在Android系統中得到三個檔案,分別是/dev/hello、/sys/class/hello/hello/val和/proc/hello。在本文中,我們將通過裝置檔案/dev/hello來串連硬體抽象層模組和Linux核心驅動程式模組。
二. 進入到在hardware/libhardware/include/hardware目錄,建立hello.h檔案:
USER-NAME@MACHINE-NAME:~/Android$ cd hardware/libhardware/include/hardware
USER-NAME@MACHINE-NAME:~/Android/hardware/libhardware/include/hardware$ vi hello.h
hello.h檔案的內容如下:
#ifndef ANDROID_HELLO_INTERFACE_H #define ANDROID_HELLO_INTERFACE_H #include <hardware/hardware.h> __BEGIN_DECLS /*--擴充C語言在編譯的時候按照C++編譯器進行統一處理,使得C++代碼能夠調用C編譯產生的中間代碼*/ /*定義模組ID*/ #define HELLO_HARDWARE_MODULE_ID "hello" /*硬體模組結構體*/ struct hello_module_t { struct hw_module_t common; }; /*硬體介面結構體*/ struct hello_device_t { struct hw_device_t common; int fd; int (*set_val)(struct hello_device_t* dev, int val); /*--set_val什麼類型?什麼作用?以及後面這樣複用結構體 何用??--*/ int (*get_val)(struct hello_device_t* dev, int* val); }; __END_DECLS #endif
/*--
hw_module_t:
/** * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM * and the fields of this data structure must begin with hw_module_t * followed by module specific information. */ //每一個硬體模組都每必須有一個名為HAL_MODULE_INFO_SYM的資料結構變數,它的第一個成員的類型必須為hw_module_t typedef struct hw_module_t { /** tag must be initialized to HARDWARE_MODULE_TAG */ uint32_t tag; // 初始化硬體模組標籤 /** major version number for the module */ uint16_t version_major; //主模組版本號碼 /** minor version number of the module */ uint16_t version_minor; //副模組版本號碼 /** Identifier of module */ const char *id; //模組標識符 /** Name of this module */ const char *name; //模組名字 /** Author/owner/implementor of the module */ const char *author; //編寫作者 /** Modules methods */ //模組方法列表,指向hw_module_methods_t* struct hw_module_methods_t* methods; //硬體模組的方法 /** module's dso */ void* dso; //DSO: Device Software Optimization,裝置軟體最佳化“安全維和組織”(DSO) /** padding to 128 bytes, reserved for future use */ uint32_t reserved[32-7]; //填充到128位元組,保留以供將來使用 } hw_module_t;
--*/
這裡按照Android硬體抽象層規範的要求,分別定義①模組ID、②模組結構體以及③硬體介面結構體。在硬體介面結構體中,fd表示裝置檔案描述符,對應我們將要處理的裝置檔案"/dev/hello",set_val和get_val為該HAL對上提供的函數介面。
三. 進入到hardware/libhardware/modules目錄,建立hello目錄,並添加hello.c檔案。 hello.c的內容較多,我們分段來看。
首先是包含相關標頭檔和定義相關結構:
#define LOG_TAG "HelloStub"
/*--HelloStub:什麼作用?
答:
HAL stub的架構比較簡單,三個結構體、兩個常量、一個函數,簡稱321架構,它的定義在:
@hardware/libhardware/include/hardware/hardware.h
@hardware/libhardware/hardware.c
--*/ #include <hardware/hardware.h> #include <hardware/hello.h> #include <fcntl.h> //檔案控制#include <errno.h> //出錯碼#include <cutils/log.h> //?#include <cutils/atomic.h> //? #define DEVICE_NAME "/dev/hello" //需要是路徑 ! #define MODULE_NAME "Hello" #define MODULE_AUTHOR "shyluo@gmail.com" //模組作者 /*裝置開啟和關閉介面*/ static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device); static int hello_device_close(struct hw_device_t* device); /*裝置提供者*/ static int hello_set_val(struct hello_device_t* dev, int val); static int hello_get_val(struct hello_device_t* dev, int* val); /*模組方法表*/
//硬體模組方法列表的定義,這裡只定義了一個open函數static struct hw_module_methods_t hello_module_methods = { open: hello_device_open }; /*模組執行個體變數*/ struct hello_module_t HAL_MODULE_INFO_SYM = { //HAL_MODULE_INFO_SYM: 上層調用時的入口(相當於main)!! common: { tag: HARDWARE_MODULE_TAG, version_major: 1, version_minor: 0, id: HELLO_HARDWARE_MODULE_ID, name: MODULE_NAME, author: MODULE_AUTHOR, methods: &hello_module_methods, //解釋看上 } };
這裡,執行個體變數名必須為HAL_MODULE_INFO_SYM,tag也必須為HARDWARE_MODULE_TAG,這是Android硬體抽象層規範規定的。
-----
定義hello_device_open函數:
static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) { struct hello_device_t* dev;
dev = (struct hello_device_t*)malloc(sizeof(struct hello_device_t));
/*--將一個分配域的起始地址轉換成struct hello_device_t* 類型 如果此函數未能成功地執行(例如記憶體不足),則返回null 指標(NULL,'0')--*/ if(!dev) { LOGE("Hello Stub: failed to alloc space"); return -EFAULT; } memset(dev, 0, sizeof(struct hello_device_t)); //清空該段記憶體,然後該記憶體段重新賦值 dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = 0; dev->common.module = (hw_module_t*)module; dev->common.close = hello_device_close; dev->set_val = hello_set_val;dev->get_val = hello_get_val; if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) { //返回 -1 時,模組開啟失敗 LOGE("Hello Stub: failed to open /dev/hello -- %s.", strerror(errno));free(dev); return -EFAULT; } *device = &(dev->common); LOGI("Hello Stub: open /dev/hello successfully."); //LOGI是列印資訊嗎? 在哪定義的? return 0; }
/*--
| shmctl(共用記憶體管理) |
| 所需標頭檔 |
#include <sys/types.h>#include <sys/shm.h> |
| 函數說明 |
完成對共用記憶體的控制 |
| 函數原型 |
int shmctl(int shmid, int cmd, struct shmid_ds *buf) |
| 函數傳入值 |
shmid |
共用記憶體標識符 |
| cmd |
IPC_STAT:得到共用記憶體的狀態,把共用記憶體的shmid_ds結構複製到buf中 |
| IPC_SET:改變共用記憶體的狀態,把buf所指的shmid_ds結構中的uid、gid、mode複製到共用記憶體的shmid_ds結構內 |
| IPC_RMID:刪除這片共用記憶體 |
| buf |
共用記憶體管理結構體。具體說明參見共用記憶體核心結構定義部分 |
| 函數傳回值 |
成功:0 |
| 出錯:-1,錯誤原因存於error中 |
| 錯誤碼 |
EACCESS:參數cmd為IPC_STAT,確無許可權讀取該共用記憶體EFAULT:參數buf指向無效的記憶體位址EIDRM:標識符為shmid的共用記憶體已被刪除EINVAL:無效的參數cmd或shmidEPERM:參數cmd為IPC_SET或IPC_RMID,卻無足夠的許可權執行 |
--*/
DEVICE_NAME定義為"/dev/hello"。由於裝置檔案是在核心驅動裡面通過device_create建立的,而device_create建立的裝置檔案預設只有root使用者可讀寫,而hello_device_open一般是由上層APP來調用的,這些APP一般不具有root許可權,這時候就導致開啟裝置檔案失敗:
Hello Stub: failed to open /dev/hello -- Permission denied. 解決辦法是類似於Linux的udev規則,開啟Android原始碼工程目錄下,進入到system/core/rootdir目錄,裡面有一個名為ueventd.rc檔案,往裡面添加一行:
/dev/hello 0666 root root
定義hello_device_close、hello_set_val和hello_get_val這三個函數:
static int hello_device_close(struct hw_device_t* device) { struct hello_device_t* hello_device = (struct hello_device_t*)device; if(hello_device) { close(hello_device->fd); free(hello_device); } return 0; } static int hello_set_val(struct hello_device_t* dev, int val) { LOGI("Hello Stub: set value %d to device.", val); write(dev->fd, &val, sizeof(val)); return 0; } static int hello_get_val(struct hello_device_t* dev, int* val) { if(!val) { LOGE("Hello Stub: error val pointer"); return -EFAULT; } read(dev->fd, val, sizeof(*val)); LOGI("Hello Stub: get value %d from device", *val); return 0; }
四. 繼續在hello目錄下建立Android.mk檔案:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := hello.c
LOCAL_MODULE := hello.default
include $(BUILD_SHARED_LIBRARY) 注意,LOCAL_MODULE的定義規則,hello後面跟有default,hello.default能夠保證我們的模組總能被硬象抽象層載入到。 五. 編譯:
USER-NAME@MACHINE-NAME:~/Android$ mmm hardware/libhardware/modules/hello
編譯成功後,就可以在out/target/product/generic/system/lib/hw目錄下看到hello.default.so檔案了。 六. 重新打包Android系統鏡像system.img:
USER-NAME@MACHINE-NAME:~/Android$ make snod
重新打包後,system.img就包含我們定義的硬體抽象層模組hello.default了。 雖然我們在Android系統為我們自己的硬體增加了一個硬體抽象層模組,但是現在Java應用程式還不能訪問到我們的硬體。我們還必須編寫JNI方法和在Android的Application Frameworks層增加API介面,才能讓上層Application訪問我們的硬體。在接下來的文章中,我們還將完成這一系統過程,使得我們能夠在Java應用程式中訪問我們自己定製的硬體。 我用的是友善之臂提供的tiny4412開發板編譯過程中:
1
make:進入目錄'/opt/FriendlyARM/tiny4412/android/android-4.1.2'
make: *** 沒有規則可以建立“out/target/product/generic/obj/SHARED_LIBRARIES/audio.usb.default_intermediates/import_includes”需要的目標“out/target/product/generic/obj/SHARED_LIBRARIES/libc_intermediates/export_includes”。 停止。
make:離開目錄“/opt/FriendlyARM/tiny4412/android/android-4.1.2”
切記:. setenv
2 局部程式修改
http://blog.csdn.net/oldmtn/article/details/9213869