目录
一、Android应用访问驱动设备的方法
- App->JNI->Kenerl Driver
- App->JNI->HAL->Kenerl Driver
- App->Service->JNI->HAL->Kenerl Driver
二、为什么需要JNI?
应用使用java编写,驱动一般使用c/cpp编写,提供一种Java访问c/cpp的方法。也就是Java代码可通过JNI接口 调用 C/C++方法。
JNI开发流程的步骤:
- 第1步:编写JNI方法表并注册
- 第2步:实现JNI的.c文件
三、为什么需要Android HAL?
Hardware Abstract Layer 硬件抽象层,由于Linux Kernel需要遵循GPL开源协议,硬件厂商为了保护自己硬件方面的各项参数不被外泄,而一个设备的驱动程序包含了硬件的一些重要参数,所以驱动的开源势必会使硬件厂商蒙受损失,Google为了保护硬件厂商的利益,所以在Android系统中加入了HAL层,在HAL层中不必遵循GPL协议,所以代码可以封闭。
也就是说,编写驱动分为两个部分,一个是HAL层的驱动代码,一个是Kernel层的驱动代码。
内核实现HAL驱动的方法有两种:
- Android_src/hardware/libhardware_legacy 老式的HAL结构(下左图),采用直接调用so动态链接库方式。
- Android_src/hardware/libhardware 新式HAL结构(下右图),采用Stub代理方式调用。
HAL_legacy:旧式的HAL是一个模块,采用共享库形式,在编译时会调用到。由于采用function call形式调用,因此可被多个进程使用,但会被mapping到多个进程空间中,造成浪费,同时需要考虑代码能否安全重入的问题。
HAL:新式的HAL采用HAL module和HAL stub结合形式,HAL stub不是一个share library,编译时上层只拥有访问HAL stub的函数指针,并不需要HAL stub。上层通过HAL module提供的统一接口获取并操作HAL stub,so文件只会被mapping到一个进程,也不存在重复mapping和重入问题。
Android源码中已经实现了一部分的HAL,包括Wi-Fi、GPS、RIL、Sensor等。
了解了Framework Service JNI HAL Driver后,应用APP如何访问设备?
1、在kernel层,编写和添加驱动和编译文件Malefie,创建设备节点文件/dev/led
2、在HAL层,增加模块接口,以访问驱动程序
3、JNI层,编写JNI方法,在应用程序框架层提供Java接口访问硬件
4、framework层,应用程序框架层,增加硬件服务接口
5、通过Manager调用Service
6、添加注册服务,addService开机服务就可以启动
7、更新API
8、修改节点权限
9、编译更新系统镜像到机器
10、android app 测试
10.1 eclipse做法:
- 将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar里的
- android/os/LedManager.class
- 导入eclipse 的sdk/platforms/android-xx/android.jar包中
- 的android/os目录
10.2 Android studio做法:
将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar导入libs目录
四、上层 app 控制 led 亮灭例子
4.1 Kenerl层添加驱动
1、在dts文件中增加 led 设备
path:kernel/arch/arm64/boot/dts/rockchip/rk3399-embeded-port.dtsi
embeded_led {
compatible = "embeded_led";
led_ctr0 = <&gpio2 27 GPIO_ACTIVE_HIGH>;
status = "okay";
};
2、led 驱动(该驱动使用write指令一直写入0)
path:kernel/drivers/leds/led-embeded.c
/* SPDX-License-Identifier: GPL-2.0 */
#include <dt-bindings/gpio/gpio.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/slab.h>
static int My_led_ID;
static ssize_t gpio_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long on = buf[0];
if(!strcmp(attr->attr.name, "embeded_blue_led")){
if(on){
gpio_direction_output(My_led_ID, 1);
printk("gpio_direction_output is %d \n",My_led_ID);
}
else{
gpio_direction_output(My_led_ID, 0);
printk("gpio_direction_output is 0\n");
}
}
return count;
}
static ssize_t gpio_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int tmp = 0;
if(!strcmp(attr->attr.name, "embeded_blue_led")){
tmp = gpio_get_value(My_led_ID);
printk("gpio_get_value is %d \n",tmp);
if(tmp>0)
return strlcpy(buf, "1\n", 3);
else
return strlcpy(buf, "0\n", 3);
}
return 0;
}
/*
DEVICE_ATTR的使用 使用DEVICE_ATTR,可以在sys fs中添加“文件”,
通过修改该文件内容,可以实现在运行过程中动态控制device的目的。
类似的还有DRIVER_ATTR,BUS_ATTR,CLASS_ATTR。这几个东东的区别
就是,DEVICE_ATTR对应的文件在/sys/devices/目录中对应的device下
面。而其他几个分别在driver,bus,class中对应的目录下。这次主要
介绍DEVICE_ATTR,其他几个类似。在documentation/driver-model/Device.txt
中有对DEVICE_ATTR的详细介绍,这儿主要说明使用方法。
先看看DEVICE_ATTR的原型:
DEVICE_ATTR(_name, _mode, _show, _store) _name: 名称,也就是将在sysfs中生成的文件名称。
_mode:上述文件的访问权限,与普通文件相同,UGO的格式。
_show:显示函数,cat该文件时,此函数被调用。
_store:写函数,echo内容到该文件时,此函数被调用。
*/
// S_IRWXU | S_IRWXG 读写权限
static DEVICE_ATTR(embeded_blue_led, S_IRWXU | S_IRWXG, gpio_show,gpio_store);
/*匹配dts文件中的compatible = "my_led_ctrl" */
static struct of_device_id My_led_of_match[] = {
{ .compatible = "embeded_led" },
{ }
};
/*
MODULE_DEVICE_TABLE一般用在热插拔的设备驱动中。
作用是:模块加载系统再加载模块时,就知道了什么模块对应什么硬件设备。
用法是:MODULE_DEVICE_TABLE(设备类型,设备表)
设备类型:包括USB,PCI等,也可以自己起名字。
设备表:也是自己定义的,它的最后一项必须是空,用来标识结束。
*/
MODULE_DEVICE_TABLE(of, My_led_of_match);
static int My_led_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;//获取平台设备节点
enum of_gpio_flags flags;
int ret;
int en_value;
struct kobject * gpio_obj;
printk("embeded led probe\n");
if (!node)
return -ENODEV; //如果设备节点不存在,则返回 -ENODEV(设备不存在)
//从设备树中读取 my_led 的 GPIO 配置编号和标志
//设备树: led_ctr0 = <&gpio1 RK_PB5 GPIO_ACTIVE_LOW>;
My_led_ID = of_get_named_gpio_flags(node, "led_ctr0", 0, &flags);
//标志获取完之后,做一个三目运算,将 1 或者 0 赋值给 en_value
en_value = (flags == GPIO_ACTIVE_HIGH)? 1:0;
//gpio_is_valid 判断该 GPIO 编号是否有效,IO是否合法
if(!gpio_is_valid(My_led_ID)){
dev_err(&pdev->dev, "invalid power gpio%d\n", My_led_ID);
}
//devm_gpio_request 申请占用该 GPIO
ret = devm_gpio_request(&pdev->dev, My_led_ID, "my_gpio_led");
if (ret) {
dev_err(&pdev->dev,"failed to request GPIO%d for my_led \n",My_led_ID);
return -EINVAL;//返回 -EINVAL (模式值无效)
}
//gpio_direction_output 设置输出高还是低电平,根据上面的 三目运算,将 1 或者 0 赋值给 en_value
gpio_direction_output(My_led_ID, en_value);
//create 一个节点
gpio_obj = kobject_create_and_add("embededled", NULL);
//如果是空的,则返回错误
if(gpio_obj == NULL){
printk("kobject_create_and_add error\n");
return -EINVAL;//返回 -EINVAL (模式值无效)
}
ret = sysfs_create_file(gpio_obj,&dev_attr_embeded_blue_led.attr);
if (ret) {
printk("sysfs_create_file error\n");
return ret;
}
return 0;
}
static int My_led_remove(struct platform_device *pdev)
{
printk("embeded led remove");
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int My_led_suspend(struct device *dev)
{
printk("embeded led suspend");
return 0;
}
static int My_led_resume(struct device *dev)
{
printk("embeded led resume");
return 0;
}
#endif
static const struct dev_pm_ops My_led_pm_ops = {
#ifdef CONFIG_PM_SLEEP
.suspend = My_led_suspend,
.resume = My_led_resume,
.poweroff = My_led_suspend,
.restore = My_led_resume,
#endif
};
/* 定义一个平台设备 My_led_driver */
static struct platform_driver My_led_driver = {
.driver = {
.name = "embeded_led",
.owner = THIS_MODULE,
.pm = & My_led_pm_ops,
.of_match_table = of_match_ptr(My_led_of_match),
},
.probe = My_led_probe,
.remove = My_led_remove,
};
module_platform_driver(My_led_driver);
MODULE_DESCRIPTION("power for embeded led gpio Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:embeded LED");
3、Makefile
path:kernel/drivers/leds/Makefile
obj-$(CONFIG_LEDS_EMBEDED) += led-embeded.o
4.Kconfig
path:kernel/drivers/leds/Kconfig
config LEDS_EMBEDED
tristate "LEDS_EMBEDEDSupport for GPIO connected LEDs"
default y
help
Enable this driver will support my_led control
5、defconfig文件中增加led
path:kernel/arch/arm64/configs/embeded_defconfig
CONFIG_LEDS_EMBEDED=y
6、编译下载
FFtools/make.sh -k -j8
下载Resource.img Kernel.img
7、验证
rk33xx_m2g:/ $ dmesg | grep embed
[ 2.091730] embeded led probe
rk33xx_m2g:/ $ su
rk33xx_m2g:/ # echo -e -n "\x01" > sys/embededled/embeded_blue_led
rk33xx_m2g:/ # echo -e -n "\x00" > sys/embededled/embeded_blue_led
8、 修改权限
diff --git a/device/rockchip/rk3399/rk3399_embeded/init.rc b/device/rockchip/rk3399/rk3399_embeded/init.rc
index e1eb2fb3a1..b6aeb966d1 100755
--- a/device/rockchip/rk3399/rk3399_embeded/init.rc
+++ b/device/rockchip/rk3399/rk3399_embeded/init.rc
@@ -560,6 +560,8 @@ on boot
# firefly leds
chmod 0666 /sys/class/leds/firefly\:yellow\:user/brightness
chmod 0666 /sys/class/leds/firefly\:blue\:power/brightness
+ # embeded leds
+ chmod 0666 /sys/embededled/embeded_blue_led
# Assume SMP uses shared cpufreq policy for all CPUs
chown system system /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
每次修改init.rc可以直接编译
make bootimage
./mkimage.sh
然后烧录rockdev/Image-rk3399_embeded/boot.img
4.2 Hal层添加驱动
- mkdir hardware/libhardware/modules/embeded
- path:hardware/libhardware/include/hardware/embededled_hal.h
- path:hardware/libhardware/modules/embeded/embededled_hal.c
如何编写HAL层驱动?
推荐新式HAL结构,基于HAL框架提供了三个结构体,分别为hw_device_t , hw_module_t ,hw_module_methods_t,编写HAL层驱动则是依据这三个结构体作扩展,我们创建自己驱动的device_t,module_t代码,并且写hw_module_methods_t这个结构体中方法的实现代码,最后JNI层通过hw_get_module调用。
#define LOG_TAG "EmbededLed"
#include <hardware/hardware.h>
#include <hardware/embededled_hal.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#define DEVICE_NAME "sys/embededled/embeded_blue_led"
#define MODULE_NAME "EmbededLed"
#define MODULE_AUTHOR "kemp@gmail.com"
/*设备打开和关闭接口*/
static int embededled_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static int embededled_device_close(struct hw_device_t* device);
/*设备访问接口*/
static int embededled_set_val(struct embededled_device_t* dev, int val);
static int embededled_get_val(struct embededled_device_t* dev, int* val);
static int embededled_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {
struct embededled_device_t* dev;dev = (struct embededled_device_t*)malloc(sizeof(struct embededled_device_t));
if(!dev) {
ALOGI("embededled Stub: failed to alloc space");
return -EFAULT;
}
memset(dev, 0, sizeof(struct embededled_device_t));
//初始化设备相关信息,实现访问接口函数
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t*)module;
dev->common.close = embededled_device_close;
dev->set_val = embededled_set_val;
dev->get_val = embededled_get_val;
if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
ALOGI("embededled Stub: failed to open sys/embededled/embeded_blue_led -- %s.", strerror(errno));free(dev);
return -EFAULT;
}
int status = 0;
write(dev->fd, &status, sizeof(status));
*device = &(dev->common);
ALOGI("embededled Stub: open sys/embededled/embeded_blue_led successfully.");
return 0;
}
static int embededled_device_close(struct hw_device_t* device) {
struct embededled_device_t* embededled_device = (struct embededled_device_t*)device;
if(embededled_device) {
close(embededled_device->fd);
free(embededled_device);
}
return 0;
}
static int embededled_set_val(struct embededled_device_t* dev, int val) {
ALOGI("embededled Stub: set value %d to device.", val);
write(dev->fd, &val, sizeof(val));
return 0;
}
static int embededled_get_val(struct embededled_device_t* dev, int* val) {
if(!val) {
ALOGI("embededled Stub: error val pointer");
return -EFAULT;
}
read(dev->fd, val, sizeof(*val));
ALOGI("embededled Stub: get value %d from device", *val);
return 0;
}
/*模块方法表*/
static struct hw_module_methods_t embededled_module_methods = {
open: embededled_device_open
};
/*模块实例变量*/
struct embededled_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: EMBEDEDLED_HARDWARE_MODULE_ID,
name: MODULE_NAME,
author: MODULE_AUTHOR,
methods: &embededled_module_methods,
}
};
led_hal.h
path:hardware/libhardware/include/hardware/embededled_hal.h
#ifndef ANDROID_EMBEDEDLED_INTERFACE_H
#define ANDROID_EMBEDEDLED_INTERFACE_H
#include <hardware/hardware.h>
__BEGIN_DECLS
/*定义模块ID*/
#define EMBEDEDLED_HARDWARE_MODULE_ID "embededled_hal"
/*硬件模块结构体*/
struct embededled_module_t {
struct hw_module_t common;
};
/*硬件接口结构体*/
struct embededled_device_t {
struct hw_device_t common;
int fd;
int (*set_val)(struct embededled_device_t* dev, int val);
int (*get_val)(struct embededled_device_t* dev, int* val);
};
__END_DECLS
#endif
Android.mk
path:hardware/rockchip/embeded/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := embededled_hal.c
LOCAL_MODULE := embededled_hal.default
include $(BUILD_SHARED_LIBRARY)
编译:
source build/envsetup.sh
mmm hardware/libhardware/modules/embeded
out/target/product/rk3399_embeded/obj/lib/embededled_hal.default.so
也可以把模块添加到
hardware/libhardware/modulest/Android.mk中,这样编译整个Android源码时就会自动编译该模块。
4.3 JNI层添加
JNI开发流程的步骤:
- 第1步:编写JNI方法表并注册
- 第2步:实现JNI的.c文件
path:frameworks/base/services/core/jni/com_android_server_EmbededLedService.cpp
#define LOG_TAG "EmbededLedNativeService"
#define LOG_NDEBUG 1
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <stdio.h>
#include <hardware/embededled_hal.h>
namespace android
{
struct embededled_device_t* embededled_device = NULL;
/*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/
static void embededled_setVal(JNIEnv* env, jobject clazz, jint value) {
int val = value;
ALOGD("embededled JNI: set value %d to device.", val);
if(!embededled_device) {
ALOGD("embededled JNI: device is not open.");
return;
}
embededled_device->set_val(embededled_device, val);
}
/*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/
static jint embededled_getVal(JNIEnv* env, jobject clazz) {
int val = 0;
if(!embededled_device) {
ALOGD("embededled JNI: device is not open.");
return val;
}
embededled_device->get_val(embededled_device, &val);
ALOGD("embededled JNI: get value %d from device.", val);
return val;
}
//通过hw_module_t->methods->open调用HAL中embededled_device_open函数
//open成功后返回需要操作的hw_device_t
static inline int embededled_device_open(const hw_module_t* module, struct embededled_device_t** device) {
return module->methods->open(module, EMBEDEDLED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}
//通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件
//hw_device_t->hw_module_t->hw_module_methods_t->open的函数
static jboolean embededled_init(JNIEnv* env, jclass clazz) {
embededled_module_t* module;
ALOGD("embededled JNI: initializing......");
//通过硬件模块ID获取hw_module_t
if(hw_get_module(EMBEDEDLED_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
ALOGD("embededled JNI: embededled Stub found.");
if(embededled_device_open(&(module->common), &embededled_device) == 0) {
ALOGD("embededled JNI: embededled device is open.");
//使用embededled_device调用HAL的set_val进行具体操作
embededled_device->set_val(embededled_device, 1);
return 1;
}
ALOGD("embededled JNI: failed to open embededled device.");
return 0;
}
ALOGD("embededled JNI: failed to get embededled stub module.");
return 0;
}
/*JNI方法表*/
static const JNINativeMethod method_table[] = {
{"init_native", "()Z", (void*)embededled_init},
{"setVal_native", "(I)V", (void*)embededled_setVal},
{"getVal_native", "()I", (void*)embededled_getVal},
};
/*注册JNI方法 EmbededLedService */
int register_android_server_EmbededLedService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/EmbededLedService", method_table, NELEM(method_table));
}
};
Android.mk
diff --git a/frameworks/base/services/core/jni/Android.mk b/frameworks/base/services/core/jni/Android.mk
index 0f0124bd46..305773298a 100644
--- a/frameworks/base/services/core/jni/Android.mk
+++ b/frameworks/base/services/core/jni/Android.mk
@@ -36,6 +36,7 @@ LOCAL_SRC_FILES += \
$(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
$(LOCAL_REL_DIR)/com_android_server_PersistentDataBlockService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_EmbededLedService.cpp \
$(LOCAL_REL_DIR)/onload.cpp
LOCAL_SRC_FILES += \
Onload.cpp
把注册JNI方法函数添加到系统中
diff --git a/frameworks/base/services/core/jni/onload.cpp b/frameworks/base/services/core/jni/onload.cpp
index d5861f8c41..b52f7917fd 100644
--- a/frameworks/base/services/core/jni/onload.cpp
+++ b/frameworks/base/services/core/jni/onload.cpp
@@ -47,6 +47,7 @@ int register_android_server_PersistentDataBlockService(JNIEnv* env);
int register_android_server_Watchdog(JNIEnv* env);
int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
int register_com_android_server_rkdisplay_RkDisplayModes(JNIEnv* env);
+int register_android_server_EmbededLedService(JNIEnv* env);
};
using namespace android;
@@ -89,7 +90,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_Watchdog(env);
register_android_server_HardwarePropertiesManagerService(env);
register_com_android_server_rkdisplay_RkDisplayModes(env);
-
-
+ register_android_server_EmbededLedService(env);
return JNI_VERSION_1_4;
}
编译
mmm frameworks/base/services/
4.4 AIDL&Service添加
AIDL
frameworks/base/core/java/android/os/IEmbededLedService.aidl
package android.os;
interface IEmbededLedService {
void setVal(int val);
int getVal();
}
生成Stub ,添加下面mk文件内容后,执行mmm frameworks/base生成接口
diff --git a/frameworks/base/Android.mk b/frameworks/base/Android.mk
index b9692de0e1..c426a3cd99 100755
--- a/frameworks/base/Android.mk
+++ b/frameworks/base/Android.mk
@@ -240,6 +240,7 @@ LOCAL_SRC_FILES += \
core/java/android/os/IUpdateLock.aidl \
core/java/android/os/IUserManager.aidl \
core/java/android/os/IVibratorService.aidl \
+ core/java/android/os/IEmbededLedService.aidl \
core/java/android/os/IDisplayDeviceManagementService.aidl \
core/java/android/os/IRkDisplayDeviceManagementService.aidl \
core/java/android/security/IKeystoreService.aidl \
Service
frameworks/base/services/java/com/android/server/EmbededLedService.java
package com.android.server;
import android.content.Context;
import android.os.IEmbededLedService;
import android.util.Slog;
public class EmbededLedService extends IEmbededLedService.Stub {
private static final String TAG = "EmbededLedService";
EmbededLedService() {
boolean status = init_native();
Slog.i(TAG,"EmbededLedService Stub init"+status);
}
public void setVal(int val) {
setVal_native(val);
}
public int getVal() {
return getVal_native();
}
//JNI方法
private static native boolean init_native();
private static native void setVal_native(int val);
private static native int getVal_native();
};
添加Service到System启动
diff --git a/frameworks/base/services/java/com/android/server/SystemServer.java b/frameworks/base/services/java/com/android/server/SystemServer.java
index cc6f1850e6..b22ecda734 100644
--- a/frameworks/base/services/java/com/android/server/SystemServer.java
+++ b/frameworks/base/services/java/com/android/server/SystemServer.java
@@ -1086,6 +1086,15 @@ public final class SystemServer {
} catch (Throwable e) {
reportWtf("starting DiskStats Service", e);
}
+
+ try {
+ Slog.i(TAG, "Embededled Service");
+ ServiceManager.addService("embededled", new EmbededLedService());
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting Embededled Service", e);
+ }
+
+
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
if (!disableSamplingProfiler) {
编译&烧写
mmm frameworks/base/services/编译生成libandroid_servers.so
make snod打包后烧写system.img
验证:
通过终端查看服务是否启动成功
rk3399_embeded:/ $ service list | grep embed
29 embededled: [android.os.IEmbededLedService]
4.5 App层调用自定义service
/packages/experimental/embededled
.
├── AndroidManifest.xml
├── Android.mk
├── res
│ ├── drawable
│ │ └── ic_launcher_background.xml
│ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ ├── layout
│ │ └── activity_main.xml
│ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher_round.xml
│ │ └── ic_launcher.xml
│ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ └── strings.xml
└── src
└── com
└── embeded
└── testapplauncher
└── MainActivity.java
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.embeded.testapplauncher">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Android.mk
# This Android.mk is empty, and >> does not build subdirectories <<.
# Individual packages in experimental/ must be built directly with "mmm".
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := embededled
include $(BUILD_PACKAGE)
MainActivity.java
package com.embeded.testapplauncher;
import com.embeded.testapplauncher.R;
import android.app.Activity;
import android.os.ServiceManager;
import android.os.Bundle;
import android.os.IEmbededLedService;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
//import com.embeded.testapplauncher.LedNative;
public class MainActivity extends Activity implements OnClickListener{
private static final String LOG_TAG = "Kemp";
private IEmbededLedService embededLedService = null;
//LedNative lednative;
private Button onButton = null;
private Button offButton = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
embededLedService = IEmbededLedService.Stub.asInterface(
ServiceManager.getService("embededled"));
if(null == embededLedService){
Log.i(LOG_TAG, "can not find service");
}
onButton = (Button)findViewById(R.id.button_on);
offButton = (Button)findViewById(R.id.button_off);
onButton.setOnClickListener(this);
offButton.setOnClickListener(this);
Log.i(LOG_TAG, "Hello Activity Created");
//lednative = new LedNative();
//lednative.openDev();
}
@Override
public void onClick(View v) {
if(v.equals(onButton)) {
try {
embededLedService.setVal(1);
Log.i(LOG_TAG, "embeded Open led ");
} catch (RemoteException e) {
Log.e(LOG_TAG, "Remote Exception while reading value from device.");
}
}
else if(v.equals(offButton)) {
try {
embededLedService.setVal(0);
Log.i(LOG_TAG, "embeded Close led");
} catch (RemoteException e) {
Log.e(LOG_TAG, "Remote Exception while writing value to device.");
}
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<Button
android:id="@+id/button_on"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/LedOn">
</Button>
<Button
android:id="@+id/button_off"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/LedOff">
</Button>
</LinearLayout>
</LinearLayout>
strings.xml
<resources>
<string name="app_name">EmbededApp</string>
<string name="value">Value</string>
<string name="hint">Please input a value...</string>
<string name="LedOn"> LedOn </string>
<string name="LedOff"> LedOff </string>
<string name="clear">Clear</string>
</resources>
编译&烧录
mmm packages/experimental/embededled/ 此步骤会生成apk文件
make snod
sudo cp out/target/product/rk3399_embeded/system.img /media/sf_ubuntuShare/img3399
烧录system.img
4.6 验证
板卡启动时,Android Studio打印以下日志
2013-01-18 22:51:19.641 511-511/system_process I/SystemServer: Embededled Service
2013-01-18 22:51:19.641 511-511/system_process D/EmbededLedNativeService: embededled JNI: initializing......
2013-01-18 22:51:19.642 511-511/system_process D/EmbededLedNativeService: embededled JNI: embededled Stub found.
2013-01-18 22:51:19.643 511-511/system_process I/EmbededLed: embededled Stub: open sys/embededled/embeded_blue_led successfully.
2013-01-18 22:51:19.643 511-511/system_process D/EmbededLedNativeService: embededled JNI: embededled device is open.
2013-01-18 22:51:19.643 511-511/system_process I/EmbededLed: embededled Stub: set value 1 to device.
2013-01-18 22:51:19.643 511-511/system_process I/EmbededLedService: EmbededLedService Stub inittrue
2013-01-18 22:51:19.643 511-511/system_process I/SystemServer: IEmbededService
2013-01-18 22:51:19.644 511-511/system_process I/EmbededService: EmbededService init
打开App操作时,可以正常操作灯开关
4.7 常见问题
问题1:
解决办法:权限问题 把权限添加到init.rc,根据3.1-8编译烧写
问题2:
解决办法:Java开放类接口问题 make update-api
问题3:
D/EMBEDEDLEDNativeService: embededled JNI: initializing......
D/EMBEDEDLEDNativeService: embededled JNI: failed to get embededled stub module.
解决办法:Hal so库未编译,重新编译3.2模块,再烧录
4.8 总结
APP调用了LedManager里的LedOn LedOff
LedManager服务通过aidl调用到LedService.java里的setOn setOff
LedService通过JNI提供的接口调用开关函数
jni层调用到HAL层里的开关函数
led_on/led_off通过write/ioctl调用到驱动调置gpio的高低电平
五、补丁包使用
补丁包下载
Docshttps://x509p6c8to.feishu.cn/docs/doccnWKSIWI5uYsLtbi6g3Mb5fd#补丁包使用
- 检查修改了哪些文件
git apply --stat 0001-add-hal-framwork-service-interface.patch
- 检查打补丁是否存在错误
git apply --check 0001-add-hal-framwork-service-interface.patch
- 执行打补丁指令
git apply 0001-add-hal-framwork-service-interface.patch
- 编译Kernel
./FFTools/make.sh -k -j8
- 编译HAL
source build/envsetup.sh
mmm hardware/libhardware/modules/embeded
输出位于out/target/product/rk3399_embeded/obj/lib/embededled_hal.default.so
- 编译Framework
mmm frameworks/base/
- 编译APP
mmm packages/experimental/embededled/ 此步骤会生成apk文件
make snod
打包后记得把out/target/product/rk3399_embeded/system.img复制到烧录路径
- 烧录固件
- 重启后打开ADB setprop persist.usb.mode peripheral
- 查看对应文件、服务、APP是否生成
- 运行APP