美文网首页
3.LED灯简单驱动程序编写流程(2)

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

作者: wit_yuan | 来源:发表于2018-04-22 16:28 被阅读0次

    1.续上一节内容

    承接上一节内容。

    1. 流程分析

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

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

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


    注册方法

    加载库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 <utils/misc.h>
    #include <utils/Log.h>
    #include <hardware_legacy/vibrator.h>
    
    #include <stdio.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/ioctl.h>
    
    
    
    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等错误信息。

    如下图所示:


    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 <hardware/hardware.h>
    #include <cutils/log.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <math.h>
    #include <hardware/led_hal.h>
    
    #include <utils/Log.h>
    
    #include <stdio.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/ioctl.h>
    
    
    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 <hardware/hardware.h>
    
    __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/<VIBRATOR_HARDWARE_MODULE_ID>.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 <utils/misc.h>
    #include <utils/Log.h>
    
    
    #include <stdio.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/ioctl.h>
    
    
    
    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)

          本文链接:https://www.haomeiwen.com/subject/zejclftx.html