java 流程驱动 框架_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接口给应用程序:

4353170636291.png

优点:简单易行;

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

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

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

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

4353170636292.png

二、举例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}*/

interfaceIMySerialService

{int serialOpen( String filename, intflags );int serialSetOpt( int fd,int nSpeed,int nBits,char nEvent,intnStop );

String serialRead(int fd, intlen );int serialWrite( intfd, String txData );void serialClose( intfd );

}

同时需要修改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 classMySerialService extends IMySerialService.Stub {private static final String TAG = "MySerialService";public native static int nativeSerialOpen(String filename, intflags ) ;public native static int nativeSerialSetOpt(int fd, int nSpeed, int nBits, char nEvent, intnStop) ;public native static java.lang.String nativeSerialRead(int fd, intlen ) ;public native static int nativeSerialWrite( intfd, java.lang.String txData ) ;public native static void nativeSerialClose(intfd );public int serialOpen(java.lang.String filename, intflags) throws android.os.RemoteException {returnnativeSerialOpen(filename,flags);

}public int serialSetOpt(int fd, int nSpeed, int nBits, char nEvent, intnStop) throws android.os.RemoteException {returnnativeSerialSetOpt( fd, nSpeed, nBits, nEvent, nStop );

}public java.lang.String serialRead(int fd, intlen) throws android.os.RemoteException {returnnativeSerialRead( fd, len );

}public int serialWrite(intfd, java.lang.String txData) throws android.os.RemoteException {returnnativeSerialWrite( fd, txData);

}public void serialClose(intfd) 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

namespaceandroid

{#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);returnrtn;

}#endifjint 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, charnEvent, 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*/

returncharTojstring(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;

importstaticandroid.os.ServiceManager.getService;public classMainActivity extends AppCompatActivity {

Button iButton=null;private IMySerialService iSerialService = null;

@Overrideprotected voidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

iButton=findViewById(R.id.button);

iButton.setOnClickListener(newView.OnClickListener() {public voidonClick(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.defaultLOCAL_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#include#include#include#include#include#include

int halSerialOpen( char* filename, intflags )

{

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

}int halSerialSetOpt( int fd, int nSpeed, int nBits, char nEvent, intnStop)

{

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

}char* halSerialRead( int fd, intlen )

{

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( intfd )

{

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_DECLSstructmyserial_device_t {structhw_device_t common;int (*serial_open)( char* filename, intflags ) ;int (*serial_setOpt)( int fd, int nSpeed, int nBits, char nEvent, intnStop) ;char* (*serial_read)( int fd, intlen );int (*serial_write)( int fd, char*txData ) ;void (*serial_close)( intfd );

} ;

__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

namespaceandroid

{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);returnrtn;

}#endifjint 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, charnEvent, 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);returncharTojstring(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值