本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
android驱动开发基础
奔跑的男人 · 2619浏览 · 发布于2019-09-05 +关注

基本环境

  • 开发板:hikey960

  • 代码:aosp,Android R

  • 开发环境:64bit ubuntu 16.04,

一、build

添加新的lunch选项(新产品):赋值COMMON_LUNCH_CHOICES,PRODUCT_MAKEFILES如下 (参考device/sample/products/AndroidProducts.mk,实例参考device/linaro/hikey/AndroidProducts.mk)

PRODUCT_MAKEFILES := \
  $(LOCAL_DIR)/sample_addon.mk
 
COMMON_LUNCH_CHOICES := sample_addon-userdebug

二、编写Android.mk

Android.mk模板如下所示:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
        hello.c
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := hello_elf_arm64
include $(BUILD_EXECUTABLE)

编译报unused parameters错时,需加上:LOCAL_CFLAGS := -Wno-unused-parameter 以忽略掉该报错。

三、LOG系统

log系统的设备节点:

hikey960:/ # ls /dev/socket/log* -l                                            
srw-rw-rw- 1 logd logd 0 1970-01-01 00:00 /dev/socket/logd
srw-rw-rw- 1 logd logd 0 1970-01-01 00:00 /dev/socket/logdr
s-w--w--w- 1 logd logd 0 1970-01-01 00:00 /dev/socket/logdw


转载来自文章《Android Log系统介绍 (基于Android N)》的log系统核心图如下所示,更多细节请参考此文。

      2019080214433979.png                

图1 Android log系统


  • Android Q、R上面log的缓冲区有:main,system,radio,events,crash


  • c/c++中打印调试信息:

 ① 宏定义标签及添加头文件:

  #define LOG_TAG "hello"

  #include <log/log.h>

 ② Android.mk中声明所依赖的动态库:

  LOCAL_SHARED_LIBRARIES := \

    liblog

 ③ 使用宏ALOGV(),ALOGD(“xxx”),ALOGI(),ALOGW(),ALOGE(),进行打印log


  • java中打印log信息:

 ① 导入包:import android.util.Log;

 ② 在类内部定义标签:private final String TAG = "listview";

 ③ 使用方法Log.d(TAG, "xxx");,Log.i(TAG, "xxx");,Log.w(TAG, "xxx");,Log.e(TAG, "xxx");进行打印


四、init.rc

参考system/core/init/init.cpp中如下代码,可知/init祖先进程通过解析/init.rc的内容来进行启动各种本地服务

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/init.rc");


  • android init语言说明文档:system/core/init/README.md

  • 在厂商.rc中添加规则,实例:device/linaro/hikey/init.common.rc

五、JNI

以控制LED灯为背景,进行JNI编程技术的介绍。aosp中JNI技术参考代码:development/samples/SimpleJNI/

1. java调用jni接口

将native接口的声明组织到LedControl\app\src\main\java\com\example\lowlevel\LedNative.java

package com.example.lowlevel;
public class LedNative {
/* 1) 加载jni动态库 */
    static {
        System.loadLibrary("led_jni");
    }
/* 2) 使用关键字native声明jni接口 */
    public native int openDev();
    public native int closeDev();
    public native int devOn();
    public native int devOff();
}


然后在class MainActivity中进行调用:

/* 3) 使用上面定义的LedNative类生成对象后调用接口 */
LedNative ledNative = new LedNative();
ledNative.devOn();


注意:普通app没有权限加载jni动态库依赖的库,需将apk预置或传入到手机的/system/app/下作为system_app

2. aosp中开发native代码

 ① 定义JAVA和C/C++间的映射表:

static JNINativeMethod ledMethod[] = { 
  {"openDev", "()I", (void*)openLed},
  {"closeDev", "()I", (void*)closeLed},
  {"devOn", "()I", (void*)ledOn},
  {"devOff", "()I", (void*)ledOff},
};


 ② 通过定义jni层的回调函数jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/),来将上述映射表注册到java虚拟机:

static const char *classPathName = "com/example/lowlevel/LedNative";
jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{
    jint ret;
    JNIEnv* env = NULL;
    ALOGD("------%s", __FUNCTION__);
    ret = vm->GetEnv((void**)&env, JNI_VERSION_1_4);
    if (ret != JNI_OK) {
        ALOGE("ERROR: GetEnv failed");
        return -1; 
    }   
    jclass cls = env->FindClass(classPathName);
    if (cls == NULL) {
        ALOGE("Native registration unable to find class '%s'", classPathName);
        return JNI_FALSE;
    }
    ret = env->RegisterNatives(cls, ledMethod, sizeof(ledMethod)/sizeof(ledMethod[0]));
    if (ret < 0) {
        ALOGE("RegisterNatives failed for '%s'", classPathName);
        return JNI_FALSE;
    }
    return JNI_VERSION_1_4;
}


 ③ Android.mk中指定生成的是动态库和编译工具分别如下:

LOCAL_MODULE:= libled_jni
include $(BUILD_SHARED_LIBRARY)


注意:测试时,需要修改设备节点权限:# chmod 0666 /sys/devices/platform/leds/leds/user_led3/brightness,以及关闭selinux:# setenforce 0


六、传统HAL

本章介绍开发传统HAL代码和调用HAL的主要流程,如下出现的代码场景是控制LED灯。


1. 接口声明

开发HAL在头文件,定义相应硬件模块和设备结构,以继承基础模块结构(struct hw_module_t)和基础设备结构(struct hw_device_t)。另外还有暴露接口给调用者的目的。具体开发流程如下所示:

#ifndef __LED_HAL_H__
#define __LED_HAL_H__
#include <hardware/hardware.h>
__BEGIN_DECLS
/* 1) 定义生成的动态库前缀的名称, xxx.default.so */
#define LED_HARDWARE_MODULE_ID "led_hal"
/* 2) 定义专有硬件模块,继承 hw_module_t,并扩展要暴露的模块控制接口 */
typedef struct led_module {
    struct hw_module_t common;
} led_module_t;
/* 3) 定义专有硬件硬件,继承 hw_device_t */
typedef struct led_device {
    struct hw_device_t common;
/* 4) 扩展要暴露出来的设备控制接口 */
    int (*control)(int enable);
} led_device_t;
/* 5) 用来调用 hw_module_t.methods->open() */
static inline int led_hal_open(const struct hw_module_t* module,
        led_device_t** device) {
    return module->methods->open(module, NULL,
            TO_HW_DEVICE_T_OPEN(device));
}
/* 6) 用来调用 hw_device_t.close() */
static inline int led_hal_close(led_device_t* device) {
    return device->common.close(&device->common);
}
__END_DECLS
#endif


2. 接口实现

 ① 实例化自己派生的专有硬件模块结构led_module_t;


led_module_t HAL_MODULE_INFO_SYM = {                /* 1.实例名必须为 HAL_MODULE_INFO_SYM */
    .common = {
        .tag = HARDWARE_MODULE_TAG,/* 2.基础结构标签必为 HARDWARE_MODULE_TAG */
        .module_api_version = HARDWARE_MODULE_API_VERSION(1,0),/* 3.模块版本为1.0 */
        .hal_api_version = HARDWARE_HAL_API_VERSION,/* 4.固定 */
        .id = LED_HARDWARE_MODULE_ID,/* 5.绑定最后生成的动态库名:[id].default.so */
        .name = "Hikey960 led HAl",
        .author = "luciferzhu@yeah.net",
        .methods = &led_module_methods,/* 6.指定激活模块下设备的方法,下文中介绍 */
    },
};


 ② 实例化struct hw_module_methods_t,该结构下成员只有open;

struct hw_module_methods_t led_module_methods = {
    .open = led_module_open,/* 在下文中定义 */
};


 ③ 定义hw_module_methods_t.open(),它的任务是实例化struct hw_device_t的派生结构体

led_device_t;
int led_module_open(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device)
{
    led_device_t* led_dev = (led_device_t*)calloc(1, sizeof(led_device_t)); 
    /* 1.申请内存实例化派生结构 */
    led_dev->common.tag = HARDWARE_DEVICE_TAG;/* 2.HAL设备标签,值固定 */
    led_dev->common.version = HARDWARE_DEVICE_API_VERSION(1, 0);
    led_dev->common.module = (struct hw_module_t*)module;
    led_dev->common.close = led_device_close;/* 2.close函数与本函数任务相反,用于释放相关资源 */
    led_dev->control = led_control;/* 3.对接自定义的接口 */
    *device = (hw_device_t*) led_dev;/* 4.通过参数返回实例化后设备指针,HAL调用者通过此指针调用设备的控制接口 */
    return 0;
};


 ④ 定义hw_device_t.close(),HAL调用者调用此函数用于关闭设备和释放hw_module_methods_t.open()中申请的资源;

int led_device_close(struct hw_device_t* device)
{
    led_device_t* priv = (led_device_t*) device;
    if (priv)
        free(priv);
    return 0;
}

 ⑤ 实现暴露给HAL调用者的接口,本场景要实现int led_control(int enable);

3. 添加编译规则

 ① 动态库名应为[id].default.so,所以LOCAL_MODULE:= led_hal.default;

 ② HAL动态库应部署到/system/lib64/hw/和/system/lib/hw/下,需加上规则LOCAL_MODULE_RELATIVE_PATH := hw;

 ③ 导出暴露给调用者的接口头文件。部署到system分区的HAL库,使用LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include;部署到vendor分区的HAL库,应使用LOCAL_HEADER_LIBRARIES := libhardware_headers。


  • 若要HAL动态库部署到/vendor/lib64/hw/和/vendor/lib/hw/,还需加上规则LOCAL_PROPRIETARY_MODULE := true

HAL参考代码:hardware/libhardware/modules/gralloc

4. 使用HAL的接口

 ① 加载硬件的HAL模块,使用 int hw_get_module(const char *id, const struct hw_module_t **module);

id——HAL动态库名字的前缀,本场景值为led_hal;
module——通过此参数获得hw_module_t 实例
#include <hardware/hardware.h>
#include <led_hal.h>
static led_module_t* pModule = NULL;/* 全局变量 */
int ret = hw_get_module(LED_HARDWARE_MODULE_ID, (const struct hw_module_t **)&pModule);
if (ret != 0) {
    ALOGE("get hardware module '%s' failed", LED_HARDWARE_MODULE_ID);
    return -1; 
}

 ② 通过hw_module_t 的派生结构体led_module_t,获取hw_device_t的派生结构体led_device_t,方法如下所示。但为了方便调用者,HAL接口头文件一般会将下面的代码实现到内联函数中,如调用前文的led_hal_open((const struct hw_module_t*)pModule, &pDevice);;

static led_device_t* pDevice = NULL;
pModule->common.methods->open((const struct hw_module_t *)pModule, NULL, (struct hw_device_t**)&pDevice);

 ③使用HAL接口进行硬件控制的形式:pDevice->foo(xxx);;如本场景使用:pDevice->control(1);来点亮LED;

 ④ 在需要关闭设备和释放有关内存资源时,需调用hw_device_t.close(),所以调用方法如下所示。但为了方便调用者,HAL接口头文件一般会将下面的代码实现到内联函数中,如调用前文的led_hal_close(pDevice);;

pDevice->common.close((struct hw_device_t*)pDevice);


注:调用HAL动态库的代码,需要链接动态库libhardware,故Android.mk有LOCAL_SHARED_LIBRARIES := libhardware。

aosp中调用HAL的参考代码:frameworks/native/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp


相关推荐

android下vulkan与opengles纹理互通

talkchan · 1173浏览 · 2020-11-23 10:37:39
Android 使用RecyclerView实现轮播图

奔跑的男人 · 2171浏览 · 2019-05-09 17:11:13
微软发布新命令行工具 Windows Terminal

吴振华 · 866浏览 · 2019-05-09 17:15:04
Facebook 停止屏蔽部分区块链广告

· 752浏览 · 2019-05-09 17:20:08
加载中

0评论

评论
分类专栏
小鸟云服务器
扫码进入手机网页