一、编写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];
- static int major = S3C6410_LEDS_MAJOR;
- static int minor = S3C6410_LEDS_MINOR;
- static dev_t dev_number;
- static struct class *leds_class = NULL;
-
-
- 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;
- }
-
-
- static 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);
- 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);
- leds_cdev.owner = THIS_MODULE;
- if (major>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 {
- 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.o
- PWD := $(shell pwd)
- #CROSS_COMPILE ?= arm-none-linux-gnueabi-
- #CC := $(CROSS_COMPILE)gcc
- #CFLAGS += -static
- KPATH := /media/Source/Forlinx/android2.3_kernel_v1.01
- all:
- make -C $(KPATH) M=$(PWD) modules
- clean:
- 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"
-
-
-
-
-
-
-
-
-
2. leds_hal.c (包含对灯控制的所有逻辑思想与操作)
- #include "leds_hal.h"
- #include "leds_hal_define.h"
-
- int dev_file = 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;
- }
- else if (on_off == 0){
- buf[4]|=0x1;
- }
- break;
-
- case 2:
- if (on_off == 1){
- buf[4]&=0xFD;
- }
- else if (on_off == 0){
- buf[4]|=0x2;
- }
- break;
-
- case 3:
- if (on_off == 1){
- buf[4]&=0xFB;
- }
- else if (on_off == 0){
- buf[4]|=0x4;
- }
- break;
-
- case 4:
- if (on_off == 1){
- buf[4]&=0xF7;
- }
- else if (on_off == 0){
- buf[4]|=0x8;
- }
- break;
-
- default:
- break;
- }
- write(dev_file, buf, 5);
- }
- else {
- LOGI("LED Stub: set led %d on error,no this led", led);
- }
- return 0;
- }
-
-
-
-
- int led_on(struct led_control_device_t *dev, int32_t led){
- return led_on_off(dev, led, 1);
- }
-
-
-
-
- int led_off(struct led_control_device_t *dev, int32_t led){
- return led_on_off(dev, led, 0);
- }
-
-
-
-
- int led_device_close(struct hw_device_t *device){
-
- struct led_control_device_t *ctx = (struct led_control_device_t*)device;
- if (ctx){
- free(ctx);
- }
- close(dev_file);
- return 0;
- }
-
-
-
-
-
- static void leds_init_gpm(){
- int tmp = 0;
- unsigned char buf[5];
-
-
- buf[0] = S3C6410_LEDS_HAL_READ_GPMCON;
- write(dev_file, buf, 5);
- read(dev_file, buf, 5);
- buf[3] |= 0x11;
- buf[4] |= 0x11;
- buf[0] = S3C6410_LEDS_HAL_WRITE_GPMCON;
- write(dev_file, buf, 5);
-
-
- 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;
- dev->hw_device.version = 0;
- dev->hw_device.module = (struct hw_module_t *)module;
-
- dev->hw_device.close = led_device_close;
- dev->set_on = led_on;
- dev->set_off = led_off;
- *device = (hw_device_t *)dev;
- 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;
- }
-
-
-
-
- static struct hw_module_methods_t led_module_methods = {
- open: led_device_open
- };
-
-
-
-
- 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,
- name:"Sample LED HAL Stub",
- author:"linkscue",
- methods:&led_module_methods,
- }
- };
3. Android.mk
在Android源代码目录下,创建一个 hardware/leds_hal(名字和路径可以随意,当然也可以放在device目录上对应的设备上);
把源代码 Android.mk leds_hal.c leds_hal_define.h leds_hal.h 复制至 hardware/leds_hal 目录下(可通过ln -s链接);
在配置好编译环境之后,通过 mmm hardware/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;
-
-
-
-
- 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);
- }
- }
-
-
-
-
- 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){
-
-
-
-
- return module->methods->open(module, LED_HARDWARE_MODULE_ID,
- (struct hw_device_t**) device);
- }
-
-
- 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;
- }
-
-
- static const JNINativeMethod methods[] = {
- { "_init", "()Z", (void*) led_init },
- { "_seton", "(I)Z" ,(void *) led_setOn },
- { "_setoff", "(I)Z", (void *) led_setOff },
- };
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int register_led_hal_jni(JNIEnv *env){
-
- static const char *const kClassName = "mobile/android/leds/hal/service/LedHalService";
- jclass clazz;
- clazz = env->FindClass(kClassName);
- if (clazz == NULL){
- LOGE("Can't find class %s", kClassName);
- return -1;
- }
-
-
-
-
-
- if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0]))
- != JNI_OK){
- LOGE("Failed register methods for %s \n", kClassName);
- return -1;
- }
- return 0;
- }
-
-
- jint JNI_OnLoad(JavaVM *vm, void *reserved){
- JNIEnv *env = NULL;
- jint result = -1;
-
-
-
- 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.mk
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE_TAGS := eng
- LOCAL_MODULE := led_hal_jni
-
- #指定Service程序库的存放路径(指编译出来的.so存放的地方)
- LOCAL_MODULE_PATH := /home/scue/work/androidexplorer/led_hal/leds_hal/leds_hal_jni
- LOCAL_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 := false
-
- include $(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源代码目录下亦可编译
(未完待续)..
注:本文大部分代码摘取自某本书的源程序代码。
(scue) |