接上一节我们已经将 BMP180 驱动开发完成,现在继续完成 HAL 开发,Android 8 版本新增的 HAL 一般采用 binder 的方式,使用 HIDL 语言进行开发。实际开发除了代码完成,绕不开 SEAndroid。下面分几个步骤介绍。
在 Android 8.0 及更高版本中,较低级别的层已重新编写以采用更加模块化的新架构。搭载 Android 8.0 或更高版本的设备必须支持使用 HIDL 语言编写的 HAL。Android R 也支持使用 AIDL 编写的 HAL。所有 AIDL HAL 均为绑定式。
绑定式 HAL。以 HAL 接口定义语言 (HIDL) 或 Android 接口定义语言 (AIDL) 表示的 HAL。这些 HAL 取代了早期 Android 版本中使用的传统 HAL 和旧版 HAL。在绑定式 HAL 中,Android 框架和 HAL 之间通过 Binder 进程间通信 (IPC) 调用进行通信。所有在推出时即搭载了 Android 8.0 或更高版本的设备都必须只支持绑定式 HAL。
直通式 HAL。以 HIDL 封装的传统 HAL 或旧版 HAL。这些 HAL 封装了现有的 HAL,可在绑定模式和 Same-Process(直通)模式下使用。升级到 Android 8.0 的设备可以使用直通式 HAL。
因为我们是在 Android 8 新增加 BMP180 HAL,因此只能采用绑定式 HAL。下面是整个 BMP180 的 HAL 目录。
首先在 hardware/interfaces 目录下新建 bmp180 目录,接着在 hardware/interfaces/bmp180 目录下新建 1.0 目录,代表 BMP180 1.0 版本 HAL。
接着在 1.0 目录下新建 IBmp180.hal 和 types.hal 文件。
IBmp180.hal
package android.hardware.bmp180@1.0;
interface IBmp180 {
open() generates (bool isSucceed);
close();
getTemperatureAndPressure() generates (BMP180TemperatureAndPressure bmp180TAndP);
};
types.hal
package android.hardware.bmp180@1.0;
struct BMP180TemperatureAndPressure {
uint64_t temperature;
uint64_t pressure;
};
IBmp180 接口中定义了 open、close BMP180 驱动节点的方法,另外也定义了一个获取温度和压力的方法 getTemperatureAndPressure,其返回值是 BMP180TemperatureAndPressure 结构,这个结构定义在 types.hal 文件中。
利用 hidl-gen 工具生成 Android.bp、源代码等文件需要源码至少全部编译过一次。
先来生成顶级目录下的 Android.bp 等。
snake@ubuntu:~/NanoPC-T4/rk3399-android-8.1$ hardware/interfaces/update-makefiles.sh
现在在 hardware/interfaces/bmp180/1.0 目录下新建 default 目录。马上使用 hidl-gen 工具生成接口 cpp 文件。
snake@ubuntu:~/NanoPC-T4/rk3399-android-8.1$ PACKAGE=android.hardware.bmp180@1.0
snake@ubuntu:~/NanoPC-T4/rk3399-android-8.1$ LOC=hardware/interfaces/bmp180/1.0/default
snake@ubuntu:~/NanoPC-T4/rk3399-android-8.1$ hidl-gen -o $LOC -Lc+±impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
snake@ubuntu:~/NanoPC-T4/rk3399-android-8.1$ hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
snake@ubuntu:~/NanoPC-T4/rk3399-android-8.1$ hidl-gen -L hash -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.bmp180@1.0::types
hardware/interfaces/current.txt
......
8e4a9ff11ad75acf15c8919fa1e751ca3c51248e670805e41a507111aa4b88a5 android.hardware.bmp180@1.0::types
现在 hardware/interfaces/bmp180/1.0/default 目录下存在 Bmp180.h、Bmp180.cpp 和 Android.bp 三个文件。由于我们会修改 Bmp180.cpp 文件的内容,因此相应的要把依赖的库手动添加到 Android.bp 中。
Bmp180.h
#ifndef ANDROID_HARDWARE_BMP180_V1_0_BMP180_H
#define ANDROID_HARDWARE_BMP180_V1_0_BMP180_H
#include
#include
#include
namespace android {
namespace hardware {
namespace bmp180 {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
struct Bmp180 : public IBmp180 {
// Methods from IBmp180 follow.
Return<bool> open() override;
Return<void> close() override;
Return<void> getTemperatureAndPressure(getTemperatureAndPressure_cb _hidl_cb) override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" IBmp180* HIDL_FETCH_IBmp180(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace bmp180
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_BMP180_V1_0_BMP180_H
Bmp180.cpp 文件中我们会实现具体方法,这些代码的用法是参照验证驱动编写的应用程序,具体可以参见上一节。
Bmp180.cpp
#define LOG_TAG "Bmp180Hal"
#include
#include
#include
#include
#include
#include
#include "Bmp180.h"
namespace android {
namespace hardware {
namespace bmp180 {
namespace V1_0 {
namespace implementation {
#define BMP_GET_CHIP_ID _IOR('Z', 1, char)
int fd = -1;
uint8_t buf[10];
// Methods from IBmp180 follow.
Return<bool> Bmp180::open() {
fd = ::open("/dev/bmp180", O_RDONLY);
ALOGI("Bmp180::open() fd=%d", fd);
if (-1 == fd) {
ALOGE("open /dev/bmp180 failed\n");
return false;
}
return true;
}
Return<void> Bmp180::close() {
if (-1 != fd) {
ALOGI("Bmp180::close()");
::close(fd);
}
return Void();
}
Return<void> Bmp180::getTemperatureAndPressure(getTemperatureAndPressure_cb _hidl_cb) {
char chip_id = 0;
long temperature = 0;
long pressure = 0;
BMP180TemperatureAndPressure bmp180TemperatureAndPressure;
if (-1 != fd) {
ioctl(fd, BMP_GET_CHIP_ID, &chip_id);
ALOGI("open bmp180 driver succeed, chip ID:[0x%x]\n", chip_id);
if (chip_id == 0x55) {
read(fd, buf, 10);
temperature = *(long *) &buf[0];
pressure = *(long *) &buf[sizeof(long)];
bmp180TemperatureAndPressure.temperature = (uint64_t)temperature;
bmp180TemperatureAndPressure.pressure = (uint64_t)pressure;
}
}
ALOGI("Temperature:[%.1f C],Pressure:[%ld Pa]\n", ((float) bmp180TemperatureAndPressure.temperature) / 10,
bmp180TemperatureAndPressure.pressure);
_hidl_cb(bmp180TemperatureAndPressure);
return Void();
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//IBmp180* HIDL_FETCH_IBmp180(const char* /* name */) {
// return new Bmp180();
//}
} // namespace implementation
} // namespace V1_0
} // namespace bmp180
} // namespace hardware
} // namespace android
这是修改后的 Android.bp 文件,最终版。
Android.bp
cc_library_shared {
name: "android.hardware.bmp180@1.0-impl",
relative_install_path: "hw",
proprietary: true,
srcs: [
"Bmp180.cpp",
],
shared_libs: [
"liblog",
"libcutils",
"libdl",
"libbase",
"libutils",
"libhardware",
] + [
"libhidlbase",
"libhidltransport",
"android.hardware.bmp180@1.0",
],
}
cc_binary {
relative_install_path: "hw",
defaults: ["hidl_defaults"],
vendor: true,
proprietary: true,
name: "android.hardware.bmp180@1.0-service",
init_rc: ["android.hardware.bmp180@1.0-service.rc"],
srcs:
["Bmp180.cpp",
"service.cpp"],
shared_libs: [
"liblog",
"libcutils",
"libdl",
"libbase",
"libutils",
"libhardware",
] + [
"libhidlbase",
"libhidltransport",
"android.hardware.bmp180@1.0",
],
}
这是为了在系统启动的时候就把我们的 HAL 服务拉起来,以便后面 framework、app 访问。
在 hardware/interfaces/bmp180/1.0/default 目录下继续新建 service.cpp 和 android.hardware.bmp180@1.0-service.rc。
service.cpp
#define LOG_TAG "android.hardware.bmp180@1.0-service"
#include
#include
#include
#include
#include "Bmp180.h"
using android::sp;
// libhwbinder:
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
// Generated HIDL filesreadpool;
using android::hardware::bmp180::V1_0::IBmp180;
using android::hardware::bmp180::V1_0::implementation::Bmp180;
int main() {
configureRpcThreadpool(1, true);
sp<IBmp180> bmp180 = new Bmp180();
ALOGI("Bmp180 registerAsService");
auto status = bmp180->registerAsService();
CHECK_EQ(status, android::OK) << "Failed to register bmp180 HAL implementation";
joinRpcThreadpool();
return 0; // joinRpcThreadpool shouldn't exit
}
现在来看 rc 脚本,我们知道这是在 init 进程中进行解析的,最后会把 class 为 hal 的所有 service 拉起来。
android.hardware.bmp180@1.0-service.rc
service bmp180-hal-1-0 /vendor/bin/hw/android.hardware.bmp180@1.0-service
class hal
user system
group system
现在整个 BMP180 HAL 就算开发完成了。编译一下,编译通过的话,就能接着进行下一步的工作。
snake@ubuntu:~/NanoPC-T4/rk3399-android-8.1/hardware/interfaces/bmp180$ mm
没有报错说明没有问题,实际开发总是会遇到问题的,有问题根据实际情况和报错 Log 修改代码,直到没有错误。
将 android.hardware.bmp180 添加到 manifest.xml。追加以下内容到 manifest.xml。
device/rockchip/rk3399/manifest.xml
android.hardware.bmp180
hwbinder
1.0
IBmp180
default
继续添加 android.hardware.bmp180 到 nanopc_t4.mk。
device/rockchip/rk3399/nanopc_t4.mk
PRODUCT_PACKAGES += \
...
android.hardware.bmp180@1.0-impl \
android.hardware.bmp180@1.0-service \
怎么保证你开发的 HAL 代码没有问题,那就需要验证 HAL 代码是否存在问题,需要写一个客户端调用 BMP180 HAL 服务。分层验证是非常必要的,不然上面的架子搭起来以后再来一次验证出了问题也不好排查。
在 hardware/interfaces/bmp180/1.0 目录下新建 test 目录,里面写一些调用 BMP180 HAL 远端服务的代码进行验证。
在 hardware/interfaces/bmp180/1.0/test 目录下新建 bmp180Hal.cpp 和 Android.bp 文件。
bmp180Hal.cpp
#define LOG_TAG "Bmp180HalClient"
#include
#include
using android::hardware::bmp180::V1_0::IBmp180;
using android::hardware::bmp180::V1_0::BMP180TemperatureAndPressure;
using android::sp;
int main() {
sp<IBmp180> service = IBmp180::getService();
if (service == nullptr) {
ALOGE("Can't find IBmp180 service...");
return -1;
}
ALOGD("service->open()...");
service->open();
ALOGI("service->getTemperatureAndPressure()...");
service->getTemperatureAndPressure([&](BMP180TemperatureAndPressure bmp180TemperatureAndPressure){
ALOGI("service->getTemperatureAndPressure()...T=%ld P=%ld",
bmp180TemperatureAndPressure.temperature,
bmp180TemperatureAndPressure.pressure);
});
ALOGI("service->close()...");
service->close();
return 0;
}
Android.bp
cc_binary {
relative_install_path: "hw",
defaults: ["hidl_defaults"],
name: "bmp180Hal",
proprietary: true,
srcs: ["bmp180Hal.cpp"],
shared_libs: [
"liblog",
"libcutils",
"libdl",
"libbase",
"libutils",
"libhardware",
] + [
"libhidlbase",
"libhidltransport",
"android.hardware.bmp180@1.0",
],
}
最后在 hardware/interfaces/bmp180/Android.bp 文件中添加 1.0/test 目录。
Android.bp
// This is an autogenerated file, do not edit.
subdirs = [
"1.0",
"1.0/default",
"1.0/test",
]
现在再来编译一次 BMP180 HAL 代码。
snake@ubuntu:~/NanoPC-T4/rk3399-android-8.1/hardware/interfaces/bmp180$ mm
bmp180Hal 二进制文件这时候也会生成到 /vendor/bin/hw 下。
全编一次代码,烧写镜像到机器进行验证。
运行 ./bmp180Hal 查看 logcat 输出,很不巧 fd = -1。
2020-08-15 17:04:00.455 265-265/? I/Bmp180Hal: Bmp180::open() fd=-1
2020-08-15 17:04:00.455 265-265/? E/Bmp180Hal: open /dev/bmp180 failed
仔细分析一下,dev/bmp180 节点权限仅仅为 crw-------。BMP180 HAL service 跑起来以后实际上我们在 rc 内配置了 user 和 group 都是 system。
既然定位到是权限问题,先手动赋予 777 看看效果。可以正常获取到温度和压力了。
2020-08-15 17:09:07.882 3313-3313/? D/Bmp180HalClient: service->open()...
2020-08-15 17:09:07.885 265-265/? I/Bmp180Hal: Bmp180::open() fd=7
2020-08-15 17:09:07.885 3313-3313/? I/Bmp180HalClient: service->getTemperatureAndPressure()...
2020-08-15 17:09:07.885 265-265/? I/Bmp180Hal: open bmp180 driver succeed, chip ID:[0x55]
2020-08-15 17:09:07.898 265-265/? I/Bmp180Hal: Temperature:[34.1 C],Pressure:[99860 Pa]
2020-08-15 17:09:07.898 3313-3313/? I/Bmp180HalClient: service->getTemperatureAndPressure()...T=341 P=99860
2020-08-15 17:09:07.898 3313-3313/? I/Bmp180HalClient: service->close()...
2020-08-15 17:09:07.898 265-265/? I/Bmp180Hal: Bmp180::close()
现在我们在 system/core/rootdir/ueventd.rc 中配置权限相关。追加一行内容:
/dev/bmp180 0660 root system
到这里整个 HAL 层工作就算完成了。后面继续介绍 Bmp180Service 服务开发、APP 开发和处理 SEAndroid。