文章目录
-
- [1. 数据结构](#1. 数据结构)
-
- 1) hw_module_t hw_module_t)
- 2) hw_module_methods_t hw_module_methods_t)
- 3) hw_device_t hw_device_t)
- [2. 程序编写](#2. 程序编写)
- [3. 编译程序](#3. 编译程序)
- [4. 验证程序](#4. 验证程序)
- [5. 添加权限](#5. 添加权限)
-
- 1) 设备节点添加权限 设备节点添加权限)
- 2) 添加 shell linux 权限 添加 shell linux 权限)
作者: baron
对 linux 驱动程序进行封装,其主要设计意图是向下屏蔽设备以及其驱动的实现细节,向上为系统服务以及 Framework 提供提供统一的设备访问接口。就是 linux 驱动只提供硬件读写接口, 业务逻辑通过 hall 封装成 so 库. 这样就不用遵循 kernel 的 gpl 开源协议, 从而保护厂商的利益. 不过也因为这个原因安卓被 linux 踢出了内核主线程.
1. 数据结构
1) hw_module_t
用来表示硬件的抽象
- 每一个模块都必须自定义一个硬件抽象层模块结构体,而且他的第一个成员变量的类型必须为
hw_module_t
. - 硬件抽象层每一个模块都必须声明为
HAL_MODULE_INFO_SYM
- 结构体
hw_module_t
的成员变量tag
的值必须设置为HARDWARE_MODULE_TAG
- dso 用来保存加载硬件抽象层模块后得到的句柄值.
cpp
// libhardware/include/hardware/hardware.h
typedef struct hw_module_t {
uint32_t tag; // 值必须声明为 HARDWARE_MODULE_TAG
uint16_t module_api_version; // 模块 API 版本
#define version_major module_api_version
uint16_t hal_api_version; // HAL API 版本
#define version_minor hal_api_version
const char *id; // 模块的唯一标识符, 通过该标识符查找该 module
const char *name; // 模块的名称
const char *author; // 模块的作者
struct hw_module_methods_t* methods; // 模块的方法集合
void* dso; // 模块的共享对象(动态共享库)
#ifdef __LP64__
uint64_t reserved[32-7]; // 保留字段,64 位系统上使用 64 位整数数组
#else
uint32_t reserved[32-7]; // 保留字段,32 位系统上使用 32 位整数数组
#endif
} hw_module_t;
2) hw_module_methods_t
封装 open 函数, 通过 open 函数获取 hw_device_t
cpp
// libhardware/include/hardware/hardware.h
typedef struct hw_module_methods_t {
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
3) hw_device_t
open 函数返回的结构, 硬件设备结构的第一个结构.
tag
的值必须设置为HARDWARE_DEVICE_TAG
- close 回调接口用来关闭设备
cpp
// libhardware/include/hardware/hardware.h
typedef struct hw_device_t {
uint32_t tag; // 赋值为 HARDWARE_DEVICE_TAG
uint32_t version; // 版本号
struct hw_module_t* module; // 属于哪个 hw_module_t
#ifdef __LP64__
uint64_t reserved[12];
#else
uint32_t reserved[12];
#endif
int (*close)(struct hw_device_t* device); // close 方法
} hw_device_t;
有了这几个接口就可以用来封装我们驱动接口了.
2. 程序编写
创建头文件: hardware/libhardware/include/hardware/hello.h
内容如下.
cpp
// include/hardware/hello.h
#ifndef ANDROID_INCLUDE_HARDWARE_HELLO_H
#define ANDROID_INCLUDE_HARDWARE_HELLO_H
#include <stdbool.h>
#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <hardware/hardware.h>
#include <hardware/hw_auth_token.h>
#define HELLO_HARDWARE_MODULE_ID "hello"
// 创建一个 hello_module_t 类用来描述硬件的抽象
// 它的第一个结构必须是 hw_module_t
// 它必须被实例化为 HAL_MODULE_INFO_SYM
typedef struct hello_module {
struct hw_module_t common;
}hello_module_t;
// 硬件设备结构 hello_device
// 它的第一个结构必须为 hw_device_t, 因为这样就可以通过 hw_device_t 拿到 hello_device
typedef struct hello_device {
struct hw_device_t common;
int fd;
int (*write_string)(struct hello_device* dev, const char *str);
int (*read_string)(struct hello_device* dev, char* str);
}hello_device_t;
#endif /* ANDROID_INCLUDE_HARDWARE_HELLO_H */
创建 hardware/libhardware/modules/hello/hello.c
文件
cpp
// hardware/libhardware/modules/hello/hello.c`
#define LOG_TAG "Legacy HelloHAL"
#include <malloc.h>
#include <stdint.h>
#include <log/log.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cutils/atomic.h>
#include <stdlib.h>
#include <unistd.h>
#include <hardware/hardware.h>
#include <hardware/hello.h>
#define DEVICE_NAME "/dev/hello"
#define MODULE_NAME "Default Hello HAL"
#define MODULE_AUTHOR "The Android Open Source Project"
// 接口声明
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_write_string(struct hello_device* dev, const char * str);
static int hello_read_string(struct hello_device* dev, char* str);
// 创建 hw_module_methods_t 用来提供 open 方法
static struct hw_module_methods_t hello_module_methods = {
.open = hello_device_open,
};
// 实例化 hello_module_t 硬件抽象模块为 HAL_MODULE_INFO_SYM
// ******** 必须实例化为 HAL_MODULE_INFO_SYM 这个名字不能变
hello_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG, // 必须设置为这个 tag
.module_api_version = 1,
.hal_api_version = 1,
.id = HELLO_HARDWARE_MODULE_ID, // 通过这个查找对应的 moduel
.name = MODULE_NAME,
.author = MODULE_AUTHOR,
.methods = &hello_module_methods, // 设置 open 方法
},
};
// open 方法的具体实现
static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device)
{
// 创建一个 hello_device_t 结构
hello_device_t *dev = malloc(sizeof(hello_device_t));
memset(dev, 0, sizeof(hello_device_t));
ALOGE("Hello: hello_device_open name = %s",name);
dev->common.tag = HARDWARE_DEVICE_TAG; // 必须设置为 HARDWARE_DEVICE_TAG
dev->common.version = 0;
dev->common.module = (hw_module_t*)module; // 设置 module
dev->common.close = hello_device_close; // 设置关闭设备接口
dev->write_string = hello_write_string; // 设置 write 方法
dev->read_string = hello_read_string; // 设置 read 方法
// 打开设备
if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
ALOGE("Hello: open /dev/hello fail-- %s.", strerror(errno));free(dev);
return -EFAULT;
}
// 返回 hw_device_t 结构
*device = &(dev->common);
ALOGE("Hello: open /dev/hello successfully.");
return 0;
}
// 关闭设备释放资源
static int hello_device_close(struct hw_device_t* device)
{
// 通过 device 可以拿到 hello_device
struct hello_device* hello_device = (struct hello_device*)device;
if(hello_device) {
close(hello_device->fd);
free(hello_device);
}
return 0;
}
// 写方法实现
static int hello_write_string(struct hello_device* dev,const char * str)
{
ALOGE("Hello:write string: %s", str);
write(dev->fd, str, sizeof(str));
return 0;
}
// 读方法实现
static int hello_read_string(struct hello_device* dev, char* str)
{
ALOGE("Hello:read hello_read_string");
read(dev->fd,str, sizeof(str));
return 0;
}
3. 编译程序
创建 hardware/libhardware/modules/hello/Android.bp
添加内容如下.
cpp
cc_library_shared {
name: "hello.default",
relative_install_path: "hw",
proprietary: true,
srcs: ["hello.c"],
cflags: ["-Wall", "-Werror"],
header_libs: ["libhardware_headers"],
shared_libs: [
"liblog",
"libcutils",
"libutils",
],
}
在 build/make/target/product/full_base.mk
中添加如下内容, 将我们的添加的 hall 库编译进系统.对应的位置为 /vendor/lib/hw/hello.default.so
.
diff
diff --git a/target/product/full_base.mk b/target/product/full_base.mk
index ffd3cde11a..9f7270bd2f 100644
-- a/target/product/full_base.mk
++ b/target/product/full_base.mk
@@ -32,7 +32,8 @@ PRODUCT_PACKAGES += \
# audio.a2dp.default is a system module. Generic system image includes
# audio.a2dp.default to support A2DP if board has the capability.
PRODUCT_PACKAGES += \
- audio.a2dp.default
+ audio.a2dp.default \
+ hello.default
运行 ./build.sh -UKAup
编译代码. 编译完成之后可以在 out 目录下发现 hello.default
shell
$ find out/ -name "hello\.default"
out/soong/.intermediates/hardware/libhardware/modules/hello/hello.default
4. 验证程序
添加验证代码 frameworks/base/services/core/jni/com_android_server_AlarmManagerService.cpp
修改如下
对应代码
cpp
#include <hardware/hardware.h>
#include <hardware/hello.h>
struct hello_device* hello_device = NULL;
static inline int hello_device_open(const hw_module_t* module, struct hello_device** device)
{
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}
static jint HelloServiceInit()
{
ALOGE("HelloServiceInit HelloServiceInit");
const hw_module_t *hw_module = NULL;
ALOGE("Hello JNI: initializing......");
// 通过 HELLO_HARDWARE_MODULE_ID 找到对应的 hw_module
if(hw_get_module(HELLO_HARDWARE_MODULE_ID, &hw_module) == 0) {
ALOGE("Hello JNI: hello Stub found.");
// 调用 open 接口获取到 hello_device
if(hello_device_open(hw_module, &hello_device) == 0) {
ALOGE("Hello JNI: hello device is open.");
return 0;
}
ALOGE("Hello JNI: failed to open hello device.");
return -1;
}
ALOGE("Hello JNI: failed to get hello stub hw_module.");
return -1;
}
修改 system/sepolicy/vendor/file_contexts
添加库的位置让系统能够找到, 该正则表达式指定了库的位置为 /vendor/lib64/hw/hello.default.so
diff
diff --git a/vendor/file_contexts b/vendor/file_contexts
index 1b2bc2357..92c166b6c 100644
-- a/vendor/file_contexts
++ b/vendor/file_contexts
@@ -86,6 +86,7 @@
/(vendor|system/vendor)/lib(64)?/hw/android\.hardware\.graphics\.mapper@4\.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
刷机开机打印 log 如下
cpp
01-18 07:32:55.775 419 419 E AlarmManagerService: HelloServiceInit HelloServiceInit
01-18 07:32:55.776 419 419 E AlarmManagerService: Hello JNI: initializing......
01-18 07:32:55.778 419 419 E AlarmManagerService: Hello JNI: hello Stub found.
01-18 07:32:55.778 419 419 E Legacy HelloHAL: Hello: hello_device_open name = hello
01-18 07:32:55.778 419 419 E Legacy HelloHAL: Hello: open /dev/hello fail-- Permission denied.
01-18 07:32:55.778 419 419 E AlarmManagerService: Hello JNI: failed to open hello device.
提示没没有权限
5. 添加权限
1) 设备节点添加权限
给我们的设备节点添加权限修改路径 system/core/rootdir/
如下
diff
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 1550894ce..428cc1ec2 100644
-- a/rootdir/ueventd.rc
++ b/rootdir/ueventd.rc
@@ -39,6 +39,7 @@ subsystem sound
/dev/vndbinder 0666 root root
/dev/pmsg0 0222 root log
+/dev/hello 0666 root root
# kms driver for drm based gpu
/dev/dri/* 0666 root graphics
添加权限之后报错如下
cpp
01-17 09:52:50.271 265 265 E MtpDeviceJNI: HelloServiceInit HelloServiceInit
01-17 09:52:50.272 265 265 E MtpDeviceJNI: Hello JNI: initializing......
// 多了这个信息需要增加 selinux 权限
01-17 09:52:50.276 265 265 W main : type=1400 audit(0.0:17): avc: denied { read write } for name="hello" dev="tmpfs" ino=11079 scontext=u:r:zygote:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissiv
e=0
01-17 09:52:50.280 265 265 E MtpDeviceJNI: Hello JNI: hello Stub found.
01-17 09:52:50.280 265 265 E Legacy HelloHAL: Hello: hello_device_open name = hello
01-17 09:52:50.281 265 265 E Legacy HelloHAL: Hello: open /dev/hello fail-- Permission denied.
01-17 09:52:50.281 265 265 E MtpDeviceJNI: Hello JNI: failed to open hello device.
2) 添加 shell linux 权限
- 在
system/sepolicy/public/device.te
中添加类别为 hello_device 的对象, 并且设置 attribute 为 dev_type. 修改如下
diff
diff --git a/public/device.te b/public/device.te
index 32563d67c..cc6fc2881 100644
-- a/public/device.te
++ b/public/device.te
@@ -39,6 +39,7 @@ type serial_device, dev_type;
type socket_device, dev_type;
type owntty_device, dev_type, mlstrustedobject;
type tty_device, dev_type;
+type hello_device, dev_type;
type video_device, dev_type;
type zero_device, dev_type, mlstrustedobject;
type fuse_device, dev_type, mlstrustedobject;
修改的文件如下,修改的内容和上面是一样的.:
- 将 /dev/hello 设备节点和 SELinux 类别为 hello_device 的对象进行关联, 即我们前面创建的. 如下所示
diff
zhaosheng@YF-zhaosheng:~/work2/ad500/system/sepolicy$ gf private/file_contexts
diff --git a/private/file_contexts b/private/file_contexts
index a5763bdf4..4475b1598 100755
-- a/private/file_contexts
++ b/private/file_contexts
@@ -102,6 +102,7 @@
/dev/input(/.*)? u:object_r:input_device:s0
/dev/iio:device[0-9]+ u:object_r:iio_device:s0
/dev/ion u:object_r:ion_device:s0
+/dev/hello u:object_r:hello_device:s0
/dev/keychord u:object_r:keychord_device:s0
/dev/loop-control u:object_r:loop_control_device:s0
/dev/modem.* u:object_r:radio_device:s0
/dev/hello:
表示规则适用于/dev/hello
这个设备节点u:object_r:hello_device:s0:
指定 SELinux 上下文,hello_device
是 SELinux 类别,s0
表示 SELinux 安全等级为 0
需要修改的文件如下, 修改的内容和上面是一模一样的.
编译完成后报错如下
cpp
01-18 07:07:17.681 421 421 E AlarmManagerService: HelloServiceInit HelloServiceInit
01-18 07:07:17.681 421 421 E AlarmManagerService: Hello JNI: initializing......
01-18 07:07:17.683 421 421 W system_server: type=1400 audit(0.0:17): avc: denied { read write } for name="hello" dev="tmpfs" ino=3901 scontext=u:r:system_server:s0 tcontext=u:object_r:hello_device:s0 tclass=c
hr_file permissive=0
01-18 07:07:17.683 421 421 E AlarmManagerService: Hello JNI: hello Stub found.
01-18 07:07:17.683 421 421 E Legacy HelloHAL: Hello: hello_device_open name = hello
01-18 07:07:17.683 421 421 E Legacy HelloHAL: Hello: open /dev/hello fail-- Permission denied.
01-18 07:07:17.683 421 421 E AlarmManagerService: Hello JNI: failed to open hello device.
01-18 07:07:17.685 421 421 E UsbAlsaJackDetectorJNI: Can't register UsbAlsaJackDetector na
- 增加 avc 权限
关键信息是这句话缺少 avc 权限, 请注意前面的报错是 tcontext=u:object_r:device
这里是 tcontext=u:object_r:hello_device
说前面的 hello_device 修改已经生效.
cpp
01-18 07:07:17.683 421 421 W system_server: type=1400 audit(0.0:17): avc: denied { read write } for name="hello" dev="tmpfs" ino=3901 scontext=u:r:system_server:s0 tcontext=u:object_r:hello_device:s0 tclass=c
hr_file permissive=0
- 缺少什么权限:
denied { read write }
==> 缺少rw_file_perms
权限 - 那个文件缺少权限:
scontext=u:r:system_server:s0
==>system_server.te
这个文件 - 谁缺少权限:
tcontext=u:object_r:hello_device:s0
==>hello_device
这个对象 - 文件类型:
tclass=chr_file
==>chr_file
字符设备
这里的知识详细请参考: 浅谈SEAndroid安全机制及应用方法
于是在 system/sepolicy/private/system_server.te
增加
diff
diff --git a/private/system_server.te b/private/system_server.te
index 3c1d192d7..d742471b1 100644
-- a/private/system_server.te
++ b/private/system_server.te
@@ -372,6 +372,7 @@ allow system_server video_device:chr_file rw_file_perms;
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;
# write access to ALSA interfaces (/dev/snd/*) needed for MIDI
allow system_server audio_device:chr_file rw_file_perms;
修改的文件如下, 修改的内容和上面是一样的.
修改完成之后再次烧录验证查看 log, 正常发现设备正常打开 hall 层添加成功. 真不容易啊 =-=.
cpp
01-18 07:44:49.688 415 415 E AlarmManagerService: HelloServiceInit HelloServiceInit
01-18 07:44:49.688 415 415 E AlarmManagerService: Hello JNI: initializing......
01-18 07:44:49.690 415 415 E AlarmManagerService: Hello JNI: hello Stub found.
01-18 07:44:49.691 415 415 E Legacy HelloHAL: Hello: hello_device_open name = hello
01-18 07:44:49.691 415 415 E Legacy HelloHAL: Hello: open /dev/hello successfully.
01-18 07:44:49.691 415 415 E AlarmManagerService: Hello JNI: hello device is open.
验证 hall 需要修改的 selinux 相关文件如下. 全部都要改到不要偷懒.
参考文章: