本文我们要讲的是在用 i.MX8 平台开发时,Android APP 如何进行访问硬件驱动。
主要内容包括:框架代码编写和注意事项(Linux Driver、C Library、SystemServer、APP 等五个部分)、Sepolicy 信息配置,话不多说,我们直接进入正题吧~
一、Android 应用层访问硬件服务框架
Android 应用层访问硬件服务框架主要可以分为:① APP;② SystemServer;③ C Library;④ Linux Driver。
① APP:
从 service_manager 里获取相关服务,再通过接口调用,接口里实现对本地方法的调用。
涉及到的相关文件:MainActivity.java、activity_main.xml、ILedService.aidl、LedService.java
② SystemServer:
通过注册 android 服务的方式加载 C 库,再将服务加入 service_manager 里面。
涉及到的相关文件:SystemServer.java、SystemServiceRegistry.java、LedManager.java
③ C Library:
HAL 层操作 /dev/myled,JNI 层加载 HAL 文件和向上注册 java 本地方法。
涉及到的相关文件:onload.cpp、com_android_server_LedService.cpp、led_hal.c、led_hal.h
④ Linux Driver
和 Linux 驱动完全一样,编写驱动,向上提供 /dev/myled 节点。
涉及到的相关文件:myled.c
二、框架代码编写
2.1 编写 Linux Driver
通过 Linux Driver 来实现从应用层到底层的控制,这里主要实现对 LED 灯的 ON/OFF 控制及 GPIO 引脚电平 HIGH/LOW 的上报。
在 vendor/nxp-opensource/kernel_imx/drivers/leds 目录下创建 myled.c
#include < linux/module.h >
#include < linux/kernel.h >
#include < linux/fs.h >
#include < linux/init.h >
#include < linux/delay.h >
#include < asm/uaccess.h >
#include < asm/irq.h >
#include < asm/io.h >
#include < linux/device.h >
#include < linux/uaccess.h >
#include < linux/platform_device.h >
#include < linux/cdev.h >
#include < linux/slab.h > /*kmalloc*/
#include < linux/vmalloc.h > /*vmalloc*/
#include < linux/types.h > /*ssize_t*/
#include < linux/gpio_keys.h >
#include < linux/gpio.h >
#include < linux/interrupt.h >
#include < linux/sched.h >
#include < asm-generic/ioctl.h >
#include < asm-generic/errno-base.h >
#define USER_LED 374 /*GPIO LED GPIO4_22*/
#define USER_BUTTON 357 /*GPIO LED GPIO4_05*/
int major;
static struct class *myled_class;
static struct class_device *myled_class_devs;
static int myled_drv_open(struct inode *inode, struct file *file)
{
printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
return 0;
}
static long myled_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int read_val;
printk("gpio_ioctln");
switch(cmd) {
case 0:
if(arg == 0)
{
gpio_set_value(USER_LED, 0);
printk("led is offn");
break;
}else
{
gpio_set_value(USER_LED, 1);
printk("led is onn");
break;
}
case 1:
read_val = gpio_get_value(USER_BUTTON);
if(copy_to_user((int __user*)arg,&read_val,sizeof(read_val)))
return ENOTTY;
printk("gpio input: %dn", read_val);
break;
default:
return -EINVAL;
}
return 0;
}
static struct file_operations myled_drv_fops = {
.owner = THIS_MODULE,
.open = myled_drv_open,
.unlocked_ioctl = myled_drv_ioctl,
};
static int myled_probe(struct platform_device *pdev)
{
int ret;
printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
ret = gpio_request(USER_LED, "LED");//第一个参数,为要申请的引脚,第二个为你要定义的名字
if (ret)
{
return ret;
}
printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
gpio_direction_output(USER_LED, 1);
printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
gpio_set_value(USER_LED, 1);
printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
major = register_chrdev(0,"myled",&myled_drv_fops);
//创建设备信息,执行后会出现 /sys/class/myled
myled_class = class_create(THIS_MODULE, "myled");
//创建设备节点,就是根据上面的设备信息来的
myled_class_devs = device_create(myled_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */
return 0;
}
static int myled_remove(struct platform_device *pdev)
{
gpio_free(USER_LED);
unregister_chrdev(major,"myled");
device_unregister(myled_class_devs);
class_destroy(myled_class);
return 0;
}
static const struct of_device_id myled_ids[] = {
{ .compatible = "fsl,myled", },
{ },
};
MODULE_DEVICE_TABLE(of, myled_ids);
/*1. 构建platform_driver 结构体*/
static struct platform_driver myled_driver={
.probe = myled_probe,
.remove = myled_remove,
.driver = {
.name = "myled",
.of_match_table = myled_ids,
}
};
static int __init myled_init(void)
{
/*2. 注册平台驱动*/
platform_driver_register(&myled_driver);
printk("[pual] Enter %s - %d -- n",__func__,__LINE__);
return 0;
}
static void __exit myled_exit(void)
{
/*3. 注销平台驱动*/
platform_driver_unregister(&myled_driver);
}
module_init(myled_init);
module_exit(myled_exit);
/* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR("WPI Pual Lin");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("i.MX8 LED Driver");
MODULE_LICENSE("GPL");
修改 vendor/nxp-opensource/kernel_imx/drivers/leds 目录下 Makefile 及 Kconfig,添加代码
Makefile
obj-$(CONFIG_MYLED) += myled.o
Kconfig
config MYLED
tristate "MYLED support"
depends on LEDS_CLASS
help
This option enables support for userspace LEDs. Say 'y' to enable this
support in kernel. To compile this driver as a module, choose 'm' here:
the module will be called uleds.
修改默认配置文件 vendor/nxp-opensource/kernel_imx/arch/arm64/configs/android_defconfig,添加代码
CONFIG_MYLED=y
由于这里给的值是 y,会把我们的 driver 编译进内核,不会生成 ko 文件,也不需要我们去手动挂载。注意,必须是给 y,因为我们后续会把 led 添加进 system server 里面,会在系统启动的时候就调用到 open 来打开 /dev/myled。
执行如下命令,开始编译 kernel 内核
source build/envsetup.sh
lunch mek_8q-eng
make bootimage -j16
2.2 编写 C Library
2.2.1 修改 onload.cpp
SystemServer.java 会加载 C 库,调用到 onload.cpp,需要在 onload.cpp 注册 LED 服务。
修改 frameworks/base/services/core/jni/onload.cpp,添加:
int register_android_server_LedService(JNIEnv* env);
……
register_android_server_LedService(env);
2.2.2 创建 com_android_server_LedService.cpp
前面用到了 register_android_server_LedService(),是在 com_android_server_LedService.cpp 里实现的。
com_android_server_LedService.cpp 理论上可以直接操作节点 /dev/myled,但一般不这样做。
通常的做法是,向上提供本地方法 (native_ledOpen),向下加载 HAL 文件 (led_hal.c),并调用 HAL 的函数。
这样操作有两个好处:
- 方便修改;
如果需要修改硬件部分的操作,只需要修改 led_hal.c,生成 so 文件,放入系统即可,而不需要编译整个 Android 系统;
- 保密代码;
因为 Linux 的 GPL 协议,一旦使用的内核代码,自己的代码也得开源出去,硬件厂商为了保密硬件的具体细节,只在内核实现操作寄存器的接口,具体的操作逻辑放在 HAL 文件里,而 Android 采用 Apache 协议,修改了代码而无需开源,这样就实现了保密代码;
编写步骤如下:
- 定义 JNINativeMethod,建立 Java 本地方法与 C 库函数名的映射关系;
- 使用 jniRegisterNativeMethods 注册本地方法,将在 onload.cpp 被调用;
- 在 open() 里:
① 使用 hw_get_module 获得 hw_module_t 结构体;
② 使用 module->methods->open 获得 hw_device_t 结构体;
③ 将 hw_device_t 转换为 led_device_t,调用对应 open;
- 完成其它函数 ctrl、close 的调用;
在 frameworks/base/services/core/jni/ 路径下创建 com_android_server_LedService.cpp
/*/frameworks/base/services/core/jni/*/
#define LOG_TAG "LedService"
#include "jni.h"
#include < nativehelper/JNIHelp.h >
#include "android_runtime/AndroidRuntime.h"
#include < utils/misc.h >
#include < utils/Log.h >
#include < stdio.h >
#include < sys/types.h >
#include < sys/stat.h >
#include < fcntl.h >
#include < sys/ioctl.h >
#include < hardware/led_hal.h >
namespace android
{
static led_device_t* led_device;
jint ledOpen(JNIEnv *env, jobject cls)
{
jint err;
hw_module_t* module;
hw_device_t* device;
ALOGI("[pual] native ledOpen");
//1. hw_get_module for get module
err = hw_get_module("led", (hw_module_t const**)&module);
ALOGE("[pual] native ledOpen");
if (err == 0) {
//2. module- >methods- >open for get device
err = module- >methods- >open(module, NULL, &device);
if (err == 0) {
//3. conversion, call led_open
led_device = (led_device_t *)device;
return led_device- >led_open(led_device);
} else {
return -1;
}
}
return -1;
}
void ledClose(JNIEnv *env, jobject cls)
{
ALOGI("[pual] nativeled Close");
return;
}
jint ledCtrl(JNIEnv *env, jobject cls, jint number, jint status)
{
ALOGI("[pual] native ledCtrl %d, %d", number, status);
return led_device- >led_ctrl(led_device, number, status);
}
static const JNINativeMethod method_table[] = {
{"native_ledOpen", "()I", (void *)ledOpen},
{"native_ledClose", "()V", (void *)ledClose},
{"native_ledCtrl", "(II)I", (void *)ledCtrl},
};
int register_android_server_LedService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/LedService",
method_table, NELEM(method_table));
}
};
修改 frameworks/base/services/core/jni/Android.bp,添加
"com_android_server_LedService.cpp",
执行 mmm frameworks/base/services 编译 com_android_server_LedService.cpp 和 onload.cpp
2.2.3 创建 HAL 文件:led_hal.c 和 led_hal.h
在 hardware/libhardware/modules 目录下新建一个目录 test_led
在 hardware/libhardware/modules/test_led 目录下创建 led_hal.c
#include < hardware/hardware.h >
#include < cutils/log.h >
#include < stdio.h >
#include < unistd.h >
#include < fcntl.h >
#include < errno.h >
#include < stdlib.h >
#include < sys/types.h >
#include < sys/stat.h >
#include < fcntl.h >
#include < sys/ioctl.h >
#include < utils/Log.h >
#include < hardware/led_hal.h >
//#define LOG_TAG "LED_HAL"
static int fd;
static int led_open(struct led_device_t* dev __unused)
{
fd = open("/dev/myled", O_RDWR);
ALOGI("led_open : %d", fd);
if (fd >= 0)
return 0;
else
return -1;
}
static int led_ctrl(struct led_device_t* dev __unused, int number, int status)
{
int read_val,ret = 0;
switch(number)
{
case 0:
ret = ioctl(fd, 0, status);
ALOGI("[pual] led_write : status = %d, ret = %d", status, ret);
return ret;
case 1:
ioctl(fd,1,&read_val);
ALOGI("[pual] led_read : read_val = %d", read_val);
return read_val;
default:
return -1;
}
}
static int led_close(struct hw_device_t* device __unused)
{
close(fd);
ALOGI("led_close : %d", fd);
return 0;
}
static struct led_device_t myled_dev = {
.common = {
.tag = HARDWARE_DEVICE_TAG,
.close = led_close,
},
.led_open = led_open,
.led_ctrl = led_ctrl,
};
static int led_device_open(const struct hw_module_t* module __unused, const char* id __unused,
struct hw_device_t** device)
{
//return by id
*device = (hw_device_t*) &myled_dev;
return 0;
}
static struct hw_module_methods_t led_module_methods = {
.open = led_device_open,
};
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.id = "led",
.methods = &led_module_methods,
};
在目录 hardware/libhardware/include/hardware 下创建 led_hal.h
#ifndef ANDROID_LED_INTERFACE_H
#define ANDROID_LED_INTERFACE_H
#include < stdint.h >
#include < sys/cdefs.h >
#include < sys/types.h >
#include < hardware/hardware.h >
__BEGIN_DECLS
struct led_device_t {
struct hw_device_t common;
int (*led_open) (struct led_device_t* dev);
int (*led_ctrl) (struct led_device_t* dev, int number, int status);
};
__END_DECLS
#endif
在 hardware/libhardware/modules/test_led 目录下创建 Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := led.default
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := hardware/libhardware
LOCAL_SRC_FILES := led_hal.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := eng
include $(BUILD_SHARED_LIBRARY)
执行 mmm hardware/libhardware/modules/test_led 编译 led_hal.c
编译完成后会在 out/target/product/mek_8q/obj_arm/SHARED_LIBRARIES/led.default_intermediates 目录下生成 so 文件 led.default.so
2.3 编写 SystemServer
2.3.1 修改 SystemServer.java
SystemServer.java 主要做两件事,一是加载 C 库,二是使用 addService 将 LED 服务加入 service_manager 里面。
加载 C 库这个是调用 onload.cpp,这个前面已经修改 onload.cpp,这里就不需要改了。
修改frameworks/base/services/java/com/android/server/SystemServer.java,添加:
traceBeginAndSlog("StartLedService");
ServiceManager.addService("led", new LedService());
traceEnd();
2.3.2 创建 LedManager.java
在目录 frameworks/base/core/java/android/app 下,创建 LedManager.java
package android.app;
import android.content.Context;
import android.os.ILedService;
import android.os.RemoteException;
public class LedManager {
ILedService mService;
public LedManager(Context ctx,ILedService service){
mService = service;
}
public void ledCtrl(int number, int status){
try {
mService.ledCtrl(number, status);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
2.3.3 修改 SystemServiceRegistry.java
在 SystemServiceRegistry.java 中注册我们的 LedManager Service 服务。
修改 frameworks/base/core/java/android/app/ SystemServiceRegistry.java,在 static 块中添加
registerService(Context.LED_SERVICE, LedManager.class,
new CachedServiceFetcher() {
@Override
public LedManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.LED_SERVICE);
ILedService service = ILedService.Stub.asInterface(b);
return new LedManager(ctx, service);
}});
这里面的 LED_SERVIC,需要在frameworks/base/core/java/android/content/Context.java 中声明
@StringDef(suffix = { "_SERVICE" }, value = {
LED_SERVICE,
…
})
…
public static final String LED_SERVICE="led";
2.4 编写 APP
2.4.1 创建 AIDL
AIDL(Android Interface Definition Language),即 Android 接口定义语言,顾名思义就是定义接口,提供给 APP 使用。
frameworks/base/core/java/android/os/ 目录下新建:ILedService.aidl :
package android.os;
interface ILedService {
int ledCtrl(int number, int status);
}
frameworks/base 目录下修改:Android.bp,添加:
"core/java/android/os/ILedService.aidl",
在 android_build 目录下执行编译
mmm frameworks/base/
编译完成后将会自动生成:
out/soong/.intermediates/frameworks/base/framework/android_common/gen/aidl/frameworks/base/core/java/android/os/ILedService.java。
2.4.2 创建 LedService.java
前面生成了 ILedService.java,需要实现 ILedService 接口的成员函数。
在 frameworks/base/services/core/java/com/android/server/ 路径下,创建 LedService.java
/*frameworks/base/services/core/java/com/android/server/*/
package com.android.server;
import android.os.ILedService;
import android.util.Slog;
public class LedService extends ILedService.Stub {
private static final String TAG = "ILedService";
//Call native c function to access hardware
public int ledCtrl(int number, int status) throws android.os.RemoteException
{
Slog.i(TAG, "[pual] enter ledCtrl.");
return native_ledCtrl(number, status);
}
public LedService() {
Slog.i(TAG, "[pual] enter native_ledOpen.");
native_ledOpen();
}
//Function declaration
public static native int native_ledCtrl(int number, int status);
public static native int native_ledOpen();
public static native void native_ledClose();
}
可以看到 LedService.java 继承于 ILedService,并调用本地方法实现了成员函数。并在构造函数里调用 native_ledOpen()。
其 Android.mk 自动包含了所有 java 文件,不需要修改 Android.mk。
执行完以上步骤后就可以执行 make -j16 命令开始编译
2.4.3 创建 APP 工程
① 打开 Android Studio,选择 “Start a new Android Studio project” 创建一个新 APP 工程;
② 然后选择 “Empty Activity” 空主界面,点击下一步;
③ 最后设置 APP 信息、保存路径、选择语言、兼容API版本、勾选支持安装 apps,点击完成。
等待自动创建完成后,会自动生成工程文件,生成的工程文件中会包含MainActivity.java 和 activity_main.xml,appsrcmainreslayoutactivity_main.xml,界面控件布局文件,既可通过图形界面设计控件,也可直接编辑 xml;
appsrcmainjavacomexampleled_demoMainActivity.java,实现控件各种具体功能,逻辑关系。
MainActivity.java
package com.example.led_demo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.view.View;
import android.os.ILedService;
import android.os.ServiceManager;
//import static android.os.ServiceManager.getService;
public class MainActivity extends AppCompatActivity {
private boolean ledStatus = false;
private int gpiostatus;
private final String TAG="MainActivity";
private Button button1,button2 = null;
private ILedService iLedService = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG,"[pual] AppService onCreate");
initView();
}
private void initView() {
iLedService =ILedService.Stub.asInterface(ServiceManager.getService("led"));
if(iLedService==null)
Log.e(TAG,"iLedService = null");
else
Log.e(TAG,"iLedService not null");
button1 = (Button) findViewById(R.id.BUTTON1);
button1.setOnClickListener(new MyButtonListener1());
button2 = (Button) findViewById(R.id.BUTTON2);
button2.setOnClickListener(new MyButtonListener2());
}
class MyButtonListener1 implements View.OnClickListener {
@Override
public void onClick(View v) {
ledStatus = !ledStatus;
if (ledStatus) {
button1.setText("LED OFF");
try {
iLedService.ledCtrl(0, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
button1.setText("LED ON");
try {
iLedService.ledCtrl(0, 1);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
class MyButtonListener2 implements View.OnClickListener {
@Override
public void onClick(View v) {
try {
gpiostatus = iLedService.ledCtrl(1, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
if (gpiostatus == 1) {
button2.setText("HIGH");
} else {
button2.setText("LOW");
}
}
}
}
activity_main.xml
< ?xml version="1.0" encoding="utf-8"? >
< androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
< LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
< TextView
android:id="@+id/TEXT"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="30dp"
android:text="Android access hardware"
android:textColor="#008577"
android:textSize="25sp"
android:textStyle="bold" / >
< Button
android:id="@+id/BUTTON1"
android:layout_width="50pt"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_gravity="center"
android:text="LED ON" / >
< Button
android:id="@+id/BUTTON2"
android:layout_width="50pt"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_gravity="center"
android:text="HIGH" / >
< /LinearLayout >
< /androidx.constraintlayout.widget.ConstraintLayout >
由于这里会调用到我们前面定义好的 ledctrl 函数接口,但是我们还没有把对应的库导进来,需要执行如下操作:
将 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar 放在 Windows 里。
在 Android Studio 里,选择 File->Project Structure,此时弹出配置界面,点击 Modules 窗口左上角的 +,选择 Import .JAR/.AAR Package,选择刚才的 classes.jar。
切换到 Dependencies 选项卡,选择 app,点击 Declared Dependencies 窗口左上角的 + 号,选择 3 Module dependency,在弹出的界面选择刚才添加的 classes,最后点击 OK。
以上操作后,会在 APP 工程根目录下生成 classes 文件夹,里面就包含了 classes.jar。
完成后可以点击 Build -> Build Bundle(s) / APK(s) -> Build APK(s) 开始编译
2.4.4 APK 安装
上面编译生成的 APK 名称为 :app-debug.apk,我们需要连接好开发板的 USB TypeC 接口,在 Windows 终端输入如下命令开始安装:
adb install app-debug.apk
安装完成后,打开 APK 即可看到如下界面:
三、Sepolicy 权限配置
3.1 denied error
在完成各层代码添加后,还需要添加 Sepolicy 权限,否则系统在启动的时候回出现类似如下报错
avc: denied { find } for service=led pid=4363 uid=10067 scontext=u:r:untrusted_app:s0:c67,c256,c512,c768 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=0
avc: denied { module_load } for pid=4566 comm="insmod" path="/data/pual/myled.ko" dev="mmcblk0p12" ino=221 scontext=u:r:su:s0 tcontext=u:object_r:system_data_file:s0 tclass=system permissive=1
我们需要在 device/fsl/imx8q/sepolicy/system_server.te 中添加:
allow system_server default_android_service:service_manager { add };
allow untrusted_app default_android_service:service_manager { find };
allow su system_data_file:system { module_load };
添加完后编译,出现了如下报错:
libsepol.report_failure: neverallow on line 1314 of system/sepolicy/public/domain.te (or line 10945 of policy.conf) violated by allow su system_data_file:system { module_load };
修改以下目录中的 domain.te,
system/sepolicy/public/domain.te
system/sepolicy/prebuilts/api/28.0/public/domain.te
将 1314 行代码注释如下:
#neverallow * ~{ system_file vendor_file rootfs }:system module_load;
3.2 open error
虽然前面我们已经将 myled 添加到系统启动的进程中,并且也可以在 /dev/ 目录下查找到了我们的 myled 设备,但是查看启动 logcat 仍然出现 open error 的现象。出现这样现象的原因主要是跟 system server 没有权限去操作我们的 myled 设备有关,解决权限的问题需要分两步走:
- myled 设备的权限
打开目录文件 android_build/system/core/rootdir/ueventd.rc ,添加如下内容:
/dev/myled 0666 root root
- system server 访问 myled 设备的权限
这里主要涉及的是 Android 的 Sepolicy 安全机制,需要修改 android_build/device/fsl/imx8q/sepolicy 目录下的三个文件:file_contexts、device.te、system_server.te
① file_contexts
仿照这个文件里面的写法,添加一个自定义的设备名字,myled_device 为自定义,其他内容保持一致:
/dev/myled u:object_r:myled_device:s0
② device.te
仿照这个文件里的写法,将刚刚上一步写的 myled_device 声明为 dev_type:
type myled_device, dev_type;
③ system_server.te
加入允许 system_server 对 /dev/myled 的读写权限:
allow system_server myled_device:chr_file rw_file_perms;
chr_file表示字符设备文件,如果是普通文件用file,目录请用dir
rw_file_perms代表读写权限
完成以上步骤后,执行 make -j16 开始编译
编译完成后会在 out/target/product/mek_8q 目录下生成镜像文件
四、注意事项
① 如果把 LED_SERVICE 添加到 system server 成功的话,系统启动完成可以通过 adb shell service list 可以看到 led service,如果没有则说明没有添加成功。
60 window: [android.view.IWindowManager]
61 alarm: [android.app.IAlarmManager]
62 consumer_ir: [android.hardware.IConsumerIrService]
63 led: [android.os.ILedService]
64 vibrator: [android.os.IVibratorService]
65 content: [android.content.IContentService]
② 如果 so 文件没有正确添加(HAL 文件没有编译),会出现如下 log
--------- beginning of crash
09-04 08:42:57.134 3404 3792 F libc : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x80 in tid 3792 (Binder:3404_7), pid 3404 (system_server)
09-04 08:42:57.247 4652 4652 I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstone
09-04 08:42:57.248 3299 3299 I /system/bin/tombstoned: received crash request for pid 3792
09-04 08:42:57.249 4652 4652 I crash_dump64: performing dump of process 3404 (target tid = 3792)
09-04 08:42:57.265 4652 4652 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
09-04 08:42:57.265 4652 4652 F DEBUG : Build fingerprint: 'Android/mek_8q/mek_8q:9/2.0.0-ga-rc4/pual09030609:eng/dev-keys'
09-04 08:42:57.265 4652 4652 F DEBUG : Revision: '0'
09-04 08:42:57.265 4652 4652 F DEBUG : ABI: 'arm64'
09-04 08:42:57.265 4652 4652 F DEBUG : pid: 3404, tid: 3792, name: Binder:3404_7 >> > system_server < < < 09-04 08:42:57.265 4652 4652 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x80
09-04 08:42:57.265 4652 4652 F DEBUG : Cause: null pointer dereference
09-04 08:42:57.265 4652 4652 F DEBUG : x0 0000000000000000 x1 0000000000000001 x2 0000000000000001 x3 0000000000000003
09-04 08:42:57.265 4652 4652 F DEBUG : x4 0000000000000067 x5 0080000000000000 x6 0aff646268757164 x7 7f7f7f7f7f7f7f7f
09-04 08:42:57.265 4652 4652 F DEBUG : x8 0000f12def02b000 x9 601f5be1668242d3 x10 0000f12de3a7f580 x11 0000000000000014
09-04 08:42:57.265 4652 4652 F DEBUG : x12 0000f12de3a7f6b8 x13 ffffffffffffffff x14 ffffffffff000000 x15 ffffffffffffffff
09-04 08:42:57.265 4652 4652 F DEBUG : x16 0000f12e0a4fbcc0 x17 0000f12e0cb317e0 x18 0000f12de3a7ee7a x19 0000000000000001
09-04 08:42:57.265 4652 4652 F DEBUG : x20 0000000000000001 x21 0000f12e09dc6c00 x22 0000f12de3a7fdc0 x23 0000000072e471d2
09-04 08:42:57.265 4652 4652 F DEBUG : x24 0000000000000008 x25 0000f12de3a81588 x26 0000f12e09dc6ca0 x27 0000000000000002
09-04 08:42:57.265 4652 4652 F DEBUG : x28 0000000000000002 x29 0000f12de3a7fbe8
09-04 08:42:57.265 4652 4652 F DEBUG : sp 0000f12de3a7faf0 lr 000000007195e9a4 pc 0000f12deeff5e30
检查设备中 /system/lib/led.default.so 文件是否存在,如果不存在则需要在主机端中将 out/target/product/mek_8q/obj/SHARED_LIBRARIES/led.default_intermediates/led.default.so 拷贝到 out/target/product/mek_8q/system/lib 目录中,再重新编译烧录。如果 led.default_intermediates 目录中也不存在 so 文件,则还要重新执行 mmm hardware/libhardware/modules/test_led,来生成 so 文件。
理论上我们也可以将 so 文件直接 push 到开发板的 /system/lib 目录中,但是由于权限问题,会出现 Read-only file system 的错误提示,所以只能通过重新编译生成系统文件来实现。
-
Android
+关注
关注
12文章
3935浏览量
127378 -
Linux
+关注
关注
87文章
11302浏览量
209431 -
硬件
+关注
关注
11文章
3324浏览量
66214 -
代码
+关注
关注
30文章
4786浏览量
68568
发布评论请先 登录
相关推荐
评论