〖Android〗OK6410a的Android HAL层代码编写笔记
一、编写LED灯的Linux驱动程序代码
之所以使用存在HAL层,是为了保护对硬件驱动过程的逻辑与原理;
所以,残留在Linux驱动层的代码,只保留了基本的读写操作,而不含有关键的逻辑思维;
1. leds_hal_define.h (包含对读写寄存器的宏定义)
#define S3C6410_LEDS_HAL_WRITE_GPMPUD 1#define S3C6410_LEDS_HAL_WRITE_GPMCON 2#define S3C6410_LEDS_HAL_WRITE_GPMDAT 3#define S3C6410_LEDS_HAL_READ_GPMPUD 4#define S3C6410_LEDS_HAL_READ_GPMCON 5#define S3C6410_LEDS_HAL_READ_GPMDAT 6
2. s3c6410_leds_hal.h (包含必须的头文件及设备的定义)
#include <linux/fs.h>#include <linux/cdev.h>#include <linux/pci.h> #include <linux/uaccess.h> #include <mach/map.h> #include <mach/regs-gpio.h>#include <mach/gpio-bank-m.h>#define DEVICE_NAME "s3c6410_leds_hal"#define DEVICE_COUNT 1#define S3C6410_LEDS_MAJOR 0#define S3C6410_LEDS_MINOR 234
3. s3c6410_leds_hal.c (包含对设备文件读写操作)
#include "s3c6410_leds_hal.h"#include "leds_hal_define.h"static unsigned char mem[5]; /* 第1字节保存寄存器类型,后边4字节保存寄存器的值 */static int major = S3C6410_LEDS_MAJOR;static int minor = S3C6410_LEDS_MINOR;static dev_t dev_number;static struct class *leds_class = NULL;//字节转换int类型static int bytes_to_int(unsigned char buf[], int start){ int n=0; n = ((int)buf[start]<<24 | (int)buf[start+1]<<16 | (int)buf[start+2]<<8 |(int)buf[start+3]); return n;}//将int转换成bytesstatic void int_to_bytes(int n, unsigned char buf[], int start){ buf[start] = n >> 24; buf[start+1] = n >> 16; buf[start+2] = n >> 8; buf[start+3] = n;}//设备的写操作static ssize_t s3c6410_leds_hal_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ if (copy_from_user(mem, buf, 5)){ return -EFAULT; } else{ int gpm_type = mem[0]; switch ( gpm_type ) { case S3C6410_LEDS_HAL_WRITE_GPMCON: iowrite32(bytes_to_int(mem,1),S3C64XX_GPMCON); break; case S3C6410_LEDS_HAL_WRITE_GPMPUD: iowrite32(bytes_to_int(mem,1),S3C64XX_GPMPUD); break; case S3C6410_LEDS_HAL_WRITE_GPMDAT: iowrite32(bytes_to_int(mem,1),S3C64XX_GPMDAT); break; default: printk(DEVICE_NAME "\tnot the write type.\n"); break; } } return 5;}//设备的读操作static ssize_t s3c6410_leds_hal_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ int gpm_type = mem[0]; int gpm_value = 0; switch ( gpm_type ) { case S3C6410_LEDS_HAL_READ_GPMCON: gpm_value=ioread32(S3C64XX_GPMCON); break; case S3C6410_LEDS_HAL_READ_GPMPUD: gpm_value=ioread32(S3C64XX_GPMPUD); break; case S3C6410_LEDS_HAL_READ_GPMDAT: gpm_value=ioread32(S3C64XX_GPMDAT); break; default: printk(DEVICE_NAME "\tnot the read type.\n"); break; } int_to_bytes(gpm_value, mem, 1); /* 转换成byte数组 */ if (copy_to_user(buf, (void*)mem, 5)){ /* 把数据复制到用户空间 */ return -EFAULT; } return 5; /* 返回已读数据长度 */}static struct file_operations dev_fops={ .owner = THIS_MODULE, .read = s3c6410_leds_hal_read, .write = s3c6410_leds_hal_write};static struct cdev leds_cdev;//创建设备文件static int leds_create_device(void){ int ret = 0; int err = 0; cdev_init(&leds_cdev, &dev_fops); /* 初始化cdev成员,与dev_fops连接 */ leds_cdev.owner = THIS_MODULE; if (major>0){ /* 若大于0,选择手动分配 */ dev_number = MKDEV(major, minor); err = register_chrdev_region(dev_number, DEVICE_COUNT,DEVICE_NAME); if (err<0){ printk(KERN_WARNING "register_chrdev_region() failed\n"); return err; } } else { /* 若主设备号小等于0,使用自动分配设备号 */ err = alloc_chrdev_region(&leds_cdev.dev, 10, DEVICE_COUNT,DEVICE_NAME); if (err<0){ printk(KERN_WARNING "alloc_chrdev_region() failed\n"); return err; } major = MAJOR(leds_cdev.dev); /* 及时保存主设备号 */ minor = MINOR(leds_cdev.dev); /* 及时保存次设备号*/ dev_number = leds_cdev.dev; /* 保存设备号信息*/ } ret = cdev_add(&leds_cdev, dev_number, DEVICE_COUNT); /* 添加驱动设备 */ leds_class = class_create(THIS_MODULE, DEVICE_NAME); /* 创建类型*/ device_create(leds_class, NULL, dev_number, NULL, DEVICE_NAME); /* 创建设备 */ return ret;}//驱动初始化函数static int __init leds_init(void){ int ret; ret = leds_create_device(); printk(DEVICE_NAME "\tintialized\n"); return ret;}//驱动卸载函数static void __exit leds_destroy_device(void){ device_destroy(leds_class, dev_number); if (leds_class){ class_destroy(leds_class); } unregister_chrdev_region(dev_number, DEVICE_COUNT); return;}module_init(leds_create_device);module_exit(leds_destroy_device);MODULE_AUTHOR("linkscue");MODULE_DESCRIPTION("leds driver witten via hal");MODULE_ALIAS("leds driver hal");MODULE_LICENSE("GPL");
4. Makefile
obj-m := s3c6410_leds_hal.oPWD := $(shell pwd)#CROSS_COMPILE ?= arm-none-linux-gnueabi-#CC := $(CROSS_COMPILE)gcc#CFLAGS += -staticKPATH := /media/Source/Forlinx/android2.3_kernel_v1.01all: make -C $(KPATH) M=$(PWD) modulesclean: rm -rf *.o *.ko *.mod.c Module.* modules.*
在编写好了Linux驱动程序代码之后,把make生成的s3c6410_leds_hal.ko上传至OK6410a并使用insmod安装,继续下一步。
二、编写LED灯的Android HAL层代码
这一层使用的协议并不是GPL,不被强制地开放源代码,这一层,包含了对LED控制过程中的所有的逻辑思维;
在本例子当中,这LED灯的初始化寄存器操作的逻辑,打开或者关闭LED灯时应当向寄存器写入什么数据等等。
1. leds_hal.h (包含必须的头文件及宏定义)
#include <hardware/hardware.h>#include <fcntl.h>#include <cutils/log.h>struct led_module_t{ struct hw_module_t hw_module;};struct led_control_device_t{ struct hw_device_t hw_device; int (*set_on)(struct led_control_device_t *dev, int32_t led); int (*set_off)(struct led_control_device_t *dev, int32_t led);};#define LED_HARDWARE_MODULE_ID "led_hal"/*----------------------------------------------------------------------------- * 编写HAL模块需要的3个非常重要的结构体: * hw_module_t * hw_device_t * hw_module_methods_t * 位置:hardware/libhardware/include/hardware/hardware.h *-----------------------------------------------------------------------------*/
2. leds_hal.c (包含对灯控制的所有逻辑思想与操作)
#include "leds_hal.h"#include "leds_hal_define.h"int dev_file = 0;/*----------------------------------------------------------------------------- * 控制LED开关的通用函数 * led表示灯的序号,从0开始 * on_off表示开(1)、关(0) *-----------------------------------------------------------------------------*/int led_on_off(struct led_control_device_t *dev, int32_t led, int32_t on_off){ if (led>0 && led<3){ if (on_off == 1){ LOGI("LED stub: set %d on", led); } else { LOGI("LED stub: set %d off", led); } unsigned char buf[5]; buf[0] = S3C6410_LEDS_HAL_READ_GPMDAT; /* 准备要读取 */ write(dev_file, buf, 5); read(dev_file, buf, 5); buf[0] = S3C6410_LEDS_HAL_WRITE_GPMDAT; /* 准备要写入 */ switch ( led ) { case 0: if (on_off == 1){ buf[4]&=0xFE; /* 1111,1110,GPMDAT最后一位是0 */ } else if (on_off == 0){ buf[4]|=0x1; /* 0000,0001,GPMDAT最后一位是1 */ } break; case 2: if (on_off == 1){ buf[4]&=0xFD; /* 1111,1101 */ } else if (on_off == 0){ buf[4]|=0x2; /* 0000,0010 */ } break; case 3: if (on_off == 1){ buf[4]&=0xFB; /* 1111,1011 */ } else if (on_off == 0){ buf[4]|=0x4; /* 0000,0100 */ } break; case 4: if (on_off == 1){ buf[4]&=0xF7; /* 1111,0111 */ } else if (on_off == 0){ buf[4]|=0x8; /* 0000,1000 */ } break; default: break; } write(dev_file, buf, 5); } else { LOGI("LED Stub: set led %d on error,no this led", led); } return 0;}/*----------------------------------------------------------------------------- * 打开指定的LED灯 *-----------------------------------------------------------------------------*/int led_on(struct led_control_device_t *dev, int32_t led){ return led_on_off(dev, led, 1);}/*----------------------------------------------------------------------------- * 关闭指定的LED灯 *-----------------------------------------------------------------------------*/int led_off(struct led_control_device_t *dev, int32_t led){ return led_on_off(dev, led, 0);}/*----------------------------------------------------------------------------- * 关闭设备函数,HAL模块的事件函数之一 *-----------------------------------------------------------------------------*/int led_device_close(struct hw_device_t *device){ //强行转换成 led_control_device_t struct led_control_device_t *ctx = (struct led_control_device_t*)device; if (ctx){ free(ctx); /* 释放设备 */ } close(dev_file); return 0;}/*----------------------------------------------------------------------------- * 初始化GPMCON,GPMPUD寄存器 *-----------------------------------------------------------------------------*/static void leds_init_gpm(){ int tmp = 0; unsigned char buf[5]; //初始化寄存器GPMDAT buf[0] = S3C6410_LEDS_HAL_READ_GPMCON; write(dev_file, buf, 5); read(dev_file, buf, 5); buf[3] |= 0x11; /* 0001,0001 */ buf[4] |= 0x11; /* 0001,0001 */ buf[0] = S3C6410_LEDS_HAL_WRITE_GPMCON; write(dev_file, buf, 5); //初始化寄存器GPMPUD buf[0] = S3C6410_LEDS_HAL_READ_GPMPUD; write(dev_file, buf, 5); read(dev_file, buf, 5); buf[4] |= 0xAA; buf[0] = S3C6410_LEDS_HAL_WRITE_GPMPUD; write(dev_file, buf, 5);}/*----------------------------------------------------------------------------- * 打开设备的函数 *-----------------------------------------------------------------------------*/static int led_device_open(const struct hw_module_t *module, const char *name, struct hw_device_t **device){ struct led_control_device_t *dev; dev = (struct led_control_device_t *)malloc(sizeof(*dev)); memset(dev, 0, sizeof(*dev)); /* 清理内存空间 */ dev->hw_device.tag = HARDWARE_DEVICE_TAG; /* HAL设备的标志 */ dev->hw_device.version = 0; /* HAL设备的版本号 */ dev->hw_device.module = (struct hw_module_t *)module; /* HAL模块的hw_module_t结构体 */ dev->hw_device.close = led_device_close; /* 关闭设备的函数 */ dev->set_on = led_on; /* HAL的接口函数 */ dev->set_off = led_off; /* HAL的接口函数 */ *device = (hw_device_t *)dev; /* 可替换为*device = &dev->hw_device */ dev_file = open("/dev/s3c6410_leds_hal", O_RDWR); if (dev_file < 0){ LOGI("LED Stub: open /dev/s3c6410_leds_hal fail."); } else { LOGI("LED Stub: open /dev/s3c6410_leds_hal success ."); } leds_init_gpm; /* 初始化寄存器 */ return 0;}/*----------------------------------------------------------------------------- * 初始化hw_module_method_t结构体的open成员变量 *-----------------------------------------------------------------------------*/static struct hw_module_methods_t led_module_methods = { open: led_device_open};/*----------------------------------------------------------------------------- * 初始化描述HAL模块信息的结构体,此结构体变量名必须是HAL_MODULE_INFO_SYM *-----------------------------------------------------------------------------*/struct led_module_t HAL_MODULE_INFO_SYM = { hw_module: { tag:HARDWARE_MODULE_TAG, /* 初始化模块标志 */ version_major:1, /* 模块的主版本号 */ version_minor:0, /* 模块的次版本情 */ id:LED_HARDWARE_MODULE_ID, /* 初始化模块的ID,通过ID找到此模块 */ name:"Sample LED HAL Stub", /* 初始化模块的名称 */ author:"linkscue", /* 初始化模块的作者 */ methods:&led_module_methods, /* 初始化模块的open函数指针 */ }};
3. Android.mk
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_PRELINK_MODULE := falseLOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hwLOCAL_SHARED_LIBRARIES := liblogLOCAL_SRC_FILES := leds_hal.cLOCAL_MODULE :=led_hal.defaultLOCAL_MODULE_TAGS := eng include $(BUILD_SHARED_LIBRARY)
在Android源代码目录下,创建一个 hardware/leds_hal(名字和路径可以随意,当然也可以放在device目录上对应的设备上);
把源代码Android.mk leds_hal.c leds_hal_define.h leds_hal.h 复制至hardware/leds_hal 目录下(可通过ln -s链接);
在配置好编译环境之后,通过 mmmhardware/leds_hal 生成HAL共享库文件led_hal.default.so(命名与头文件的ID有关);
三、编写调用HAL模块的Service
此模块中一个非常重要的函数是 hw_get_module ,它通过在leds_hal.h中定义的LED_HARDWARE_MODULE_ID找到LED的HAL模块,并取得led_module_t结构体。通过 led_module_t.hw_module.methods.open 函数初始化LED驱动,并通过 open 函数返回 led_control_device_t 结构体,在此结构体中含有我们需要的对LED操作的set_on和set_off函数指针。这一段由于使用了C++和JNI(NDK)编程,阅读起来相对有点晦涩难懂,不过编程量相对也比较少,只需要两文件即可。
1.LedHalService.cpp (主要的源代码文件)
#include <stdlib.h>#include <stdio.h>#include <string.h>#include <unistd.h>#include <assert.h> #include <jni.h>#include <leds_hal.h>struct led_control_device_t *led_hal_device = NULL;/*----------------------------------------------------------------------------- * JNI函数,打开指定的LED *-----------------------------------------------------------------------------*/static jboolean led_setOn(JNIEnv *env, jobject thiz, jint led){ LOGI("Led HAL JNI: led_setOn() is invoked."); if (led_hal_device == NULL){ LOGI("LED HAL JNI: led_hal_device was not fetched correcttly."); return -1; } else { return led_hal_device->set_on(led_hal_device, led); }}/*----------------------------------------------------------------------------- * JNI函数,关闭指定的LED *-----------------------------------------------------------------------------*/static jboolean led_setOff(JNIEnv *env, jobject thiz, jint led){ LOGI("Led HAL JNI: led_setOff() is invoked."); if (led_hal_device == NULL){ LOGI("LED HAL JNI: led_hal_device was not fetched correcttly."); return -1; } else { return led_hal_device->set_off(led_hal_device, led); }}static inline int led_control_open(const struct hw_module_t *module, struct led_control_device_t **device){ /* * 调用led_module_t.hw_module.methods.open进行一系列的初始化工作 * open函数会获取led_control_device_t结构体 */ return module->methods->open(module, LED_HARDWARE_MODULE_ID, (struct hw_device_t**) device);}//JNI函数,此方法通过LED_HARDWARE_MODULE_ID找到HAL模块static jboolean led_init(JNIEnv *env, jclass clazz){ led_module_t *module; LOGE("************start find hal************"); LOGE("LED_HARDWARE_MODULE_ID"); if (hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t **) &module) == 0){ LOGI("LedService JNI: LED Stub found."); if (led_control_open(&module->hw_module, &led_hal_device) == 0){ LOGI("LedService JNI: Got LED operations."); return 0; } } LOGE("LedService JNI: Got LED operations failed."); return -1;}//定义JNI函数的映射static const JNINativeMethod methods[] = { { "_init", "()Z", (void*) led_init }, { "_seton", "(I)Z" ,(void *) led_setOn }, { "_setoff", "(I)Z", (void *) led_setOff },};/*----------------------------------------------------------------------------- * Java数据类型在JNI中的描述符: * boolean Z * byte B * char C * short S * int I * long J * float F * double D *-----------------------------------------------------------------------------*//*----------------------------------------------------------------------------- * Java复杂类型在JNI的描述符: * String "Ljava/lang/String;" * int[] "[I" * Object[] "[Ljava/lang/Object;" * 小技巧: * [ 表示数组,比如byte[]在JNI中是"[B" * V 表示未返回任何值即void,比如void method(int a)的返回值是"(I)V" *-----------------------------------------------------------------------------*///将JNI程序库与Java类绑定int register_led_hal_jni(JNIEnv *env){ //必须由此类调用当前的JNI程序库 static const char *const kClassName = "mobile/android/leds/hal/service/LedHalService"; jclass clazz; clazz = env->FindClass(kClassName); /* 获取LedHalService类的jclass对象 */ if (clazz == NULL){ LOGE("Can't find class %s", kClassName); return -1; } /* * 将此函数注册至LedHalService类当中的一个方法 * sizeof(methods) / sizeof(methods[0]) 用于计算methods的长度 */ if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) != JNI_OK){ LOGE("Failed register methods for %s \n", kClassName); return -1; } return 0;}//自动调用JNI_OnLoad函数,用于初始化JNI模块jint JNI_OnLoad(JavaVM *vm, void *reserved){ JNIEnv *env = NULL; jint result = -1; /* * JNI_VERSION_1_4表明只能在JSE1.4版本以上才能使用 */ if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK){ LOGE("GetEnv failed!"); return result; } register_led_hal_jni(env); return JNI_VERSION_1_4;}
2. Android.mk
#Android.mkLOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := engLOCAL_MODULE := led_hal_jni#指定Service程序库的存放路径(指编译出来的.so存放的地方)LOCAL_MODULE_PATH := /home/scue/work/androidexplorer/led_hal/leds_hal/leds_hal_jniLOCAL_SRC_FILES := \ LedHalService.cpp#指定共享库的位置LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ libcutils \ libhardware \ libhardware_legacy \ libnativehelper \ libsystem_server \ libutils \ libui \ libsurfaceflinger_client #指定头文件的位置LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ hardware/leds_hal#指定预链接模式LOCAL_PRELINK_MODULE := falseinclude $(BUILD_SHARED_LIBRARY)
把 Android.mk LedHalService.cpp放至同一目录(/home/scue/work/androidexplorer/led_hal/leds_hal/leds_hal_jni);
配置好Android编译环境之后,使用命令 mmm -B/home/scue/work/androidexplorer/led_hal/leds_hal/leds_hal_jni 编译;
mmm 表示编译指定目录的中的内容
-B 表示强制编译其中的内容(即使源代码.cpp文件没有任何改变)
小技巧:
可以不用把/home/scue/work/androidexplorer/led_hal/leds_hal/leds_hal_jni移至Android源代码目录下亦可编译
(未完待续)..
注:本文大部分代码摘取自某本书的源程序代码。
更多相关文章
- Android App模块化及工程扩展
- android 4.0横屏重复调用onCreate()函数
- Android中除了利用VideoView、Mediaplayer播放视频文件外,还可以
- Android Browser学习八 书签历史模块: 历史UI的实现
- 为Android扩展一个硬件模块需要做两件事:
- Android利用系统提供的函数或常量判断版本大小
- 使用Kotlin开发Android 扩展函数(Extensions)
- 如何掌握Android-Camera模块
- 【Android 多模块构建】如何启动另外一个module中的Activity