android_10.0.0_r41自定义驱动编译+Framework层调用_3(自定义驱动HAL层)

参考文献:https://blog.csdn.net/luoshengyang/java/article/details/6568411  //罗升阳博客   

Android_10.0.0_r41自定义驱动HAL层

完成内核驱动程序后,便可以在Android系统中得到三个文件/dev/hello、/sys/class/hello/hello/val和/proc/hello。我们将通过/dev/hello来连接硬件抽象层模块(HAL)和Linux内核驱动程序模块。

1.定义HAL抽象层hello.h头文件

进入到hardware/libhardware/include/hardware目录,新建hello.h文件

​
#ifndef ANDROID_HELLO_INTERFACE_H
#define ANDROID_HELLO_INTERFACE_H
#include <hardware/hardware.h>
​
//__BEGIN_DECLS
​
#if defined(__cplusplus)
extern "C" {
#endif
    /* define module ID */
    #define HELLO_HARDWARE_MODULE_ID "hello"
​
    /* hardware module struct */
    struct hello_module_t {
        struct hw_module_t common;
    };
​
    /* hardware interface struct */
    struct hello_device_t {
        struct hw_device_t common;
        int fd; // 设备文件描述符,对应我们将要处理的设备文件"/dev/hello"
        int (*set_val)(struct hello_device_t* dev, int val); // set_val 为HAL对上提供的函数接口
        int (*get_val)(struct hello_device_t* dev, int* val); // // get_val 为HAL对上提供的函数接口
    };
#if defined(__cplusplus)
}
#endif
//__END_DECLS
#endif

这里按照标准Android硬件标准抽象层规范要求,分别定义模块ID、模块结构体以及硬件接口结构体。在硬件接口结构体中,fd表示设备文件描述符,对应我们将要处理的设备/dev/hello,set_val和get_val为该HAL对上提供的函数接口。

2.实现硬件访问hello.c

进入到hardware/libhardware/modules目录,新建hello目录,并添加hello.c文件。

#define LOG_TAG "HelloStub"
​
#include <hardware/hardware.h>
#include <hardware/hello.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
​
#include <string.h> /* memset */
#include <unistd.h> /* close */
#include <stdio.h>
#include <stdlib.h>
​
​
​
#define DEVICE_NAME "/dev/hello"
#define MODULE_NAME "Hello"
#define MODULE_AUTHOR "momxmo"
​
/*设备打开和关闭接口*/
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);
​
​
/* 模块方法表*/
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, tag也必须为HARDWARE_MODULE_TAG
// 这是android硬件抽象层规范规定的。
    .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,
    }
};
​
​
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));
    ALOGD("Hello Stub: hello_device_open_name=%s",name);
​
    if(!dev) {
        ALOGE("Hello Stub: failed to alloc space");
        return -EFAULT;
    }
​
// memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值,
//这个函数通常为新申请的内存做初始化工作
    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;
// open是UNIX系统(包括LINUX、Mac等)的系统调用函数,区别于C语言库函数fopen
// https://baike.baidu.com/item/open/13009226#1_1
    if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
        ALOGE("Hello Stub:failed to open /dev/hello --%s.", strerror(errno));
        free(dev);
        return -EFAULT;
    }
​
    *device = &(dev->common);
    ALOGI("Hello Stub: open /dev/hello successfully.");
​
    return 0;
}
​
​
static int hello_device_close(struct hw_device_t* device) {
//强转类型
    struct hello_device_t* hello_device = (struct hello_device_t*)device;
    ALOGD("Hello Stub: hello_device_close");
    if(hello_device) {
        close(hello_device->fd);
        ALOGI("Hello Stub:hello_device_close hello_device->fd:%d", hello_device->fd);
        free(hello_device);
    }
​
    return 0;
}
​
static int hello_set_val(struct hello_device_t * dev, int val) {
    ALOGI("Hello Stub: hello_set_val set value %d to device.", val);
// write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。 fd,是open时打开的
// 例: int fp = open("/home/test.txt", O_RDWR|O_CREAT);
    int result = write(dev->fd, &val, sizeof(val));
    ALOGI("Hello Stub:hello_set_val result:%d", result);
    return 0;
}
​
​
static int hello_get_val(struct hello_device_t* dev, int* val) {
    ALOGD("Hello Stub: hello_get_val");
    if(!val) {
        ALOGE("Hello Stub: error val pointer");
        return -EFAULT;
    }
    int result = read(dev->fd, val, sizeof(*val));
    ALOGI("Hello Stub:hello_get_val result:%d", result);
    ALOGI("Hello Stub: get value %d from device.", *val);
    return 0;
}

3.节点权限问题:

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/vndbinder            0666   root       root
/dev/hello                0666   root       root    //添加此行

4.在hardware/libhardware/modules/hello目录中,新建Android.bp文件:

cc_library_shared {
    name: "hello.default",
    relative_install_path: "hw",
    proprietary: true,
    srcs: ["hello.c"],
    cflags: ["-Wall", "-Werror"],
    header_libs: ["libhardware_headers"],
    shared_libs: [
        "libcutils",
        "liblog",
        "libutils",
    ],
}

注意: LOCAL_MODULE的定义规则,hello后面跟有default, hello.default能够保证我们的模块总能被硬象抽象层加载到

5.将hello模块添加到系统

这里的目的是保证在整编译系统的时候,才会将hello模块打包到vendor.img镜像中; 需要在/hardware/libhardware/modules/Android.mk中添加hello模块:

hardware_modules := \
    camera \
    gralloc \
    sensors \
    hello   //  添加此行
include $(call all-named-subdir-makefiles,$(hardware_modules))

6.编译,执行命令

mmm hardware/libhardware/modules/hello

编译成功后,就可以在Android/out/target/product/angler/vendor/lib64/hw目录下看到hello.default.so文件了。

7.so库导入系统中

如果不想重新打包镜像系统,这里我们可以通过adb push的方式将hello.default.so文件push到手机系统/vendor/lib64/hw/目录下,并添加777权限;可以通过adb shell命令,进入到此文件夹观察hello.default.so文件是否存在此目录下.

8.这样hello.default.so文件已经存在系统的vendor/lib64/hw文件夹中,但添加app后打开HAL层后仍报错:log显示 : Access denied finding property "ro.hardware.hello" 很明显是权限不允许。然后从Hello JNI: failed to get hello stub hw_module.的打印可以知道是 hw_get_module 的时候没有找到我们新添加的HAL层。 其实后面还有一句比较长的log

system_server: type=1400 audit(0.0:111): avc: denied { read } for name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=292 scontext=u:r:system_server:s0 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0

通过前的log我们知道,这个是selinux权限的问题,因此这里需要配置selinux的权限

修改两个文件:/system/sepolicy/prebuilts/api/29.0/public/property_contexts和/system/sepolicy/public/property_contexts:

*****************************************************************************
ro.hardware.fingerprint u:object_r:exported_default_prop:s0 exact string
ro.hardware.hello u:object_r:exported_default_prop:s0 exact string  //添加这一行
ro.hardware.flp u:object_r:exported_default_prop:s0 exact string
*****************************************************************************

之后还是报错:

avc: denied { read } for name="hello.default.so" dev="dm-1" ino=596 scontext=u:r:system_server:s0 tcontext=u:object_r:vendor_file:s0 tclass=file permissive=0

还需要修改 /system/sepolicy/vendor/file_contexts:

# Same process HALs installed by platform into /vendor
/(vendor|system/vendor)/lib(64)?/hw/android\.hardware\.graphics\.mapper@2\.0-impl\.so u:object_r:same_process_hal_file:s0
/(vendor|system/vendor)/lib(64)?/hw/android\.hardware\.graphics\.mapper@3\.0-impl\.so u:object_r:same_process_hal_file:s0
/(vendor|system/vendor)/lib(64)?/hw/android\.hardware\.renderscript@1\.0-impl\.so     u:object_r:same_process_hal_file:s0
/(vendor|system/vendor)/lib(64)?/hw/gralloc\.default\.so                              u:object_r:same_process_hal_file:s0
/(vendor|system/vendor)/lib(64)?/hw/hello\.default\.so                              u:object_r:same_process_hal_file:s0   //添加这一行

编译完后又发现有报错:

system_server: type=1400 audit(0.0:114): avc: denied { read write } for name="hello" dev="tmpfs" ino=7375 scontext=u:r:system_server:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=0 还需要配置selinux权限,分别修改/system/sepolicy/private/和/system/sepolicy/prebuilts/api/29.0/private/文件夹下的file_contexts和system_server.te文件,/system/sepolicy/public/和/system/sepolicy/prebuilts/api/29.0/public/文件夹下的device.te文件:

file_contexts:

************************************************************
/dev/zero       u:object_r:zero_device:s0
/dev/hello      u:object_r:hello_device:s0     //添加这一行
/dev/__properties__ u:object_r:properties_device:s0
/dev/__properties__/property_info   u:object_r:property_info:s0

system_server.te:

************************************************************
allow system_server adbd_socket:sock_file rw_file_perms;
allow system_server rtc_device:chr_file rw_file_perms;
allow system_server audio_device:dir r_dir_perms;
allow system_server hello_device:chr_file rw_file_perms;  //添加这一行

device.te:

***********************************************************
type tty_device, dev_type;
type video_device, dev_type;
type zero_device, dev_type, mlstrustedobject;
type hello_device, dev_type, mlstrustedobject;  //添加这一行
type fuse_device, dev_type, mlstrustedobject;
**********************************************************

之后重新编译完成就可以了,但是直接编译m可能会出错,也是由于权限策略的问题,可以使用命令:

make SELINUX_IGNORE_NEVERALLOWS=true

正常完成编译并打开系统应用后,初始化可以得到以下日志:

/system_process D/HelloManagerService: HelloService init
/system_process I/HelloService: Hello JNI: initializing......
/system_process I/HelloService: Hello JNI: hello Stub found.
/system_process D/HelloStub: Hello Stub: hello_device_open_name=hello
/system_process I/HelloStub: Hello Stub: open /dev/hello successfully.
/system_process I/HelloService: Hello JNI: hello device is open.

可以发现已经找到驱动并已经打开,说明HAL层添加成功.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值