3.LED灯简单驱动程序编写流程(2)


1.续上一节内容

承接上一节内容。

  1. 流程分析

上一节主要是跳出android系统的设计思路,按照最简单的linux分层思想来做的一个实践,而没有使用android系统自带的服务。

先简单说一说android自带的服务流程。

系统启动,zygote运行起来,调用到SystemServer服务,运行run()方法。会加载库android_servers,也就是调用到onload.cpp中的JNI_ONLoad程序,其中会注册很多的服务,如下所示:


3.LED灯简单驱动程序编写流程(2)_第1张图片
注册方法

加载库android_servers文件,其实就是libandroid_servers,查看一下文件:frameworks/base/services/Android.mk,可以找到如下一行内容:


libandroid_servers库文件

所以,其实会加载frameworks/base/services/目录下的内容。

以示例VibratorService为例,执行到frameworks\base\services\core\jni\com_android_server_VibratorService.cpp文件内容,也就是能调用到JNI。

接着看SystemServer内容,最后还会执行startOtherServices()函数,添加了一项服务,还是以Vibrator为例子,如下图所示:


Vibrator服务添加例子

这个函数的执行,会进入VibratorService.java程序。这里面有jni需要执行的底层函数。

基于此,就能简单的认为,编写目前的LED程序,需要下面的步骤:
1.编写一个JNI程序,命名和Vibrator相似,如:com_android_server_LedService.cpp
2.基于1,在onload.cpp中添加刚注册的函数内容。
3.编写LedService.java文件,添加JNI上一层的函数调用关系。
4.修改SystemServer.java,将3的服务添加进去。
5.编写ILedService.java,该文件给Android APP提供接口,解脱APP开发人员的工作。

2.编写代码

现在开始从上往下添加代码:
1.编写ILedService.java接口文件(aidl文件)
参考IVibratorService.aidl文件,将文件先命名为:ILedService.java,然后放入 android-5.0.2\frameworks\base\core\java\android\os下。

aidl文件内容如下所示:

package android.os;

/** {@hide} */
interface ILedService
{
    int ledctl(int which,int state);
}

接着先将环境变量设置一遍:

$ sudo -i
$ . setenv
$ lunch
$ cd frameworks/base
$ vi Android.mk
...
core/java/android/os/ILedService.aidl 
...
$ mmm .
...
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes-jarjar.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/emma_out/lib/classes-jarjar.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/noproguard.classes.jar
target Dex: framework


Copying: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.dex
target Jar: framework (out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/javalib.jar)
Install: out/target/product/tiny4412/system/framework/framework.jar
make: Leaving directory `/home/wityuan/Desktop/android-5.0.2'

#### make completed successfully (03:12 (mm:ss)) ####
...
$ cd ../../
$ cd out
$ find -name "ILedService.java"
./target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java
$ 

之后使用该接口,可以参考SystemVibrator.java文件。

2.编写LedService.java文件。

文件路径为:frameworks/base/services/core/java/com/android/server/
该文件主要用来调用本地方法。


package com.android.server;
import android.os.ILedService;

public class LedService extends ILedService.Stub{
    private static final String TAG = "LedService";
     

    public int ledctl(int which, int state) throws android.os.RemoteException
    {
        return native_ledctl(which,state);
    }

    public LedService()
    {
        native_ledopen();
    }

     public static native int native_ledctl(int which, int state);
     public static native int native_ledopen( );
     public static native void native_ledclose( );
}

3.修改SystemServer.java文件:
文件路径为:android-5.0.2/android-5.0.2/frameworks/base/services/java/com/android/server/SystemServer.java

...
            Slog.i(TAG, "Led Service");           
            ServiceManager.addService("Led", new LedService());
...

4.添加com_android_server_LedService.cpp文件

加入anroid系统源码的:frameworks/base/services/core/jni/位置。

#define LOG_TAG "LedService"

#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 



namespace android
{

static jint fd;

jint native_ledopen(JNIEnv *env , jobject thiz)
{
    fd = open("/dev/leds",O_RDWR);
    
    __android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_open...%d \r\n",fd);
    if(fd<0)
        return -1;
    return 0;
}


void native_ledclose(JNIEnv *env , jobject thiz)
{
    __android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_close...");
    close(fd);
}


jint native_ledctl(JNIEnv *env , jobject thiz ,jint number, jint state)
{
    int ret = ioctl(fd,state,number);
    __android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_control:%d,%d...",number,state);

    return ret;
}


static JNINativeMethod methods[] = {
    { "native_ledctl", "(II)I", (void*) native_ledctl },
    { "native_ledopen", "()I", (void*) native_ledopen },
    { "native_ledclose", "()V", (void*) native_ledclose }
};

int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            methods, NELEM(methods));
}

};

修改frameworks\base\services\core\jni\Android.mk文件:

$ vi frameworks/base/services/core/jni/Android.mk
...
$(LOCAL_REL_DIR)/com_android_server_LedService.cpp \
...

5.修改onload.cpp文件内容
文件路径为: android-5.0.2/android-5.0.2/frameworks/base/services/core/jni/Onload.cpp

...
int register_android_server_LedService(JNIEnv *env);
...
...
register_android_server_LedService(env);
...

完成以上步骤之后,就可以执行编译操作了。

$ mmm frameworks/base/services/
$ make snod
$ ./gen-img.sh
$  cp system.img /mnt/hgfs/Desktop

把以上需要拷贝的文件,集中到下面:

$ cp /mnt/hgfs/Desktop/LedService.java   frameworks/base/services/core/java/com/android/server/

$ cp /mnt/hgfs/Desktop/SystemServer.java  frameworks/base/services/java/com/android/server/

$ cp /mnt/hgfs/Desktop/com_android_server_LedService.cpp frameworks/base/services/core/jni/ 

$ cp /mnt/hgfs/Desktop/onload.cpp frameworks/base/services/core/jni/

2.编写App

将一些framework程序拷贝到android内核中之后,需要在app中引用,这个时候就需要对Android studio做一些设置了。

1.拷贝classes.jar包

$ cp out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar /mnt/hgfs/Desktop/

添加到Android studio中。
file--->project structure--->+import .JAR/.AAR Package--->....

2.解决出现的错误

在build.gradle(Module:app)中添加如下内容:

defaultConfig{
...

...
multiDexEnabled true
}

3.出现错误以及解决办法

1.出现binder error等错误信息。

如下图所示:


3.LED灯简单驱动程序编写流程(2)_第2张图片
binder

出现这种情况的时候,是进入不了启动界面的,也就不能测试App了。

经过排查,是以上的代码:

            Slog.i(TAG, "Led Service");           
            ServiceManager.addService("Led", new LedService()); 

添加的有问题导致。
最终排查到onload.cpp文件没有修改,也就是说,jni文件没有起效。

4.更进一步

在上面的程序中,其实使用仍然是jni控制的驱动程序,并没有完全的体现出非常好的分层效果。不过有一个需要说明的是,它将APP与framework之间的分层体现出来了。这样写APP的开发人员并不需要了解驱动,而只需要知道要调用哪些接口程序,接口程序的功能即可。

但是,还有一个问题仍然没有解决,HAL一般是厂商写成so文件的,JNI解析so文件即可。这样就能做成厂商封闭其不想开源的部分。

要达到这个效果,就需要hal层提供so文件中的函数接口。

可以查看hw_get_module接口函数。而具体的使用方法,可以参考com_android_server_lights_LightService.cpp文件。对于编写底层的hal程序,可以参考vibrator.c文件。

现在第一步可以编写led_hal.c文件,是针对linux接口的最底层的开发的上层应用接口。

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 


static int led_close(struct hw_device_t* dev);
static int led_open(struct led_device_t* dev);
static int led_ctrl(struct led_device_t* dev,int number,int state);


struct led_device_t led_dev={
    .common = {
        .close = led_close,
    },
    .led_open = led_open,
    .led_ctrl = led_ctrl,
};

static int fd;



static int led_close(struct hw_device_t* dev)
{
    close(fd);

    return 0;
}


static int led_ctrl(struct led_device_t* dev,int number,int state)
{
    int ret = ioctl(fd,state,number);
    __android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_control:%d,%d...",number,state);

    return ret;

}

static int led_open(struct led_device_t* dev)
{
    fd = open("/dev/leds",O_RDWR);
    
    __android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_open...%d \r\n",fd);
    if(fd<0)
        return -1;
    return 0;
}



int led_device_open(const struct hw_module_t* module, const char* id,
        struct hw_device_t** device)
{
     *device = (hw_device_t *)&led_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 = "led",
    .id = "led",
    .name = "Default led HAL",
    .author = "wit_yuan",
    .methods = &led_module_methods,
};

也定义一个led_hal.h文件,内容如下:

#ifndef _HARDWARE_led_H
#define _HARDWARE_led_H

#include 

__BEGIN_DECLS

struct led_device_t {
      
    struct hw_device_t common;
    int (*led_open)(struct led_device_t* dev);
    //void (*led_close)(struct led_device_t* dev);
    int (*led_ctrl)(struct led_device_t* dev,int number,int state);
};

__END_DECLS

#endif  // _HARDWARE_VIBRATOR_H

接着编写Android.mk文件:


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := led.default

# HAL module implementation stored in
# hw/.default.so
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)

将这三个文件传到位置:

hardware/libhardware/include/hardware/led_hal.h
hardware/libhardware/modules/led/led_hal.c
hardware/libhardware/modules/led/Android.mk

执行命令如下:

$ mkdir hardware/libhardware/modules/led
$ cp /mnt/hgfs/Desktop/led_hal.c hardware/libhardware/modules/led/led_hal.c
$ cp /mnt/hgfs/Desktop/led_hal.h hardware/libhardware/include/hardware/led_hal.h
$ cp /mnt/hgfs/Desktop/Android.mk hardware/libhardware/modules/led/Android.mk
$ cp /mnt/hgfs/Desktop/com_android_server_LedService.cpp frameworks/base/services/core/jni/ 

重新编写com_android_server_LedService.cpp文件:

#define LOG_TAG "LedService"

#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"

#include 
#include 


#include 
#include 
#include 
#include 
#include 



namespace android
{
    led_device_t *led_device;

static jint fd;

jint native_ledopen(JNIEnv *env , jobject thiz)
{
    jint err;
    hw_module_t *module;

    hw_device_t *device;
    
    err = hw_get_module("led",(const struct hw_module_t * * )&module);

    if(err == 0){
        err = module->methods->open(module,NULL,&device);
        if(err == 0){
            led_device = (led_device_t *)device;
            return led_device->led_open(led_device);
        }
        else{
            return -1;
        }
    }
    return -1;
}


void native_ledclose(JNIEnv *env , jobject thiz)
{
    __android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_close...");
    
}


jint native_ledctl(JNIEnv *env , jobject thiz ,jint number, jint state)
{

    
    __android_log_print(ANDROID_LOG_DEBUG,"led_jni","led_control:%d,%d...",number,state);

    return led_device->led_ctrl(led_device,number,state);
}


static JNINativeMethod methods[] = {
    { "native_ledctl", "(II)I", (void*) native_ledctl },
    { "native_ledopen", "()I", (void*) native_ledopen },
    { "native_ledclose", "()V", (void*) native_ledclose }
};

int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            methods, NELEM(methods));
}

};

整体编译程序:

$ mmm frameworks/base/services
$ mmm hardware/libhardware/modules/led
$ make snod
$ ./gen-img.sh
$ cp system.img /mnt/hgfs/Desktop/

编译完成之后,将文件system.img烧写到系统中,然后运行程序。

你可能感兴趣的:(3.LED灯简单驱动程序编写流程(2))