美文网首页
4-安卓底层-HAL层-实现安卓控制LED

4-安卓底层-HAL层-实现安卓控制LED

作者: ibo | 来源:发表于2017-02-20 18:34 被阅读0次

    Android硬件抽象层(HAL)

    Android的硬件抽象层,简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux内核驱动程序运行在内核空间。为什么要这样安排呢?把硬件抽象层和内核驱动整合在一起放在内核空间不可行吗?从技术实现的角度来看,是可以的,然而从商业的角度来看,把对硬件的支持逻辑都放在内核空间,可能会损害厂家的利益。我们知道,Linux内核源代码版权遵循GNU License,而Android源代码版权遵循Apache License,前者在发布产品时,必须公布源代码,而后者无须发布源代码。如果把对硬件支持的所有代码都放在Linux驱动层,那就意味着发布时要公开驱动程序的源代码,而公开源代码就意味着把硬件的相关参数和实现都公开了,在手机市场竞争激烈的今天,这对厂家来说,损害是非常大的。因此,Android才会想到把对硬件的支持分成硬件抽象层和内核驱动层,内核驱动层只提供简单的访问硬件逻辑,例如读写硬件寄存器的通道,至于从硬件中读到了什么值或者写了什么值到硬件中的逻辑,都放在硬件抽象层中去了,这样就可以把商业秘密隐藏起来了。也正是由于这个分层的原因,Android被踢出了Linux内核主线代码树中。大家想想,Android放在内核空间的驱动程序对硬件的支持是不完整的,把Linux内核移植到别的机器上去时,由于缺乏硬件抽象层的支持,硬件就完全不能用了,这也是为什么说Android是开放系统而不是开源系统的原因。
    下面这个图阐述了硬件抽象层在Android系统中的位置,以及它和其它层的关系:
    0_1308977488PkP8.gif

    重要的三个结构体:

    hw_module_t
    86 typedef struct hw_module_t {    //声明模块                                                                            
    87     /** tag must be initialized to HARDWARE_MODULE_TAG */
    88     uint32_t tag;
    
    132     /** Identifier of module */
    133     const char *id;
    134 
    135     /** Name of this module */
    136     const char *name;
    137                                                                                                             
    138     /** Author/owner/implementor of the module */
    139     const char *author;
    140 
    141     /** Modules methods */
    142     struct hw_module_methods_t* methods;
    143      uint16_t module_api_version
    144     
    145     void* dso;
    146 
    147 #ifdef __LP64__
    148     uint64_t reserved[32-7];
    149 #else
    150     /** padding to 128 bytes, reserved for future use */
    151     uint32_t reserved[32-7];
    152 #endif
    153 
    154 } hw_module_t;
    
    hw_module_methods_t
    156 typedef struct hw_module_methods_t {
    157     /** Open a specific device */
    158     int (*open)(const struct hw_module_t* module, const char* id,
    159             struct hw_device_t** device);
    160 
    161 } hw_module_methods_t;
    
    hw_device_t
    typedef struct hw_device_t {
    168     /** tag must be initialized to HARDWARE_DEVICE_TAG */
    169     uint32_t tag;
                                                                                                        
    189     /** reference to the module this device belongs to */
    190     struct hw_module_t* module;
                
    187     uint32_t version;
    188 
    189     /** reference to the module this device belongs to */
    190     struct hw_module_t* module;
    191 
    192     /** padding reserved for future use */
    193 #ifdef __LP64__
    194     uint64_t reserved[12];
    195 #else
    196     uint32_t reserved[12];
    197 #endif
    198 
    199     /** Close this device */
    200     int (*close)(struct hw_device_t* device);
    201 
    202 } hw_device_t;
    

    重要的两个函数:

    /**
     * Get the module info associated with a module by id.
     *
     * @return: 0 == success, <0 == error and *module == NULL
     */
    int hw_get_module(const char *id, const struct hw_module_t **module)
    
    
    /**
     * Get the module info associated with a module instance by class 'class_id'
     * and instance 'inst'.
     *
     * Some modules types necessitate multiple instances. For example audio supports
     * multiple concurrent interfaces and thus 'audio' is the module class
     * and 'primary' or 'a2dp' are module interfaces. This implies that the files
     * providing these modules would be named audio.primary.<variant>.so and
     * audio.a2dp.<variant>.so
     *
     * @return: 0 == success, <0 == error and *module == NULL
     */
    int hw_get_module_by_class(const char *class_id, const char *inst, const struct hw_module_t **module);
    
    
    dlopen 函数 :
    • 包含头文件:#include <dlfcn.h>
    • 函数定义: void * dlopen( const char * pathname, int mode);
    • 函数描述:
      • mode是打开方式,其值有多个,不同操作系统上实现的功能有所不同,在linux下,按功能可分为三类:
        1、解析方式
        RTLD_LAZY:在dlopen返回前,对于动态库中的未定义的符号不执行解析(只对函数引用有效,对于变量引用总是立即解析)。
        RTLD_NOW: 需要在dlopen返回前,解析出所有未定义符号,如果解析不出来,在dlopen会返回NULL,错误为:: undefined symbol: xxxx.......
        2、作用范围,可与解析方式通过“|”组合使用。
        RTLD_GLOBAL:动态库中定义的符号可被其后打开的其它库解析。
        RTLD_LOCAL: 与RTLD_GLOBAL作用相反,动态库中定义的符号不能被其后打开的其它库重定位。如果没有指明是RTLD_GLOBAL还是RTLD_LOCAL,则缺省为RTLD_LOCAL。
        3、作用方式
        RTLD_NODELETE: 在dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量。这个flag不是POSIX-2001标准。
        RTLD_NOLOAD: 不加载库。可用于测试库是否已加载(dlopen()返回NULL说明未加载,否则说明已加载),也可用于改变已加载库的flag,如:先前加载库的flag为RTLD_LOCAL,用dlopen(RTLD_NOLOAD|RTLD_GLOBAL)后flag将变成RTLD_GLOBAL。这个flag不是POSIX-2001标准。
        RTLD_DEEPBIND:在搜索全局符号前先搜索库内的符号,避免同名符号的冲突。这个flag不是POSIX-2001标准。
        返回值:
        打开错误返回NULL
        成功,返回库引用
        编译时候要加入 -ldl (指定dl库)
        例如
        gcc test.c -o test -ldl

    实现安卓控制LED

    1. 在 hardware/libhardware/include/hardware 下新建ibo_hal.h文件
    ibo_hal.h
    #ifndef ANDROID_LED_INTERFACE_H
    #define ANDROID_LED_INTERFACE_H
    
    #include <stdint.h>
    #include <sys/cdefs.h>
    #include <sys/types.h>
    
    #include <hardware/hardware.h>
    
    __BEGIN_DECLS
    
    struct iboled_device_t {
        struct hw_device_t common;
        int (*ibo_open)(struct iboled_device_t* dev);
        int (*ibo_ctrl)(struct iboled_device_t* dev, int cmd);
    };
    
    __END_DECLS
    
    #endif 
    
    2. 在 hardware/libhardware/modules目录下 新建 iboled 文件夹
    3. 在 iboled 文件夹下新建 ibo_hal.c 和 Android.mk 两个文件
    ibo_hal.c
    #define LOG_TAG "IboHal"
    
    #include <hardware/vibrator.h>
    #include <hardware/hardware.h>
    #include <cutils/log.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <utils/Log.h>
    
    #include <hardware/ibo_hal.h>
    
    #define LED_ON _IO('k',1)
    #define LED_OFF _IO('k',0)
    static int fd;
    
    /** Close this device */
    static int ibo_close(struct hw_device_t* device)
    {
        close(fd);
        ALOGI("ibo_close: %d",fd);
        return 0;
    }
    
    static int ibo_open(struct iboled_device_t* dev)
    {
        fd = open("/dev/led", O_RDWR);
        ALOGI("ibo_open : %d", fd);
            return fd ;
    }
    
    static int ibo_ctrl(struct iboled_device_t* dev, int cmd)
    {
        if(cmd == 0)
            ioctl(fd,LED_OFF);
        else
            ioctl(fd,LED_ON);
    
        ALOGI("ibo_ctrl: %d",cmd);
        return 0;
    }
    
    static struct iboled_device_t ibo_dev = {
        .common = {
            .close = ibo_close,
        },
        .ibo_open  = ibo_open,
        .ibo_ctrl  = ibo_ctrl,
    };
    
    static int iboled_device_open(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device)
    {
        *device =(hw_device_t*) &ibo_dev;
        return 0;
    }
    
    
    static struct hw_module_methods_t led_module_methods = {
        .open = iboled_device_open,
    };
    
    struct hw_module_t HAL_MODULE_INFO_SYM = {
        .id = "iboled",
        .methods = &led_module_methods,
    };
    
    Android.mk
    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE := iboled.default
    
    LOCAL_MODULE_RELATIVE_PATH := hw
    LOCAL_C_INCLUDES := hardware/libhardware
    LOCAL_SRC_FILES := ibo_hal.c
    LOCAL_SHARED_LIBRARIES := liblog
    LOCAL_MODULE_TAGS := eng
    LOCAL_MODULE_PATH:= $(LOCAL_PATH)
    include $(BUILD_SHARED_LIBRARY)
    
    4.在 iboled 文件夹下输入 mm 命令 , hw 文件夹下生成 iboled.default.so 文件
    5. 把 iboled.default.so 文件拷贝到 fastboot 根目录
    6.输入adb push iboled.default.so /system/lib/hw命令把文件传到平板的 /system/lib/hw 文件夹下,hal层的代码完成了, 现在去写jni 。
    7.在 androidL 目录下新建 testhal 文件夹
    8.testhal 文件夹下新建Android.mk文件
    Android.mk
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE:= libiboled
    LOCAL_SRC_FILES:= src/iboled.c
    LOCAL_MODULE_TAGS:= eng
    LOCAL_SHARED_LIBRARIES:= liblog libhardware 
    LOCAL_MODULE_PATH:= $(LOCAL_PATH)/lib
    LOCAL_C_INCLUDES:= $(LOCAL_PATH)/inc
    include $(BUILD_SHARED_LIBRARY)
    
    9.在testhal 文件夹下新建src文件夹 , src 文件夹下新建 iboled.c文件。
    iboled.c
    #include <jni.h>
    #include <sys/ioctl.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <android/log.h>
    #include <hardware/hardware.h>
    #include <utils/Log.h>
    #include <hardware/ibo_hal.h>
    
    struct iboled_device_t *iboled_device;
    
    jint Jopen(JNIEnv *env,jobject obj){
        jint err;
        hw_module_t* module;
        hw_device_t* device;
    
        ALOGI("ibo ledOpen ...");
    
        err = hw_get_module("iboled", (hw_module_t const**)&module);
        if (err == 0) {
            err = module->methods->open(module, NULL, &device);
            if (err == 0) {
                iboled_device = (struct led_device_t *)device;
                return iboled_device->ibo_open(iboled_device);
            }
        }
        return -1;  
    }
    
    void Jclose(JNIEnv *env,jobject obj){
        ALOGI("ibo Jclose ...");
        iboled_device -> common.close((hw_device_t *)iboled_device);
        return ;
    }
    
    void  Jioctl(JNIEnv *env,jobject obj,jint cmd){
        ALOGI("ibo Jioctl ...");
        iboled_device -> ibo_ctrl(iboled_device,cmd);
        return ;
    }
    
    JNINativeMethod method[] = {
        {(char *)"ibo_open",(char *)"()I",(void *)Jopen},
        {(char *)"ibo_close",(char *)"()V",(void *)Jclose},
        {(char *)"ibo_ioctl",(char *)"(I)V",(void *)Jioctl},
    };
    
    JNIEXPORT jint JNICALL
    JNI_OnLoad(JavaVM *jvm, void *reserved)
    {
        JNIEnv *env;
        jclass cls;
    
        if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2)) {
            return JNI_ERR; /* JNI version not supported */
        }
        ALOGI("ibo JNI_OnLoad ...");
        cls = (*env)->FindClass(env, "com/ibo/jnidemo/TestJni"); //注意这里的包名换成自己app的包名。
        if (cls == NULL) {
            return JNI_ERR;
        }
    
        (*env) -> RegisterNatives(env,cls,method,sizeof(method)/sizeof(JNINativeMethod));
        return JNI_VERSION_1_2;
    }
    
    10.在testhal 文件夹下输入 mm 命令,testhal/lib 目录下生成了 libiboled.so 文件。
    11.输入adb push libiboled.so /system/lib/命令把文件传到平板的 /system/lib/ 目录下 。
    12.打开eclipse新建android项目,新建一个类.
    TestJni.java
    class TestJni{
        static{
            System.loadLibrary("iboled");
        }
    
        public native int ibo_open();
        public native void ibo_close();
        public native void ibo_ioctl(int cmd);
    }
    
    13.MainActivity :
    MainActivity.java
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        private Button btn;
        boolean isLight = false;
        private TestJni ibo;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            ibo = new TestJni();
            int open = ibo.ibo_open();
            if (open<0){
                System.out.println("####  open error ! "+open);
                return;
            }
    
            btn = (Button) findViewById(R.id.btn);
            btn.setOnClickListener(this);
        }
    
        @Override
        protected void onDestroy() {
            ibo.ibo_ioctl(0);
            ibo.ibo_close();
            super.onDestroy();
        }
    
        @Override
        public void onClick(View v) {
            if (isLight){
                isLight=false;
                ibo.ibo_ioctl(1);
                btn.setText("on");
            }else{
                isLight=true;
                ibo.ibo_ioctl(0);
                btn.setText("off");
            }
        }
    }
    

    完成了!

    相关文章

      网友评论

          本文标题:4-安卓底层-HAL层-实现安卓控制LED

          本文链接:https://www.haomeiwen.com/subject/ifmewttx.html