赞
踩
参考文献: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层添加成功.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。