`
hulunberbus
  • 浏览: 855415 次
文章分类
社区版块
存档分类
最新评论

自己动手写最简单的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驱动编写的驱动,代码如下:

  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. #defineLED_ON_IO('k',1)
  10. #defineLED_OFF_IO('k',2)
  11. intmain()
  12. {
  13. inti=0;
  14. intdev_fd;
  15. dev_fd=open("/dev/led",O_RDWR);
  16. if(dev_fd==-1){
  17. printf("Cann'topenfile/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. return0;
  40. }

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

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

struct hw_module_t;

struct hw_device_t;

struct hw_module_methods_t;

android源码里面结构体的声明

  1. typedefstructhw_module_t{
  2. uint32_ttag;
  3. uint16_tversion_major;
  4. uint16_tversion_minor;
  5. constchar*id;
  6. constchar*name;
  7. constchar*author;
  8. consthw_module_methods_t*methods;
  9. void*dso;
  10. uint32_treserved[32-7];
  11. }hw_module_t;


  1. typedefstructhw_device_t{
  2. uint32_ttag;
  3. uint32_tversion;
  4. structhw_module_t*module;
  5. uint32_treserved[12];
  6. int(*close)(structhw_device_t*device);
  7. }hw_device_t;


  1. typedefstructhw_module_methods_t{
  2. int(*open)(conststructhw_module_t*module,constchar*id,
  3. structhw_device_t**device);
  4. }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

代码如下;

  1. #include<hardware/hardware.h>
  2. #include<fcntl.h>
  3. #include<errno.h>
  4. #include<cutils/log.h>
  5. #include<cutils/atomic.h>
  6. #defineLED_HARDWARE_MODULE_ID"led"
  7. structled_module_t{
  8. structhw_module_tcommon;
  9. };
  10. structled_control_device_t{
  11. structhw_device_tcommon;
  12. int(*set_on)(structled_control_device_t*dev,intarg);
  13. int(*set_off)(structled_control_device_t*dev,intarg);
  14. };
  15. structled_control_context_t{
  16. structled_control_device_tdevice;
  17. };


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

代码如下:

  1. #defineLOG_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. #defineLED_ON_IO('k',1)
  10. #defineLED_OFF_IO('k',2)
  11. intfd;
  12. staticintled_set_on(structled_control_device_t*dev,intarg)
  13. {
  14. LOGI("led_set_on");
  15. ioctl(fd,LED_ON,arg);
  16. return0;
  17. }
  18. staticintled_set_off(structled_control_device_t*dev,intarg)
  19. {
  20. LOGI("led_set_off");
  21. ioctl(fd,LED_OFF,arg);
  22. return0;
  23. }
  24. staticintled_device_close(structhw_device_t*device)
  25. {
  26. structled_control_context_t*context=(structled_control_context_t*)device;
  27. if(context)free(context);
  28. close(fd);
  29. return0;
  30. }
  31. staticintled_device_open(conststructhw_module_t*module,constchar*name,
  32. structhw_device_t**device)
  33. {
  34. structled_control_context_t*context;
  35. LOGD("led_device_open");
  36. context=(structled_control_context_t*)malloc(sizeof(*context));
  37. memset(context,0,sizeof(*context));
  38. context->device.common.tag=HARDWARE_DEVICE_TAG;
  39. context->device.common.version=0;
  40. context->device.common.module=module;
  41. context->device.common.close=led_device_close;
  42. context->device.set_on=led_set_on;
  43. context->device.set_off=led_set_off;
  44. *device=(structhw_device_t*)&(context->device);
  45. if((fd=open("/dev/led",O_RDWR))==-1)
  46. {
  47. LOGI("ERROR:open");
  48. }else{
  49. LOGI("openleddeviceok\n");
  50. }
  51. return0;
  52. }
  53. staticstructhw_module_methods_tled_module_methods={
  54. open:led_device_open
  55. };
  56. conststructled_module_tHAL_MODULE_INFO_SYM={
  57. common:{
  58. tag:HARDWARE_MODULE_TAG,
  59. version_major:1,
  60. version_minor:0,
  61. id:LED_HARDWARE_MODULE_ID,
  62. name:"led_stub",
  63. author:"K_Linux_Man",
  64. methods:&led_module_methods,
  65. },
  66. };


首先先看 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文件

文件代码:

  1. #defineLOG_TAG"ledService"
  2. #include"utils/Log.h"
  3. #include<stdlib.h>
  4. #include<string.h>
  5. #include<unistd.h>
  6. #include<assert.h>
  7. #include<jni.h>
  8. #include"../stub/include/led.h"
  9. staticled_control_device_t*sLedDevice=NULL;
  10. staticjintled_set_on(JNIEnv*env,jobjectthiz,jintarg)
  11. {
  12. if(sLedDevice){
  13. LOGI("led_set_on");
  14. sLedDevice->set_on(sLedDevice,(int)arg);
  15. }else{
  16. LOGI("sLedDeviceisNULL");
  17. };
  18. return0;
  19. }
  20. staticjintled_set_off(JNIEnv*env,jobjectthiz,jintarg)
  21. {
  22. if(sLedDevice){
  23. LOGI("led_set_off");
  24. sLedDevice->set_off(sLedDevice,(int)arg);
  25. }else{
  26. LOGI("sLedDeviceisnull");
  27. }
  28. return0;
  29. }
  30. staticinlineintled_control_open(conststructhw_module_t*module,
  31. structled_control_device_t**device)
  32. {
  33. LOGI("led_control_open");
  34. returnmodule->methods->open(module,LED_HARDWARE_MODULE_ID,
  35. (structhw_device_t**)device);
  36. }
  37. staticjintled_init(JNIEnv*env,jclassclazz)
  38. {
  39. led_module_tconst*module;
  40. LOGI("led_init");
  41. if(hw_get_module(LED_HARDWARE_MODULE_ID,(consthw_module_t**)&module)==0){
  42. LOGI("getModuleOK");
  43. if(led_control_open(&module->common,&sLedDevice)!=0){
  44. LOGI("led_initerror");
  45. return-1;
  46. }
  47. }
  48. LOGI("led_initsuccess");
  49. return0;
  50. }
  51. staticconstJNINativeMethodgMethods[]={
  52. {"_init","()Z",(void*)led_init},
  53. {"_set_on","(I)I",(void*)led_set_on},
  54. {"_set_off","(I)I",(void*)led_set_off},
  55. };
  56. staticintregisterMethods(JNIEnv*env){
  57. staticconstchar*constkClassName=
  58. "com/farsight/server/ledService";
  59. jclassclazz;
  60. clazz=env->FindClass(kClassName);
  61. if(clazz==NULL){
  62. LOGE("Can'tfindclass%s\n",kClassName);
  63. return-1;
  64. }
  65. if(env->RegisterNatives(clazz,gMethods,
  66. sizeof(gMethods)/sizeof(gMethods[0]))!=JNI_OK)
  67. {
  68. LOGE("failedregisteringmethodsfor%s\n",kClassName);
  69. return-1;
  70. }
  71. return0;
  72. }
  73. jintJNI_OnLoad(JavaVM*vm,void*reserved){
  74. JNIEnv*env=NULL;
  75. jintresult=-1;
  76. LOGI("JNI_onLoad");
  77. if(vm->GetEnv((void**)&env,JNI_VERSION_1_4)!=JNI_OK){
  78. LOGE("ERROR:jni_onload()\n");
  79. gotofail;
  80. }
  81. assert(env!=NULL);
  82. if(registerMethods(env)!=0){
  83. LOGE("ERROR:registerMethod()\n");
  84. gotofail;
  85. }
  86. result=JNI_VERSION_1_4;
  87. fail:
  88. returnresult;
  89. }

在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

代码:

  1. packagecom.farsight.server;
  2. importandroid.util.Log;
  3. publicclassledService{
  4. static{
  5. Log.i("ledService","LoadNativeserviceLIB");
  6. System.loadLibrary("led_runtime");
  7. }
  8. publicledService(){
  9. Log.i("JavaService","doinitNativeCall");
  10. _init();
  11. }
  12. publicbooleanset_on(intarg){
  13. if(0==_set_on(arg)){
  14. returntrue;
  15. }else{
  16. returnfalse;
  17. }
  18. }
  19. publicbooleanset_off(intarg){
  20. if(0==_set_off(arg)){
  21. returntrue;
  22. }else{
  23. returnfalse;
  24. }
  25. }
  26. privatestaticnativeboolean_init();
  27. privatestaticnativeint_set_on(intarg);
  28. privatestaticnativeint_set_off(intarg);
  29. }


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


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics