一、编写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源代码目录下亦可编译

(未完待续)..

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

更多相关文章

  1. Android App模块化及工程扩展
  2. android 4.0横屏重复调用onCreate()函数
  3. Android中除了利用VideoView、Mediaplayer播放视频文件外,还可以
  4. Android Browser学习八 书签历史模块: 历史UI的实现
  5. 为Android扩展一个硬件模块需要做两件事:
  6. Android利用系统提供的函数或常量判断版本大小
  7. 使用Kotlin开发Android 扩展函数(Extensions)
  8. 如何掌握Android-Camera模块
  9. 【Android 多模块构建】如何启动另外一个module中的Activity

随机推荐

  1. mysql累积聚合原理与用法实例分析
  2. mysql连续聚合原理与用法实例分析
  3. 解决Win10系统安装MySQL8.0遇到的问题
  4. mysql滑动订单问题原理与解决方法实例分
  5. MySQL流程函数常见用法实例分析
  6. mysql存储过程之case语句用法实例详解
  7. mysql存储过程之游标(DECLARE)原理与用法详
  8. mysql存储过程之if语句用法实例详解
  9. MySQL多表连接的入门实例教程
  10. MySql5.x升级MySql8.x的方法步骤