s5p4418 Android 4.4.2 驱动层 HAL层 服务层 应用层 开发流程记录(二 硬件抽象层HAL)

欢迎转载,务必注明出处:http://blog.csdn.net/wang_shuai_ww/article/details/44305599


本篇文章记录硬件抽象层。

还是跟之前一样,主要参考《Android系统源码情景分析》。

一.硬件抽象层

书里面写的是在/hardware/libhardware目录下写硬件抽象层,我这里并没有在该目录下,因为我使用的是与板子相关的,所以我就放在了板级目录下了,路径为/device/nexell/realarm,在/device/nexell/realarm路径下建立一个led文件夹来存放需要的.c、.h、.mk文件,我的文件名为led.c、led.h、Android.mk,源码分别如下:

led.c:

#include 
#include "led.h"

#include 
#include 
#include 
#include 

// 引入log头文件
#include   
// log标签
#define  TAG    "Led_Load_HAL"
// 定义info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG,__VA_ARGS__)
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)

#define DEVICE_NAME		"/dev/real_led"
#define MODULE_NAME		"led"
#define MODULE_AUYHOR	"[email protected]"

#define LED_ON 		1
#define LED_OFF		0

static int led_device_open(const struct hw_module_t *module, const char *id, struct hw_device_t **device);
static int led_device_close(struct hw_device_t *device);

static int led_set_on(struct led_device_t *dev, int num);
static int led_set_off(struct led_device_t *dev, int num);

static struct hw_module_methods_t led_module_methods = {
	open: led_device_open
};

struct led_module_t HAL_MODULE_INFO_SYM = {
	common: {
		tag: HARDWARE_MODULE_TAG,
		version_major: 1,
		version_minor: 0,
		id: LED_HARDWARE_MODULE_ID,
		name: DEVICE_NAME,
		author: MODULE_AUYHOR,
		methods: &led_module_methods,
	}
};

static int led_device_open(const struct hw_module_t *module, const char *id, struct hw_device_t **device) {
	if(!strcmp(id, LED_HARDWARE_DEVICE_ID)) {
		struct led_device_t *dev;
		dev = (struct led_device_t *)malloc(sizeof(struct led_device_t));
		if(!dev) {
			LOGE("Failed to alloc space for led_device_t");
			return -EFAULT;
		}
		
		memset(dev, 0, sizeof(struct led_device_t));
		
		dev->common.tag = HARDWARE_MODULE_TAG;
		dev->common.version = 0;
		dev->common.module = (hw_module_t *)module;
		dev->common.close = led_device_close;
		dev->set_on = led_set_on;
		dev->set_off = led_set_off;
		
		if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
			LOGE("Failed to open device file "DEVICE_NAME"-- %s.", strerror(errno));
			free(dev);
			return -EFAULT;
		}
		*device = &(dev->common);
		LOGI("Open device file "DEVICE_NAME" successfully.");
		return 0;
	}
	return -EFAULT;
}

static int led_device_close(struct hw_device_t *device){
	struct led_device_t *led_device = (struct led_device_t *)device;
	if(led_device){
		close(led_device->fd);
		free(led_device);
	}
	return 0;
}

static int led_set_on(struct led_device_t *dev, int num){
	if(!dev){
		LOGE("Null dev pointer.");
		return -EFAULT;
	}
	LOGI("Set the first %d LED lights.", num);
	ioctl(dev->fd, LED_ON, num);
	
	return 0;
}

static int led_set_off(struct led_device_t *dev, int num){
	if(!dev){
		LOGE("Null dev pointer.");
		return -EFAULT;
	}
	LOGI("Set the first %d LED close.", num);
	ioctl(dev->fd, LED_OFF, num);
	
	return 0;
}



led.h:

#ifndef ANDROID_LED_INTERFACE_H
#define ANDROID_LED_INTERFACE_H

#include 

__BEGIN_DECLS

#define LED_HARDWARE_MODULE_ID	"led"
#define LED_HARDWARE_DEVICE_ID	"led"

/*自定义模块结构体*/
struct led_module_t {
	struct hw_module_t common;
};

/*自定义设备结构体*/
struct led_device_t {
	struct hw_device_t common;
	int fd;
	int (*set_on)(struct led_device_t *dev, int num);
	int (*set_off)(struct led_device_t *dev, int num);
};

__END_DECLS
#endif
Android.mk:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := led.c
LOCAL_SHARED_LIBRARIES := liblog
#LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_MODULE := led.default

include $(BUILD_SHARED_LIBRARY)
上面三个文件生成的是.so文件,该硬件抽象层为JNI方法提供接口。
编译命令是,进入Android源码根目录,执行下面的命令:

mmm ./device/nexell/realarm/led/
在执行上面的mmm命令时,先确保已经把Android源码的环境变量已经添加进系统,也就是是否执行过source ./build/envsetup.sh 这条命令了。

另外在使用mmm指令时,如果没有使用lunch命令选择过编译的板级目标,那么一般默认的可能不是需要的板级目标,所以要使用lunch命令进行选择一下,如下所示:

wsh@ubuntu:/wsh_space/nexell/s5p4418/debug/android/android$ lunch

You're building on Linux

Lunch menu... pick a combo:
     1. aosp_arm-eng
     2. aosp_x86-eng
     3. aosp_mips-eng
     4. vbox_x86-eng
     5. mini_x86-userdebug
     6. mini_mips-userdebug
     7. mini_armv7a_neon-userdebug
     8. aosp_manta-userdebug
     9. aosp_drone2-userdebug
     10. aosp_drone-userdebug
     11. aosp_realarm-userdebug
     12. aosp_grouper-userdebug
     13. aosp_deb-userdebug
     14. aosp_flo-userdebug
     15. aosp_tilapia-userdebug
     16. aosp_hammerhead-userdebug
     17. aosp_mako-userdebug

Which would you like? [aosp_arm-eng]

我使用的是realarm的板子,所以选择数字11。


编译完成后,在out/target/product/realarm/system/lib/hw目录下即可看到led.default.so这个文件。

二.硬件访问服务JNI方法

下面的部分,新手可能比较难理解,不过没关系,照着写先实现功能再说,以后慢慢自然就了解了。

1.首先是硬件访问服务接口

硬件访问服务接口一般是在/frameworks/base/core/java/android/os目录下定义。我这里的文件名是ILedService.aidl,源码如下:

package android.os;

interface ILedService{
	int seton(int num);
	int setoff(int num);
}
为了能够编译它需要修改 /frameworks/base/目录下的Android.mk文件,添加一行代码:core/java/android/os/ILedService.aidl \
是加在LOCAL_SRC_FILES += \后面的任何一个位置,一般是放在最后,由于太长,只贴一部分如下所示:

	packages/services/Proxy/com/android/net/IProxyCallback.aidl \
	packages/services/Proxy/com/android/net/IProxyPortListener.aidl \
	core/java/android/os/ILedService.aidl \
然后使用下面命令进行编译:

mmm ./frameworks/base/

2.实现硬件访问服务

/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server;

import android.content.Context;
import android.os.ILedService;
import android.util.Slog;

/**
 * Shared singleton foreground thread for the system.  This is a thread for regular
 * foreground service operations, which shouldn't be blocked by anything running in
 * the background.  In particular, the shared background thread could be doing
 * relatively long-running operations like saving state to disk (in addition to
 * simply being a background priority), which can cause operations scheduled on it
 * to be delayed for a user-noticeable amount of time.
 */
public class LedService extends ILedService.Stub {
	private static final String TAG = "LedService";
	
	private int mPtr = 0;
	LedService(){
		mPtr = init_native();
		if(mPtr == 0){
			Slog.e(TAG, "Failed to initialize Led service.");
		}
	}
	public int setOn(int num){
		if(mPtr == 0){
			Slog.e(TAG, "Led service is not initialize.");
			return 0;
		}
		setOn_native(mPtr, num);
		
		return 0;
	}
	
	public int setOff(int num){
		if(mPtr == 0){
			Slog.e(TAG, "Led service is not initialize.");
			return 0;
		}
		setOff_native(mPtr, num);
		
		return 0;
	}
	private static native int init_native();
	private static native int setOn_native(int ptr, int num);
	private static native int setOff_native(int ptr, int num);
};


由上面代码可知,实现了硬件服务接口setOn和setOff的具体方法,由于java不能够直接使用HAL层提供的接口,所以这里使用native方式来与JNI方法连接。
小知识,声明native标示的函数,在这里无需具体实现,它是java和c/c++连接的桥梁,具体的在JNI层实现这些函数。
编译:
mmm ./frameworks/base/services/java/
编译得到的server.jar就包含了LedService类。

3.硬件访问服务的JNI方法

目录为/frameworks/base/services/jni,文件命名为com_android_server_LedService.cpp,要注意命名格式,一般为com_android_server_xxx.cpp。

源码如下:

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

#include 
#include 
#include 
#include "../../device/nexell/realarm/led/led.h"

#include 

// 引入log头文件
#include   
// log标签
#define LOG_TAG "LedServiceJNI"
// 定义info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

namespace android
{
	static jint led_seton(JNIEnv *env, jobject clazz, jint ptr, jint number){
		led_device_t *device = (led_device_t *)ptr;
		if(!device){
			LOGE("Device led is not open");
			return 0;
		}
		int num = number;
		LOGI("Set the first %d LED lights.", num);
		device->set_on(device, num);
		
		return num;
	}
	
	static jint led_setoff(JNIEnv *env, jobject clazz, jint ptr, jint number){
		led_device_t *device = (led_device_t *)ptr;
		if(!device){
			LOGE("Device led is not open");
			return 0;
		}
		int num = number;
		LOGI("Set the first %d LED close.", num);
		device->set_off(device, num);
		
		return num;
	}
	
	static inline int led_device_open(const hw_module_t *module, struct led_device_t **device){
		return module->methods->open(module, LED_HARDWARE_DEVICE_ID, (struct hw_device_t**)device);
	}
	
	static jint led_init(JNIEnv *env, jobject clazz){
		led_module_t *module;
		led_device_t *device;
		LOGI("Initializing HAL stub led......");
		
		if(hw_get_module(LED_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0){
			LOGI("Device led found");
			
			if(led_device_open(&(module->common), &device) == 0){
				LOGI("Device led is open.");
				return (jint)device;
			}
			LOGE("Failed to open device led.");
			return 0;
		}
		LOGE("Failed to get HAL stub led.");
		return 0;
	}
	
	static const JNINativeMethod method_table[] = {
		{"init_native", "()I", (void*)led_init},
		{"setOn_native", "(II)I", (void*)led_seton},
		{"setOff_native", "(II)I", (void*)led_setoff},
	};
	
	int register_android_server_LedService(JNIEnv *env){
		return jniRegisterNativeMethods(env, "com/android/server/LedService", method_table, NELEM(method_table));
	}
};

另外还需要修改两个文件。

修改/frameworks/base/services/jni/onload.cpp,完整文件如下:

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"

namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_AssetAtlasService(JNIEnv* env);
int register_android_server_LedService(JNIEnv *env);//user add
};

using namespace android;

extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("GetEnv failed!");
        return result;
    }
    ALOG_ASSERT(env, "Could not retrieve the env!");

    register_android_server_PowerManagerService(env);
    register_android_server_SerialService(env);
    register_android_server_InputApplicationHandle(env);
    register_android_server_InputWindowHandle(env);
    register_android_server_InputManager(env);
    register_android_server_LightsService(env);
    register_android_server_AlarmManagerService(env);
    register_android_server_UsbDeviceManager(env);
    register_android_server_UsbHostManager(env);
    register_android_server_VibratorService(env);
    register_android_server_SystemServer(env);
    register_android_server_location_GpsLocationProvider(env);
    register_android_server_location_FlpHardwareProvider(env);
    register_android_server_connectivity_Vpn(env);
    register_android_server_AssetAtlasService(env);
    register_android_server_ConsumerIrService(env);
	register_android_server_LedService(env);//user add

    return JNI_VERSION_1_4;
}
添加了int register_android_server_LedService(JNIEnv *env);//user add和register_android_server_LedService(env);//user add。

修改/frameworks/base/services/jni/Android.mk文件,添加如下:

LOCAL_SRC_FILES:= \
    ......
    com_android_server_LedService.cpp \
   onload.cpp

然后编译之,命令为:

mmm ./frameworks/base/services/jni/

生成的libandroid_servers.so文件就包含了我们实现的native方法了。

到此硬件访问服务LedService的实现就完成了,下面介绍怎么启动它。

三.启动硬件服务的方法

该部分的目的是让系统在启动的时候就加载led服务。

修改目录/frameworks/base/services/java/com/android/server/目录下的SystemServer.java文件,如下所示:

ActivityManagerService.self().systemReady(new Runnable() {
            public void run() {
 
		......
		//user add
                try {
                    Slog.i(TAG, "Realarmled service");
                    ServiceManager.addService("led", new LedService());
                } catch (Throwable e) {
                    Slog.e(TAG, "Failure starting Realarmled Service", e);
                }
	}

 让系统在启动的时候加载LedService服务。 
  

编译:

mmm ./frameworks/base/services/java/

到这里,out/target/product/realarm/system目录下就已经包含了我们所编译后的jar和.so文件了,打包系统文件烧写到开发板,下一个博客记录怎么写配套的应用程序app。


注意:打包之前检查一下ueventd.realarm.rc这个文件的最后时候有这一句/dev/real_led     0666   systemsystem,该句是修改驱动程序生成的real_led设备节点权限,以提供给HAL使用,否则会提示没有权限,无法打开的错误。


应用程序的写法介绍用两种方式,eclipse和Android源码目录下这两种方式。这里面涉及一些库的使用题,也是最头痛的事情,怎么让eclipse能够应用我们自己的类。


你可能感兴趣的:(s5p4418,android)