zoukankan      html  css  js  c++  java
  • 〖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转换成bytes
    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 "	not the write type.
    ");
                    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 "	not the read type.
    ");
                    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
    ");
                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
    ");
                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 "	intialized
    ");
        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"
    
    
    /*-----------------------------------------------------------------------------
     *  编写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 := false
    LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
    LOCAL_SHARED_LIBRARIES := liblog
    LOCAL_SRC_FILES := leds_hal.c
    LOCAL_MODULE :=led_hal.default
    LOCAL_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链接);

      在配置好编译环境之后,通过 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;
    
    /*-----------------------------------------------------------------------------
     *  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 
    ", 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.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源代码目录下亦可编译

    (未完待续)..

    注:本文大部分代码摘取自某本书的源程序代码。

  • 相关阅读:
    实现可折叠的列表
    ajax知识点补充
    Ajax 完整教程
    从页面左边飞入页面动画效果
    文字闪烁
    点击按钮之后等待60秒
    [SCOI2016]萌萌哒
    [HNOI2011]卡农
    [NOI2015]寿司晚宴
    【bzoj 3489】A simple rmq problem
  • 原文地址:https://www.cnblogs.com/scue/p/3482772.html
Copyright © 2011-2022 走看看