zoukankan      html  css  js  c++  java
  • Android驱动学习-app调用内核驱动过程(驱动框架回顾)

    考研已经过去了,android驱动的学习也断了半年多了,现在重新捡起来学习,回顾一下Android驱动的大体框架。

    Android系统的核心是java,其有一个David虚拟机。Android-app操作硬件也相当于是java操作硬件。

    在Linux系统上操作硬件是通过open read write等来实现,也就是操作C库。如果java能直接调用C库中的函数,也就解决了app操作硬件的问题。

    下面的文章是java调用C/C++库的方法。

    链接:JAVA程序通过JNI调用C/C++库

    1.方法1——jni调用底层驱动

    在android框架中写入c/c++直接调用底层linux驱动,并向上提供jni接口给应用程序:

    优点:简单易行;

    缺点:主要在于驱动程序,由于在linux中需要遵循GPL协议,需要开源,而许多厂商的一些代码不希望开源。

    而且像屏幕等设备可能需要多个app同时操作,这样的话将会导致各种各样的问题。

    2.方法2——增加硬件抽象层

    将驱动程序一分为二,一部分开源在内核中,一部分不开源在android框架中:

    二、举例led android驱动:

    从这里我们将看到整个应用框架层到底层驱动的走向。首先,无论是哪种方法,我们都需要实现一个linux驱动以供上层访问led资源。

     同样也是通过jni来加载C库,从而通过调用open等来实现对硬件的操作。Android为了实现多个APP能操作同一个硬件,硬件不由app来直接操作,而是有SystemServer来操作。app需要把请求通过serviceManager发给SystemServer,由SystemServer最终完成对硬件的操作。

    这里我们反过来思考一个app操作硬件的过程:

    1、app需要申请服务和获得服务getservice。

    而这个服务是有接口完成的。所以第一步我们需要建立一个aidl文件,来生成接口类。

    frameworks/base/core/java/android/os/ILedService.aidl

    同时修改 frameworks/base/Android.mk, 加入新建的aidl文件。

    现在以实现一个利用app来通过串口对外发送数据的案例;

    1. 首先创建文件  frameworks/base/core/java/android/os/IMySerialService.aidl

    package android.os;
    
    /** {@hide} */
    interface IMySerialService
    {
        int serialOpen( String filename, int flags );
        int serialSetOpt( int fd,int nSpeed,int nBits,char nEvent,int nStop );
        String serialRead( int fd, int len );
        int serialWrite( int fd, String txData );
        void serialClose( int fd );
    }

      同时需要修改Android.mk文件添加我们的aidl文件

    2、自动生成ILedService.java

    mmm frameworks/base/

    编译自动生成

    out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/ILedService.java

    ILedService.java文件获得了,现在就需要新建与之对应的java文件,实现java下的对硬件操作的函数。

      2.运行:mmm frameworks/base/  进行编译,将会生成 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/IMySerialService.java 文件。

        下一步就是要实现 ISerialService.java 中被自动创建的接口函数了

    3、创建 LedService.java 实现接口函数

    创建完LedService.java后将其放到frameworks/base/services/core/java/com/android/server/中,其上层Android.mk会自动包含此java文件。

      3.  新建文件  frameworks/base/services/core/java/com/android/server/SerialService.java

      

    package com.android.server;
    
    import android.os.IMySerialService;
    import android.util.Slog;
    
    public class MySerialService extends IMySerialService.Stub {
        private static final String TAG = "MySerialService";
    
        public native static int nativeSerialOpen(String filename, int flags ) ;
        public native static int nativeSerialSetOpt(int fd, int nSpeed, int nBits, char nEvent, int nStop) ;
        public native static java.lang.String nativeSerialRead(int fd, int len ) ;
        public native static int nativeSerialWrite( int fd, java.lang.String txData ) ;
        public native static void nativeSerialClose(int fd );
        
    
        public int serialOpen(java.lang.String filename, int flags) throws android.os.RemoteException {
            return nativeSerialOpen(filename,flags);
        }
        public int serialSetOpt(int fd, int nSpeed, int nBits, char nEvent, int nStop) throws android.os.RemoteException {
            return nativeSerialSetOpt( fd, nSpeed, nBits, nEvent, nStop );
        }
        public java.lang.String serialRead(int fd, int len) throws android.os.RemoteException {
            return nativeSerialRead( fd, len );
        }
        public int serialWrite(int fd, java.lang.String txData) throws android.os.RemoteException {
            return nativeSerialWrite( fd, txData);
        }
        public void serialClose(int fd) throws android.os.RemoteException {
            nativeSerialClose(fd);
        }
      
    }

        public int serialOpen(java.lang.String filename, int flags) throws android.os.RemoteException 

        这些函数的定义在自动生成的IMySerialService.java的最下面,直接拷贝过来实现即可。

    4、将服务注册到Service Manager当中

    修改frameworks/base/services/java/com/android/server/SystemServer.java

    这样的话,app就能获得服务,从而实现对SystemServer的通信,从而实现对硬件的操作。现在就是需要对SystemServer进行修改了。

    SystemServer对硬件的操作也是jni,所以其也需要加载C/C++库。在Android系统中已经把所有对硬件操作的jni文件打包为一个.so库,所以我们需要做的是在so库中添加我们对硬件的支持。

      4. 将下面的代码加入到 SystemServer.java 中,这样的话就把服务加入到了ServiceManager

                Slog.i(TAG, "Serial Service");
                ServiceManager.addService("serial", new SerialService());

    5、实现com_android_server_LedService.cpp   供 LedService.java使用

    将jni文件放到frameworks/base/services/core/jni/目录中。还要注册native接口,在frameworks/base/services/core/jni/onload.cpp中修改。

    并修改Android.mk文件,加入com_android_server_LedService.cpp的编译。

    同时还要修改onload.cpp,添加 register_android_server_MySerialService(env);  和 函数声明。

    #define LOG_TAG "MySerialService"
    
    #include "jni.h"
    #include "JNIHelp.h"
    #include "android_runtime/AndroidRuntime.h"
    
    #include <utils/misc.h>
    #include <utils/Log.h>
    
    #include <stdio.h>
    
    namespace android
    {
    #if 1
    static jstring charTojstring(JNIEnv* env, const char* pat) {
        //定义java String类 strClass
        jclass strClass = (env)->FindClass("Ljava/lang/String;");
        //获取String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
        jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
        //建立byte数组
        jbyteArray bytes = (env)->NewByteArray(strlen(pat));
        //将char* 转换为byte数组
        (env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*) pat);
        // 设置String, 保存语言类型,用于byte数组转换至String时的参数
        jstring encoding = (env)->NewStringUTF("GB2312");
        //将byte数组转换为java String,并输出
        return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);
    }
    
    char* jstringToChar(JNIEnv* env, jstring jstr) {
        char* rtn = NULL;
        jclass clsstring = env->FindClass("java/lang/String");
        jstring strencode = env->NewStringUTF("GB2312");
        jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
        jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
        jsize alen = env->GetArrayLength(barr);
        jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
        if (alen > 0) {
            rtn = (char*) malloc(alen + 1);
            memcpy(rtn, ba, alen);
            rtn[alen] = 0;
        }
        env->ReleaseByteArrayElements(barr, ba, 0);
        return rtn;
    }
    #endif
    
    jint jniSerialOpen( JNIEnv *env, jobject clazz, jstring filename, jint flags ) 
    {
        char *arrChars = jstringToChar(env, filename );
        ALOGI("jniSerialOpen:%s,%d
    ",arrChars,flags);
    
        return flags+1;
    }
    jint jniSerialSetOpt( JNIEnv *env, jobject clazz, jint fd, jint nSpeed, jint nBits, char nEvent, jint nStop) 
    {
        ALOGI("jniSerialSetOpt:%d,%c
    ",fd,nEvent);
        return fd+1;
    }
    jstring jniSerialRead( JNIEnv *env, jobject clazz, jint fd, jint len )
    {
        char arrTx[] = "This is a chars";
        ALOGI("jniSerialRead:%d,%c
    ",fd,len);
        /* CStr2Jstring */
    
        return charTojstring(env, arrTx);
    }
    jint jniSerialWrite( JNIEnv *env, jobject clazz, jint fd, jstring txData ) 
    {
        char *arrChars = jstringToChar(env, txData );
        ALOGI("jniSerialWrite:%d,%s
    ",fd,arrChars);
        
        return 0;
    }
    void jniSerialClose( JNIEnv *env, jobject clazz, jint fd )
    {
        ALOGI("jniSerialClose:%d
    ",fd);
    }
    
    static JNINativeMethod method_table[] = {
        { "nativeSerialOpen", "(Ljava/lang/String;I)I", (void*)jniSerialOpen },
        { "nativeSerialSetOpt", "(IIICI)I", (void*)jniSerialSetOpt },
        { "nativeSerialRead", "(II)Ljava/lang/String;", (void*)jniSerialRead },
        { "nativeSerialWrite", "(ILjava/lang/String;)I", (void*)jniSerialWrite },
        { "nativeSerialClose", "(I)V", (void*)jniSerialClose },
    
         
    };
    
    int register_android_server_MySerialService(JNIEnv *env)
    {
        return jniRegisterNativeMethods(env, "com/android/server/MySerialService",
                method_table, NELEM(method_table));
    }
    
    };

    这里其实已经差不多实现了对硬件的操作,因为我们实现了对cpp文件的调用并打包进系统,cpp文件就能直接加载C库,调用open等函数实现对硬件的操作。

    但是这样的话有一个问题,就是如果驱动有点儿问题,我们就需要修改com_android_server_LedService.cpp,并重新编译系统、烧写系统。这样的话不是很方便。我们可以再引入硬件抽象层(HAL),这样更有利于搭建整个系统。

    注意:编译的时候使用:

    mmm frameworks/base/services/
    mmm frameworks/base/
    make snod

    只执行  mmm frameworks/base/  将不会 frameworks/base/services/ 下修改的文件进行编译。 这个要注意。

     JNI 向上提供本地函数,向下加载HAL文件并调用HAL的函数

    HAL负责访问驱动程序执行硬件操作(dlopen)。

    这里编译系统,运行app  即可打印出信息(logcat | grep "MySerialSerice")

    APP代码: 需要添加jar包。在 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar

    package com.lmissw.serialdevicetest;
    
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.os.IMySerialService;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    
    import static android.os.ServiceManager.getService;
    
    public class MainActivity extends AppCompatActivity {
        Button iButton=null;
        private IMySerialService iSerialService = null;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            iButton = findViewById(R.id.button);
            iButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    iSerialService = IMySerialService.Stub.asInterface(getService("myserial"));
                    try {
                        int fd = iSerialService.serialOpen("/dev/s3c2410_serial2",0);
                        iSerialService.serialSetOpt(2,3,4,'t',6);
                        Log.i("serialTest","App serialSetOpt"+fd);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            });
    
        }
    }

     6、实现Hal层程序

    JNI 向上提供本地函数,向下加载HAL文件并调用HAL的函数。

    HAL负责访问驱动程序并执行硬件操作。而HAL层代码被编程一个.so动态库。

    所以JNI加载HAL就是加载动态库,而在linux系统中加载动态库使用的是dlopen。

    在Android系统中,不直接使用dlopen,是因为Android对dlopen做了一层封装,使用的是hw_get_module("模块名");

    而hw_get_module最终还是调用dlopen,那么就需用从模块名拿到文件名了。

      模块名 >> 文件名

        在模块名转化为名时,使用的是hw_module_by_class(模块名,NULL),在其中将name=“模块名”;

          然后使用property_get 获得属性值(价值对),先查询ro.hardware.模块名、ro.hardware、ro.product.board、ro.board.platform、ro.arch。上面的每个属性都对应一个subname。

          然后使用hw_module_exists 在 HAL_LIBRARY_PATH 环境变量、/vendor/lib/hw、/system/lib/hw 三个文件夹中 判断 "name"."subname".so 文件是否存在。如果都没有则查找 "name".default.so。

          如果上述的模块存在,则调用load 加载,在load中使用了dlopen。再使用dlsym("HMI")从so获得名为hw_module_t结构体。然后判断结构体中的名字和name是否一致,一致则表明找到了模块。

    JNI怎么使用HAL

      使用hw_get_module获得一个hw_module_t结构体。调用module中的open函数来获得一个hw_device_t结构体。并且把hw_device_t结构体转换为设备自定义的结构体。自定义的结构体第一个成员是 hw_device_t结构体。

    在 hardware/libhardware/include/hardware/ 下创建hal层的头文件,在hardware/libhardware/modules/ 目录下创建一个新的目录用来 存放hal的C文件,并创建一个Android.mk文件。

    编译的话用 mmm  hardware/libhardware/modules/+创建的目录名。

    /* filename: Android.mk */
    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE := myserial.default
    
    LOCAL_MODULE_RELATIVE_PATH := hw
    LOCAL_C_INCLUDES := hardware/libhardware
    LOCAL_SRC_FILES := myserial.c
    LOCAL_SHARED_LIBRARIES := liblog
    LOCAL_MODULE_TAGS := eng
    
    include $(BUILD_SHARED_LIBRARY)

    myserial.c

    #define LOG_TAG "MySerialHal"
    
    #include <hardware/myserial.h>
    #include <hardware/hardware.h>
    #include <cutils/log.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    
    int halSerialOpen( char* filename, int flags ) 
    {
        ALOGI("halSerialOpen:%s,%d
    ",filename,flags);
        return 0;
    }
    int halSerialSetOpt( int fd, int nSpeed, int nBits, char nEvent, int nStop) 
    {
        ALOGI("halSerialSetOpt:%d,%c
    ",fd,nEvent);
        return 0;
    }
    char* halSerialRead( int fd, int len )
    {
        ALOGI("halSerialRead:%d,%c
    ",fd,len);
        return 0;
    }
    int halSerialWrite( int fd, char* txData ) 
    {
        ALOGI("halSerialWrite:%d,%s
    ",fd,txData);
        return 0;
    }
    void halSerialClose( int fd )
    {
        ALOGI("halSerialClose:%d
    ",fd);
    }
    
    struct myserial_device_t myserial_device = {
        .common = {
            .tag = HARDWARE_DEVICE_TAG,
        },
        .serial_open = halSerialOpen,
        .serial_setOpt = halSerialSetOpt,
        .serial_read = halSerialRead,
        .serial_write = halSerialWrite,
        .serial_close = halSerialClose,
    };
    
    int serial_open(const struct hw_module_t* module, const char* id,
                struct hw_device_t** device) {
        *device = &myserial_device;
         return 0;
     }
    
    static struct hw_module_methods_t myserial_module_methods = {
        .open = serial_open,
    };
    
    struct hw_module_t HAL_MODULE_INFO_SYM = {
        .tag = HARDWARE_MODULE_TAG,    
        .name = "myserial Device HAL",   
        .id = "myserial",        //必填
        .methods = &myserial_module_methods, //必填
    };

    myserial.h

    #ifndef _HARDWARE_MYSERIAL_H
    #define _HARDWARE_MYSERIAL_H
    
    #include <hardware/hardware.h>
    
    __BEGIN_DECLS
    
    struct myserial_device_t {
        struct hw_device_t common;
        int (*serial_open)( char* filename, int flags ) ;
        int (*serial_setOpt)( int fd, int nSpeed, int nBits, char nEvent, int nStop) ;
        char* (*serial_read)( int fd, int len );
        int (*serial_write)( int fd, char* txData ) ;
        void (*serial_close)( int fd );
    } ;
    
    __END_DECLS
    
    #endif  // _HARDWARE_MYSERIAL_H
    /* filename com_android_server_MySerialService.cpp */
    #define
    LOG_TAG "MySerialService" #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include <utils/misc.h> #include <utils/Log.h> #include <hardware/myserial.h> #include <stdio.h> namespace android { struct hw_module_t *module; struct hw_device_t* device; struct myserial_device_t* serial_dev; #if 1 static jstring charTojstring(JNIEnv* env, const char* pat) { //定义java String类 strClass jclass strClass = (env)->FindClass("Ljava/lang/String;"); //获取String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V"); //建立byte数组 jbyteArray bytes = (env)->NewByteArray(strlen(pat)); //将char* 转换为byte数组 (env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*) pat); // 设置String, 保存语言类型,用于byte数组转换至String时的参数 jstring encoding = (env)->NewStringUTF("GB2312"); //将byte数组转换为java String,并输出 return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding); } static char* jstringToChar(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("GB2312"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*) malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; } #endif jint jniSerialOpen( JNIEnv *env, jobject clazz, jstring filename, jint flags ) { jint err; char *arrChars = jstringToChar(env, filename ); err = hw_get_module("myserial",(hw_module_t const**)&module); if (err==0) { err = module->methods->open( module, NULL, &device ); if (err==0) { serial_dev = (struct myserial_device_t*) device; ALOGI("jniSerialOpen:%s,%d ",arrChars,flags); return serial_dev->serial_open(arrChars,flags); } else { ALOGI("open:error "); } } else { ALOGI("hw_get_module:error "); } return -1; } jint jniSerialSetOpt( JNIEnv *env, jobject clazz, jint fd, jint nSpeed, jint nBits, char nEvent, jint nStop) { ALOGI("jniSerialSetOpt:%d,%c ",fd,nEvent); return serial_dev->serial_setOpt(fd,nSpeed,nBits,nEvent,nStop); } jstring jniSerialRead( JNIEnv *env, jobject clazz, jint fd, jint len ) { char* arrTx= serial_dev->serial_read(fd,len); ALOGI("jniSerialRead:%d,%c ",fd,len); return charTojstring(env, arrTx); } jint jniSerialWrite( JNIEnv *env, jobject clazz, jint fd, jstring txData ) { char *arrChars = jstringToChar(env, txData ); ALOGI("jniSerialWrite:%d,%s ",fd,arrChars); return serial_dev->serial_write(fd,arrChars); } void jniSerialClose( JNIEnv *env, jobject clazz, jint fd ) { ALOGI("jniSerialClose:%d ",fd); serial_dev->serial_close(fd);; } static JNINativeMethod method_table[] = { { "nativeSerialOpen", "(Ljava/lang/String;I)I", (void*)jniSerialOpen }, { "nativeSerialSetOpt", "(IIICI)I", (void*)jniSerialSetOpt }, { "nativeSerialRead", "(II)Ljava/lang/String;", (void*)jniSerialRead }, { "nativeSerialWrite", "(ILjava/lang/String;)I", (void*)jniSerialWrite }, { "nativeSerialClose", "(I)V", (void*)jniSerialClose }, }; int register_android_server_MySerialService(JNIEnv *env) { return jniRegisterNativeMethods(env, "com/android/server/MySerialService", method_table, NELEM(method_table)); } };

    首先 实现一个 hw_module_t结构体,实现一个methodsidmethods中实现open函数,open函数根据传入的名字返回设备自定义的结构体。实现一个设备自定义的结构体,并在设备自定义的结构体中加入需要的函数指针,并在.c文件中实现这些函数。

    编译:

    mmm frameworks/base/services/
    mmm  hardware/libhardware/modules/myserial/
    make snod
  • 相关阅读:
    HDU 1075 What Are You Talking About(字典树)
    HDU 1075 What Are You Talking About (stl之map映射)
    HDU 1247 Hat’s Words(字典树活用)
    字典树HihoCoder
    HDU 1277全文检索(字典树)
    HDU 3294 Girls' research(manachar模板题)
    HDU 3294 Girls' research(manachar模板题)
    HDU 4763 Theme Section(KMP灵活应用)
    Ordering Tasks UVA
    Abbott's Revenge UVA
  • 原文地址:https://www.cnblogs.com/ynxf/p/8214773.html
Copyright © 2011-2022 走看看