您好,欢迎来到叨叨游戏网。
搜索
您的当前位置:首页Android驱动学习-app调用内核驱动过程(驱动框架回顾)

Android驱动学习-app调用内核驱动过程(驱动框架回顾)

来源:叨叨游戏网
Android驱动学习-app调⽤内核驱动过程(驱动框架回顾)

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

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

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

下⾯的⽂章是java调⽤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.javaILedService.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 #include #include

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, \"\", \"([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\\n\",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\\n\",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\\n\",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\\n\",fd,arrChars);

return 0;}

void jniSerialClose( JNIEnv *env, jobject clazz, jint fd ){

ALOGI(\"jniSerialClose:%d\\n\",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 := hwLOCAL_C_INCLUDES := hardware/libhardwareLOCAL_SRC_FILES := myserial.c

LOCAL_SHARED_LIBRARIES := liblogLOCAL_MODULE_TAGS := enginclude $(BUILD_SHARED_LIBRARY)

myserial.c

#define LOG_TAG \"MySerialHal\"#include #include #include #include #include #include #include

int halSerialOpen( char* filename, int flags ) {

ALOGI(\"halSerialOpen:%s,%d\\n\",filename,flags); return 0;}

int halSerialSetOpt( int fd, int nSpeed, int nBits, char nEvent, int nStop) {

ALOGI(\"halSerialSetOpt:%d,%c\\n\",fd,nEvent); return 0;}

char* halSerialRead( int fd, int len ){

ALOGI(\"halSerialRead:%d,%c\\n\",fd,len); return 0;}

int halSerialWrite( int fd, char* txData ) {

ALOGI(\"halSerialWrite:%d,%s\\n\",fd,txData); return 0;}

void halSerialClose( int fd ){

ALOGI(\"halSerialClose:%d\\n\",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 __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 #include

#include #include 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, \"\", \"([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\\n\",arrChars,flags);

return serial_dev->serial_open(arrChars,flags); } else {

ALOGI(\"open:error\\n\"); } } else {

ALOGI(\"hw_get_module:error\\n\"); }

return -1; }

jint jniSerialSetOpt( JNIEnv *env, jobject clazz, jint fd, jint nSpeed, jint nBits, char nEvent, jint nStop) {

ALOGI(\"jniSerialSetOpt:%d,%c\\n\",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\\n\",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\\n\",fd,arrChars);

return serial_dev->serial_write(fd,arrChars);}

void jniSerialClose( JNIEnv *env, jobject clazz, jint fd ){

ALOGI(\"jniSerialClose:%d\\n\",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结构体,实现⼀个methods和id,methods中实现open函数,open函数根据传⼊的名字返回设备⾃定义的结构体。实现⼀个设备⾃定义的结构体,并在设备⾃定义的结构体中加⼊需要的函数指针,并在.c⽂件中实现这些函数。 编译:

mmm frameworks/base/services/

mmm hardware/libhardware/modules/myserial/make snod

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- gamedaodao.net 版权所有 湘ICP备2024080961号-6

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务