• 自己动手写最简单的Android驱动---LED驱动的编写【转】


    本文转载自:http://blog.csdn.net/k_linux_man/article/details/7023824

    转载注明出处,作者:K_Linux_Man, 薛凯 山东中医药大学,给文章内容引入个人毕业设计。

     开发平台:farsight s5pc100-a

    内核:linux2.6.29

    环境搭配:有博文介绍

    开发环境:Ubuntu 、Eclipse

    首先强调一下要点:

    1.编写Android驱动时,首先先要完成Linux驱动,因为android驱动其实是在linux驱动基础之上完成了HAL层(硬件抽象层),如果想要测试的话,自己也要编写Java程序来测试你的驱动。

    2.android的根文件系统是eclair_2.1版本。我会上传做好的根文件系统提供大家。这里要说的是,android底层内核还是linux的内核,只是进行了一些裁剪。做好的linux内核镜像,这个我也会上传给大家。android自己做了一套根文件系统,这才是android自己做的东西。android事实上只是做了一套根文件系统罢了。

    假设linux驱动大家都已经做好了。我板子上有四个灯,通过ioctl控制四个灯,给定不同的参数,点亮不同的灯。

    linux驱动代码因平台不同而有所不同,这就不黏代码了。

    这是我测试linux驱动编写的驱动,代码如下:

    [cpp] view plain copy
     
    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <unistd.h>  
    4. #include <fcntl.h>  
    5. #include <string.h>  
    6. #include <sys/types.h>  
    7. #include <sys/stat.h>  
    8. #include <sys/ioctl.h>  
    9. #define LED_ON _IO ('k',1)  
    10. #define LED_OFF _IO ('k',2)  
    11. int main()  
    12. {  
    13.     int i = 0;  
    14.     int dev_fd;  
    15.     dev_fd = open("/dev/led",O_RDWR);  
    16.     if ( dev_fd == -1 ) {  
    17.         printf("Cann't open file /dev/led ");  
    18.         exit(1);  
    19.     }  
    20.     while(1)  
    21.     {  
    22.      ioctl(dev_fd,LED_ON,1);  
    23.      sleep(1);  
    24.      ioctl(dev_fd,LED_OFF,1);  
    25.      sleep(1);  
    26.      ioctl(dev_fd,LED_ON,2);  
    27.      sleep(1);  
    28.      ioctl(dev_fd,LED_OFF,2);  
    29.      sleep(1);  
    30.      ioctl(dev_fd,LED_ON,3);  
    31.      sleep(1);  
    32.      ioctl(dev_fd,LED_OFF,3);  
    33.      sleep(1);  
    34.      ioctl(dev_fd,LED_ON,4);  
    35.      sleep(1);  
    36.      ioctl(dev_fd,LED_OFF,4);  
    37.      sleep(1);  
    38.   
    39.     }  
    40.     return 0;  
    41. }  

    下面开始把linux驱动封装成android驱动。

    首先介绍一下android驱动用到的三个重要的结构体,

    struct hw_module_t;

    struct hw_device_t;

    struct hw_module_methods_t;

    android源码里面结构体的声明

    [cpp] view plain copy
     
    1. typedef struct hw_module_t {  
    2.   
    3. uint 32_t   tag;  
    4.   
    5. uint16_t    version_major;  
    6.   
    7. uint16_t    version_minor;  
    8.   
    9. const char *id;  
    10.   
    11. const char *name;  
    12.   
    13. const char *author;  
    14.   
    15. const hw_module_methods_t  *methods;  
    16.   
    17. void* dso;  
    18.   
    19. uint32_t reserved[32-7];  
    20.   
    21. } hw_module_t;  


     

    [cpp] view plain copy
     
    1. typedef struct hw_device_t {  
    2.   
    3. uint32_t tag;  
    4.   
    5. uint32_t version;  
    6.   
    7. struct hw_module_t* module;  
    8.   
    9. uint32_t reserved[12];  
    10.   
    11. int (*close) (struct hw_device_t  *device);  
    12.   
    13. }hw_device_t;  


     

    [cpp] view plain copy
     
    1. typedef struct hw_module_methods_t {  
    2.   
    3.  int (*open) (const struct hw_module_t *module, const char *id,  
    4.   
    5.                       struct hw_device_t **device);  
    6.   
    7. } hw_module_methods_t;  


    我们经常会用到这三个结构体。

    android驱动目录结构:

    led

       |--- hal

       |       |----jni

       |               |----- Android.mk

       |               |----com_farsgiht_server_ledServer.cpp

       |       |----stub

       |                 |---- include

       |                 |             |-----led.h

       |                 |-----module

       |                               |-----Android.mk

       |                               |-----led.c

       |--- linux_drv

    首先我们要编写一个stub(代理),代理的意思是,针对你所特有的设备,你找一个代理人就可以帮你完成,它是操作linux驱动的第一层。

    编写头文件,名字led.h

    代码如下;

    [cpp] view plain copy
     
    1. #include <hardware/hardware.h>  
    2. #include <fcntl.h>  
    3. #include <errno.h>  
    4. #include <cutils/log.h>  
    5. #include <cutils/atomic.h>  
    6.   
    7.   
    8. #define LED_HARDWARE_MODULE_ID "led"  
    9.   
    10.   
    11. struct led_module_t {  
    12.     struct hw_module_t common;  
    13. };  
    14.   
    15. struct led_control_device_t {  
    16.     struct hw_device_t common;  
    17.   
    18.     int (*set_on) (struct led_control_device_t *dev, int arg);  
    19.     int (*set_off)(struct led_control_device_t *dev, int arg);  
    20. };  
    21.   
    22.   
    23. struct led_control_context_t {  
    24.     struct led_control_device_t device;  
    25. };  


    struct hw_module_t  sturct hw_device_t  这两个结构体不能直接使用,所以进行了一下封装(继承)。

    led_module_t 继承 hw_module_t

    led_control_device_t 继承 hw_device_t

    led_control_context_t 继承 led_control_device_t

    在led_control_device_t 结构体有函数指针的声明,因为后面代码中会给这些函数指针赋值

    编写led.c

    代码如下:

    [cpp] view plain copy
     
    1. #define LOG_TAG "LedStub"  
    2. #include <hardware/hardware.h>  
    3. #include <fcntl.h>  
    4. #include <errno.h>  
    5. #include <cutils/log.h>  
    6. #include <cutils/atomic.h>  
    7. #include <sys/ioctl.h>  
    8. #include "../include/led.h"  
    9.   
    10.   
    11. #define LED_ON  _IO ('k',1)  
    12. #define LED_OFF     _IO ('k',2)  
    13.   
    14. int fd;  
    15.   
    16. static int led_set_on(struct led_control_device_t *dev, int arg)  
    17. {  
    18.     LOGI("led_set_on");  
    19.     ioctl(fd, LED_ON, arg);  
    20.     return 0;  
    21. }  
    22.   
    23. static int led_set_off(struct led_control_device_t *dev, int arg)  
    24. {  
    25.     LOGI("led_set_off");  
    26.     ioctl(fd, LED_OFF, arg);  
    27.     return 0;  
    28. }  
    29.   
    30. static int led_device_close(struct hw_device_t *device)  
    31. {  
    32.     struct led_control_context_t *context = (struct led_control_context_t *)device;  
    33.     if(context) free(context);  
    34.     close(fd);  
    35.     return 0;  
    36. }  
    37.   
    38.   
    39. static int led_device_open(const struct hw_module_t *module, const char *name,   
    40.     struct hw_device_t **device)  
    41. {  
    42.     struct led_control_context_t *context;  
    43.     LOGD("led_device_open");  
    44.     context = (struct led_control_context_t *)malloc(sizeof(*context));  
    45.     memset(context, 0, sizeof(*context));  
    46.   
    47.     context->device.common.tag = HARDWARE_DEVICE_TAG;  
    48.     context->device.common.version = 0;  
    49.     context->device.common.module= module;  
    50.     context->device.common.close = led_device_close;  
    51.   
    52.     context->device.set_on = led_set_on;  
    53.     context->device.set_off = led_set_off;  
    54.       
    55.     *device = (struct hw_device_t *)&(context->device);  
    56.   
    57.     if((fd = open("/dev/led",O_RDWR)) == -1)  
    58.     {  
    59.         LOGI("ERROR: open");  
    60.     }else {  
    61.         LOGI("open led device ok ");  
    62.     }  
    63.   
    64.     return 0;  
    65. }  
    66.   
    67. static struct hw_module_methods_t led_module_methods = {  
    68. open:led_device_open  
    69. };  
    70.   
    71.   
    72. const struct led_module_t HAL_MODULE_INFO_SYM = {  
    73. common:{  
    74. tag: HARDWARE_MODULE_TAG,  
    75.       version_major:1,  
    76.       version_minor:0,  
    77.       id:LED_HARDWARE_MODULE_ID,  
    78.       name:"led_stub",  
    79.       author:"K_Linux_Man",  
    80.       methods: &led_module_methods,  
    81.          },  
    82. };  


    首先先看 struct led_module_t HAL_MODULE_INFO_SYM。这个结构体的名字必须是这个名字,否则系统无法找到led_module_t这个结构体。

    然后对led_module_t 里的成员hw_module_t结构体赋值。最关键的为id和methods两个成员的赋值,id必须要赋值,因为后面有个函数要找到hw_module_t就是通过id号去找的。 methods被赋值之后,上层的jni才能去调用。

    接着看methods 结构体里的成员就一个,open函数指针,对这个函数指针进行了赋值,赋了led_device_open函数,这个函数实现的主要就是分配led_control_context_t结构体空间,并对成员进行赋值。注意hw_device_t 里的成员module、close必须赋值。

    函数指针赋值:

    context->device.set_on = led_set_on;

    context->device.set_off = led_set_off;

    下面这句话的用意是,传进来的device指针赋予新的值,只要调用这个函数,传进来的二级指针所指向的一级指针就有值了(二级指针改变了一级指针的指向,你可以看我写的 int*p 和 int **p 博文)。

    *device = (struct hw_device_t *)&(context->device);

    接着就是打开设备文件,得到fd

    led_set_on();里面调用ioctl;

    led_set_off();里面调用ioctl;

    接下来写jni了。。com_farsight_server_ledServer.cpp文件

    文件代码:

    [cpp] view plain copy
     
    1. #define LOG_TAG "ledService"  
    2.   
    3. #include "utils/Log.h"  
    4. #include <stdlib.h>  
    5. #include <string.h>  
    6. #include <unistd.h>  
    7. #include <assert.h>  
    8. #include <jni.h>  
    9. #include "../stub/include/led.h"  
    10.   
    11.   
    12. static led_control_device_t *sLedDevice = NULL;  
    13.   
    14.   
    15. static jint led_set_on(JNIEnv *env, jobject thiz, jint arg)   
    16. {  
    17.     if(sLedDevice) {  
    18.         LOGI("led_set_on");  
    19.         sLedDevice->set_on(sLedDevice, (int)arg);  
    20.     }else {  
    21.         LOGI("sLedDevice is NULL");  
    22.     };  
    23.     return 0;  
    24. }  
    25.   
    26. static jint led_set_off(JNIEnv *env, jobject thiz, jint arg)  
    27. {  
    28.     if(sLedDevice) {  
    29.         LOGI("led_set_off");  
    30.         sLedDevice->set_off(sLedDevice, (int)arg);  
    31.     }else {  
    32.         LOGI("sLedDevice is null");  
    33.     }  
    34.     return 0;  
    35. }  
    36.   
    37.   
    38.   
    39. static inline int led_control_open(const struct hw_module_t *module,  
    40.     struct led_control_device_t **device)  
    41. {  
    42.     LOGI("led_control_open");  
    43.     return module->methods->open(module, LED_HARDWARE_MODULE_ID,  
    44.         (struct hw_device_t **)device);  
    45. }  
    46.   
    47.   
    48. static jint led_init(JNIEnv *env, jclass clazz)  
    49. {  
    50.     led_module_t const *module;  
    51.     LOGI("led_init");  
    52.   
    53.     if(hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t **)&module) == 0) {  
    54.         LOGI("get Module OK");  
    55.         if (led_control_open(&module->common, &sLedDevice) != 0) {  
    56.             LOGI("led_init error");  
    57.             return -1;  
    58.         }  
    59.     }  
    60.         LOGI("led_init success");  
    61.         return 0;  
    62.           
    63. }  
    64.   
    65.   
    66. static const JNINativeMethod gMethods[] = {  
    67.     {"_init",           "()Z",          (void *)led_init},  
    68.     {"_set_on",         "(I)I",         (void *)led_set_on},  
    69.     {"_set_off",        "(I)I",         (void *)led_set_off},  
    70. };  
    71.   
    72. static int registerMethods(JNIEnv *env) {  
    73.     static const char * const kClassName =   
    74.         "com/farsight/server/ledService";  
    75.     jclass clazz;  
    76.     clazz = env->FindClass(kClassName);  
    77.     if(clazz == NULL) {  
    78.         LOGE("Can't find class %s ", kClassName);  
    79.         return -1;  
    80.     }  
    81.   
    82.     if(env->RegisterNatives(clazz, gMethods,   
    83.             sizeof(gMethods)/sizeof(gMethods[0])) !=  JNI_OK)   
    84.     {  
    85.         LOGE("failed registering methods for %s ", kClassName);  
    86.         return -1;  
    87.     }  
    88.   
    89.     return 0;  
    90. }  
    91.   
    92.   
    93. jint JNI_OnLoad(JavaVM *vm, void *reserved) {  
    94.     JNIEnv *env = NULL;  
    95.     jint result = -1;  
    96.     LOGI("JNI_onLoad");  
    97.   
    98.     if(vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {  
    99.         LOGE("ERROR: jni_onload() ");  
    100.         goto fail;  
    101.     }  
    102.   
    103.     assert(env != NULL);  
    104.     if(registerMethods(env) != 0) {  
    105.         LOGE("ERROR: registerMethod() ");  
    106.         goto fail;  
    107.     }  
    108.   
    109.     result = JNI_VERSION_1_4;  
    110.   
    111. fail:  
    112.     return result;  
    113. }  

    在jni里首先加载jni库文件的时候先要调用JNI_OnLoad函数,通过系统函数GetEnv让env指针获得有效的值。然后接着调用registerMethods函数,这个函数是自己定义一个函数。

    static const char * const kClassName = "com/farsight/server/ledService"; 类名与Eclipse下开发对应的包一致。不过点换成了下划线。

    然后找到对应的类,接着就是向系统注册Native函数(Native Interface即本地接口函数),函数列表gMethods里 _init是上层framework去加载库时候调用的,当上层调用_init时,与之对应调用的函数就是led_init, ()Z的意思是函数led_init参数为空,返回为空。这里其实就是做了一个函数的映射,上层用的java函数,在这里与之对应成c 函数。

    同理,其余的_set_on _set_off就不必赘述。

    在调用led_init()函数时,系统是如何找到与之对应的stub的呢(也就是如何找到hw_module_t结构体的呢)?主要的函数就是hw_get_module这个函数是通过第一个参数ID号,找到系统里已经存在的与之对应id号的stub(即led_module_t HAL_MODULE_INFO_SYM 结构体变量),第二个参数就传进去的二级指针,让module获取有效的值,

    接着调用 led_control_open,这个函数是内联函数,函数里面接着调用了HAL_MODULE_INFO_SYM 里的methods,methods里就一个成员open,其实呢就是调用了led.c(stub)的led_device_open函数,sLedDevice指针是一个全局变量,经过这个函数的调用,sLedDevice就获得了hw_deive_t的地址(sLedDevice指向了hw_device_t)。

    本来一个指针没有值,但是通过传进去二级指针,就能让原来为空的指针获得有效的值,你可以参考我写的博文 int*p和 int **p,对你们理解二级指针改变一级指针指向有帮助。既然在jni层能够获得stub里的hw_module_t 和 hw_device_t,那么去调用stub里的函数也就不是问题了。

    接下来就是去实现framework层了,framew层里的service去调用jni的。framework层里的service是在eclipse下开发的。

    文件名:ledService.java

    代码:

    [cpp] view plain copy
     
    1. package com.farsight.server;  
    2.   
    3. import android.util.Log;  
    4.   
    5. public class ledService {  
    6.     static {  
    7.         Log.i("ledService", "Load Native service LIB");  
    8.         System.loadLibrary("led_runtime");  
    9.     }  
    10.     public ledService() {  
    11.         Log.i ( "Java Service" , "do init Native Call" );  
    12.         _init ();  
    13.     }  
    14.     public boolean set_on(int arg) {  
    15.         if(0 == _set_on(arg)) {  
    16.             return true;  
    17.         }else {  
    18.             return false;  
    19.         }  
    20.     }  
    21.       
    22.     public boolean set_off(int arg) {  
    23.         if(0 == _set_off(arg)) {  
    24.             return true;  
    25.         }else {  
    26.             return false;  
    27.         }  
    28.     }  
    29.       
    30.     private static native boolean _init();  
    31.     private static native int _set_on(int arg);  
    32.     private static native int _set_off(int arg);   
    33. }  


     

    private static native boolean _init();

    private static native int _set_on(int arg);

    private static native int _set_off(int arg);

    这里的三个函数,就是在jni里声明的native interface接口函数。

    当声明一个ledService 的对象时,static里的函数库会加载,默认的路径就是去加载/system/lib下与之对应的库,强调一点就是,led_runtime省去了前面的lib和后缀.so。

    这样,我们去调用jni的时候就能成功,否则会失败。

    其余的就是在应用程序里声明一个ledService对象,然后调用对象里的set_on 和 set_off 就可以了。可以自己写一个应用程序去测试一下。

    下面是我的一个项目的截图:

    因为设计到M0开发板,所以会有温湿度以及RFID卡的截图。

     源码下载地址:http://download.csdn.net/detail/k_linux_man/3865567

    Android根文件系统、内核zIamge下载;http://download.csdn.net/detail/k_linux_man/3865826

  • 相关阅读:
    poj 1050
    poj 2479 Maximum sum
    Trie树结构(AC自动机前奏)(转)
    poj 3624(zeroonepack)
    poj 3630(Phone List )
    KMP算法(AC自动机前奏)(转)
    AC自动机(转)
    AC自动机模板(hdu2222)
    Running CMD.EXE as Local System
    什么是WPF(Avalon)
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7103313.html
Copyright © 2020-2023  润新知