Android HAL编程

实验平台:Linux-3.14 + Android 4.4

本文以led灯为例,led灯使用gpio口来控制,输出高电平点亮led,driver使用linux内核自带的leds-gpio.c驱动,那么对于对于我们来说,只需要在dts文件(或者其他地方)中配置一下就可以了,配置示例如下:

gpioleds {
	compatible = "gpio-leds";
	leds_red {
		label = "red";
		default-state = "off";
		gpios = <&mcu_gpio n 0>;
	};
};
要使用leds-gpio.c这个驱动呢,首先要将compatible属性赋值为"gpio-leds",然后是子节点,子节点可以有多个(这里以充电指示灯红灯为例),在子节点中lable属性的值将在/sys/class/leds目录下创建一个名为red的子目录,而default-state属性表示led灯的默认状态(on或off),而在gpios属性中,mcu_gpio是和具体平台相关的,会对应一个gpio节点,n表示使用的哪一个gpio口,而最后一个值是一个标志位,表示低电平有效的标志位,如果需要低电平点亮led灯,那么这里就需要赋值为1,否则赋值0就好了,好了,led灯的driver层就介绍完了,驱动初始化完整之后,将创建一个/sys/class/leds/red这样一个目录,其中我们这里会用到一个文件是brightness,往里写1即点亮led灯,写0即关闭led灯。

led的操作相应大家都应该很清楚了,那么接下来看HAL层代码应该怎样编写,代码如下:

/* hardware/xxx/led/led.c */
#define LOG_TAG "Led"

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <cutils/log.h>

#include <hardware/led.h>

const char * const LED_FILE = "/sys/class/leds/red/brightness";

static int write_int(const char *path, int value)
{
	int fd;

	fd = open(path, O_RDWR);
	if (fd < 0) {
		ALOGE("write_int failed to open %s\n", path);
		return -errno;
	}

	char buffer[20];
	int bytes = sprintf(buffer, "%d\n", value);
	int amt = write(fd, buffer, bytes);
	close(fd);

	return amt == -1 ? -errno : 0;
}

static int led_device_close(struct hw_device_t *dev)
{
	struct led_device_t *led_dev = (struct led_device_t *)dev;

	ALOGI("%s\n", __func__);

	if (led_dev) {
		free(led_dev);
	}
	return 0;
}

static int led_set_on(struct led_device_t *dev, int enable)
{
	ALOGI("%s\n", __func__);

	if (enable) {	/* turn on led */
		write_int(LED_FILE, 1);
	}
	return 0;
}

static int led_set_off(struct led_device_t *dev, int enable)
{
	ALOGI("%s\n", __func__);

	if (enable) {	/* turn off led */
		write_int(LED_FILE, 0);
	}
	return 0;
}

static int led_device_open(const struct hw_module_t *module, const char *name,
		struct hw_device_t **device)
{
	struct led_device_t *dev;

	ALOGI("%s\n", __func__);

	dev = (struct led_device_t *)malloc(sizeof(struct led_device_t));
	memset(dev, 0, sizeof(struct led_device_t));

	dev->common.tag = HARDWARE_DEVICE_TAG;
	dev->common.version = 0;
	dev->common.module = (struct hw_module_t *)module;
	dev->common.close = led_device_close;

	dev->set_on = led_set_on;
	dev->set_off = led_set_off;

	*device = &dev->common;

	return 0;
}

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,
		.dso = NULL,
		.name = "Led Module",
		.author = "Tracy",
		.methods = &led_module_methods,
	},
};
操作都是很简单的,跟前面描述的一样,往brightness文件中写1即打开led灯,写0即关闭led灯,这里需要注意一点的是HAL_MODULE_INFO_SYM前面不要加const关键字,大家可能在网上会看到有的HAL层代码这里有个const关键字,但在Android 4.4版本上是不行的,其它版本也许加上可能没有问题。

然后其中用到了一个led.h这样一个头文件,代码如下:

/* hardware/libhardware/include/hardware/led.h */
#ifndef __ANDROID_HAL_LED_H
#define __ANDROID_HAL_LED_H

#include <hardware/hardware.h>

struct led_module_t {
	struct hw_module_t common;
};

struct led_device_t {
	struct hw_device_t common;
	
	int (*set_on)(struct led_device_t *dev, int enable);
	int (*set_off)(struct led_device_t *dev, int enable);
};

#define LED_HARDWARE_MODULE_ID "led"

#endif /* __ANDROID_HAL_LED_H */

Android.mk如下:

# hardware/xxx/led/Android.mk
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := led.c

LOCAL_MODULE_TAGS := optional

LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw

LOCAL_SHARED_LIBRARIES := liblog

LOCAL_MODULE := led.default

include $(BUILD_SHARED_LIBRARY)
编译完成之后将在out目录中的system/lib/hw/中生成一个名为led.default.so的一个so文件(使用mm方式编译的,如果需要在编译系统时就编译这里的代码,需要修改相应的的地方,这里就不去描述了)。

HAL层代码介绍完毕,我们都知道Java是不能直接调用c代码的,所以这里需要编写JNI,代码如下:

/* frameworks/base/services/jni/com_android_server_LedService.cpp */
#define LOG_TAG "Led"

#include <jni.h>
#include <stdlib.h>
#include <JNIHelp.h>
#include <utils/Log.h>

#include <hardware/led.h>

namespace android {

struct led_device_t *LedDevice = NULL; 

static led_device_t *get_device(hw_module_t *module, const char *name)
{
	int err;
	hw_device_t *device;

	ALOGI("%s\n", __func__);

	err = module->methods->open(module, name, &device);
	if (err == 0) {
		return (led_device_t *)device;
	}

	return NULL;
}

static jboolean init_native(JNIEnv *env, jobject obj)
{
	hw_module_t *module;

	ALOGI("%s\n", __func__);

	if (hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t **)&module) == 0) {
		LedDevice = get_device(module, LED_HARDWARE_MODULE_ID);
		if (LedDevice != NULL)
			return true;
	}

	return false;
}

static void finalize_native(JNIEnv *env, jobject obj)
{
	ALOGI("%s\n", __func__);

	if (LedDevice != NULL)
		free(LedDevice);
}

static void seton_native(JNIEnv *env, jobject obj)
{
	ALOGI("%s\n", __func__);

	if (LedDevice != NULL)
		LedDevice->set_on(LedDevice, 1);
}

static void setoff_native(JNIEnv *env, jobject obj)
{
	ALOGI("%s\n", __func__);

	if (LedDevice != NULL)
		LedDevice->set_off(LedDevice, 1);
}

static JNINativeMethod methods_table[] = {
	{ "_init", "()Z", (void *)init_native },
	{ "_finalize", "()V", (void *)finalize_native },
	{ "_set_on", "()V", (void *)seton_native },
	{ "_set_off", "()V", (void *)setoff_native },
};

int register_android_server_LedService(JNIEnv *env)
{
	return jniRegisterNativeMethods(env, "com/android/server/LedService",
		methods_table, NELEM(methods_table));
}

};
JNI代码编写好了之后,还需要调用register_android_server_LedService函数去完成JNI的注册,这里就在该目录下的onload.cpp去调用:
/* frameworks/base/services/jni/onload.cpp */
namespace android {
	/* ... */
	int register_android_server_LedService(JNIEnv *env);
	/* ... */
};

using namespace android;

extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
	/* ... */
	register_android_server_LedService(env);
	/* ... */
}
当然还需要修改对应的Android.mk文件:
LOCAL_SRC_FILES:= \
	
	com_android_server_LedService.cpp \
	onload.cpp
这部分代码编译之后将在system/lib/目录下生成libandroid_servers.so。

JNI部分也写好了,还需要framework层代码:

/* frameworks/base/services/java/com/android/server/LedService.java */
package com.android.server;

import android.util.Log;

public class LedService {
	private static final String TAG = "Led";

	static {
		System.load("/system/lib/libandroid_servers.so");
	}

	public LedService() {
		Log.i(TAG, "_init()");
		_init();
	}

	public void finalize() throws Throwable {
		Log.i(TAG, "_finalize()");
		_finalize();
		super.finalize();
	}

	public void set_on() {
		Log.i(TAG, "_set_on()");
		_set_on();
	}

	public void set_off() {
		Log.i(TAG, "_set_off()");
		_set_off();
	}

	private static native boolean _init();
	private static native void _finalize();

	private static native void _set_on();
	private static native void _set_off();
}
注意这里需要调用Sytem.load去加载native库,否则会出现找不到native方法的问题。

最后是应用层,activity代码如下:

/* package/apps/LedTest/src/com/example/ledtest/MainActivity.java */
package com.example.ledtest;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

import com.android.server.LedService;

public class MainActivity extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        final LedService led_service = new LedService();
        
        final TextView led_status = (TextView)this.findViewById(R.id.led_status);
        
        Button led_on = (Button)this.findViewById(R.id.led_on);
        led_on.setOnClickListener(new Button.OnClickListener() {
        	public void onClick(View v) {
        		led_status.setText("status: on");
        		led_service.set_on();
        	}
        });
        
        Button led_off = (Button)this.findViewById(R.id.led_off);
        led_off.setOnClickListener(new Button.OnClickListener() {
        	public void onClick(View v) {
        		led_status.setText("status: off");
        		led_service.set_off();
        	}
        });
    }
}
app层有简单的提供了三个控件,静态文本显示led灯的状态消息,两个按钮用于打开、关闭led灯,其他地方都没有特别需要注意的,只是要稍微注意一下Android.mk文件。

前面忘记说明framework层代码编译之后将生成services.jar这样一个jar包,所以在编写Android.mk文件时需要在LOCAL_JAVA_LIBRARIES指定加载services这个jar包,完整的Android.mk在下面:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res

LOCAL_PACKAGE_NAME := LedTest

LOCAL_SDK_VERSION := current

LOCAL_JAVA_LIBRARIES := services

#LOCAL_JNI_SHARED_LIBRARIES := libjni_mosaic libjni_tinyplanet

include $(BUILD_PACKAGE)
注意在app层是直接通过调用framework层的代码,所以在app层实例化了一个类LedService,好了代码就介绍完了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值