自己动手写最简单的Android驱动---LED驱动的编写

笛里谁知壮士心,沙头空照征人骨。这篇文章主要讲述自己动手写最简单的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\n");    
  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\n");    
  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\n",  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\n",  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()\n");    
  100.                 goto  fail;    
  101.         }   
  102.    
  103.         assert(env  !=  NULL);    
  104.         if(registerMethods(env)  !=  0)  {   
  105.                 LOGE("ERROR:  registerMethod()\n");    
  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
自己动手写最简单的Android驱动---LED驱动的编写

文章图片

代码:
[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卡的截图。
自己动手写最简单的Android驱动---LED驱动的编写

文章图片

自己动手写最简单的Android驱动---LED驱动的编写

文章图片
自己动手写最简单的Android驱动---LED驱动的编写

文章图片

自己动手写最简单的Android驱动---LED驱动的编写

文章图片

自己动手写最简单的Android驱动---LED驱动的编写

文章图片

 
自己动手写最简单的Android驱动---LED驱动的编写

文章图片

自己动手写最简单的Android驱动---LED驱动的编写

文章图片

自己动手写最简单的Android驱动---LED驱动的编写

文章图片

自己动手写最简单的Android驱动---LED驱动的编写

文章图片

  源码下载地址:http://download.csdn.net/detail/k_linux_man/3865567
【自己动手写最简单的Android驱动---LED驱动的编写】Android根文件系统、内核zIamge下载; http://download.csdn.net/detail/k_linux_man/3865826

    推荐阅读