本文转载自: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. #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. return 0;
  40. }

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

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

struct hw_module_t;

struct hw_device_t;

struct hw_module_methods_t;

android源码里面结构体的声明

  1. typedef struct hw_module_t {
  2. uint 32_t   tag;
  3. uint16_t    version_major;
  4. uint16_t    version_minor;
  5. const char *id;
  6. const char *name;
  7. const char *author;
  8. const hw_module_methods_t  *methods;
  9. void* dso;
  10. uint32_t reserved[32-7];
  11. } hw_module_t;
  1. typedef struct hw_device_t {
  2. uint32_t tag;
  3. uint32_t version;
  4. struct hw_module_t* module;
  5. uint32_t reserved[12];
  6. int (*close) (struct hw_device_t  *device);
  7. }hw_device_t;
  1. typedef struct hw_module_methods_t {
  2. int (*open) (const struct hw_module_t *module, const char *id,
  3. struct hw_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. #define LED_HARDWARE_MODULE_ID "led"
  7. struct led_module_t {
  8. struct hw_module_t common;
  9. };
  10. struct led_control_device_t {
  11. struct hw_device_t common;
  12. int (*set_on) (struct led_control_device_t *dev, int arg);
  13. int (*set_off)(struct led_control_device_t *dev, int arg);
  14. };
  15. struct led_control_context_t {
  16. struct led_control_device_t device;
  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. #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. #define LED_ON  _IO ('k',1)
  10. #define LED_OFF     _IO ('k',2)
  11. int fd;
  12. static int led_set_on(struct led_control_device_t *dev, int arg)
  13. {
  14. LOGI("led_set_on");
  15. ioctl(fd, LED_ON, arg);
  16. return 0;
  17. }
  18. static int led_set_off(struct led_control_device_t *dev, int arg)
  19. {
  20. LOGI("led_set_off");
  21. ioctl(fd, LED_OFF, arg);
  22. return 0;
  23. }
  24. static int led_device_close(struct hw_device_t *device)
  25. {
  26. struct led_control_context_t *context = (struct led_control_context_t *)device;
  27. if(context) free(context);
  28. close(fd);
  29. return 0;
  30. }
  31. static int led_device_open(const struct hw_module_t *module, const char *name,
  32. struct hw_device_t **device)
  33. {
  34. struct led_control_context_t *context;
  35. LOGD("led_device_open");
  36. context = (struct led_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 = (struct hw_device_t *)&(context->device);
  45. if((fd = open("/dev/led",O_RDWR)) == -1)
  46. {
  47. LOGI("ERROR: open");
  48. }else {
  49. LOGI("open led device ok\n");
  50. }
  51. return 0;
  52. }
  53. static struct hw_module_methods_t led_module_methods = {
  54. open:led_device_open
  55. };
  56. const struct led_module_t HAL_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. #define LOG_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. static led_control_device_t *sLedDevice = NULL;
  10. static jint led_set_on(JNIEnv *env, jobject thiz, jint arg)
  11. {
  12. if(sLedDevice) {
  13. LOGI("led_set_on");
  14. sLedDevice->set_on(sLedDevice, (int)arg);
  15. }else {
  16. LOGI("sLedDevice is NULL");
  17. };
  18. return 0;
  19. }
  20. static jint led_set_off(JNIEnv *env, jobject thiz, jint arg)
  21. {
  22. if(sLedDevice) {
  23. LOGI("led_set_off");
  24. sLedDevice->set_off(sLedDevice, (int)arg);
  25. }else {
  26. LOGI("sLedDevice is null");
  27. }
  28. return 0;
  29. }
  30. static inline int led_control_open(const struct hw_module_t *module,
  31. struct led_control_device_t **device)
  32. {
  33. LOGI("led_control_open");
  34. return module->methods->open(module, LED_HARDWARE_MODULE_ID,
  35. (struct hw_device_t **)device);
  36. }
  37. static jint led_init(JNIEnv *env, jclass clazz)
  38. {
  39. led_module_t const *module;
  40. LOGI("led_init");
  41. if(hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t **)&module) == 0) {
  42. LOGI("get Module OK");
  43. if (led_control_open(&module->common, &sLedDevice) != 0) {
  44. LOGI("led_init error");
  45. return -1;
  46. }
  47. }
  48. LOGI("led_init success");
  49. return 0;
  50. }
  51. static const JNINativeMethod gMethods[] = {
  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. static int registerMethods(JNIEnv *env) {
  57. static const char * const kClassName =
  58. "com/farsight/server/ledService";
  59. jclass clazz;
  60. clazz = env->FindClass(kClassName);
  61. if(clazz == NULL) {
  62. LOGE("Can't find class %s\n", kClassName);
  63. return -1;
  64. }
  65. if(env->RegisterNatives(clazz, gMethods,
  66. sizeof(gMethods)/sizeof(gMethods[0])) !=  JNI_OK)
  67. {
  68. LOGE("failed registering methods for %s\n", kClassName);
  69. return -1;
  70. }
  71. return 0;
  72. }
  73. jint JNI_OnLoad(JavaVM *vm, void *reserved) {
  74. JNIEnv *env = NULL;
  75. jint result = -1;
  76. LOGI("JNI_onLoad");
  77. if(vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
  78. LOGE("ERROR: jni_onload()\n");
  79. goto fail;
  80. }
  81. assert(env != NULL);
  82. if(registerMethods(env) != 0) {
  83. LOGE("ERROR: registerMethod()\n");
  84. goto fail;
  85. }
  86. result = JNI_VERSION_1_4;
  87. fail:
  88. return result;
  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. package com.farsight.server;
  2. import android.util.Log;
  3. public class ledService {
  4. static {
  5. Log.i("ledService", "Load Native service LIB");
  6. System.loadLibrary("led_runtime");
  7. }
  8. public ledService() {
  9. Log.i ( "Java Service" , "do init Native Call" );
  10. _init ();
  11. }
  12. public boolean set_on(int arg) {
  13. if(0 == _set_on(arg)) {
  14. return true;
  15. }else {
  16. return false;
  17. }
  18. }
  19. public boolean set_off(int arg) {
  20. if(0 == _set_off(arg)) {
  21. return true;
  22. }else {
  23. return false;
  24. }
  25. }
  26. private static native boolean _init();
  27. private static native int _set_on(int arg);
  28. private static native int _set_off(int arg);
  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

自己动手写最简单的Android驱动---LED驱动的编写【转】的更多相关文章

  1. 动手写一个简单版的谷歌TPU-矩阵乘法和卷积

    谷歌TPU是一个设计良好的矩阵计算加速单元,可以很好的加速神经网络的计算.本系列文章将利用公开的TPU V1相关资料,对其进行一定的简化.推测和修改,来实际编写一个简单版本的谷歌TPU.计划实现到行为 ...

  2. 动手写一个简单版的谷歌TPU-指令集

    系列目录 谷歌TPU概述和简化 基本单元-矩阵乘法阵列 基本单元-归一化和池化(待发布) TPU中的指令集 SimpleTPU实例: (计划中) 拓展 TPU的边界(规划中) 重新审视深度神经网络中的 ...

  3. 动手写一个简单的Web框架(模板渲染)

    动手写一个简单的Web框架(模板渲染) 在百度上搜索jinja2,显示的大部分内容都是jinja2的渲染语法,这个不是Web框架需要做的事,最终,居然在Werkzeug的官方文档里找到模板渲染的代码. ...

  4. 动手写一个简单的Web框架(Werkzeug路由问题)

    动手写一个简单的Web框架(Werkzeug路由问题) 继承上一篇博客,实现了HelloWorld,但是这并不是一个Web框架,只是自己手写的一个程序,别人是无法通过自己定义路由和返回文本,来使用的, ...

  5. 动手写一个简单的Web框架(HelloWorld的实现)

    动手写一个简单的Web框架(HelloWorld的实现) 关于python的wsgi问题可以看这篇博客 我就不具体阐述了,简单来说,wsgi标准需要我们提供一个可以被调用的python程序,可以实函数 ...

  6. (笔记)linux设备驱动--LED驱动

    linux设备驱动--LED驱动 最近正在学习设备驱动开发,因此打算写一个系列博客,即是对自己学习的一个总结,也是对自己的一个督促,有不对,不足,需要改正的地方还望大家指出,而且希望结识志同道合的朋友 ...

  7. 自己动手写一个简单的MVC框架(第一版)

    一.MVC概念回顾 路由(Route).控制器(Controller).行为(Action).模型(Model).视图(View) 用一句简单地话来描述以上关键点: 路由(Route)就相当于一个公司 ...

  8. 自己动手写一个简单的MVC框架(第二版)

    一.ASP.NET MVC核心机制回顾 在ASP.NET MVC中,最核心的当属“路由系统”,而路由系统的核心则源于一个强大的System.Web.Routing.dll组件. 在这个System.W ...

  9. 自己动手写一个简单的(IIS)小型服务器

    因为第一次在博客园发表随笔,不太会用,这个笔记是我之前在印象笔记中写好的,然后直接copy过来,有兴趣自己做一个IIS服务器的小伙伴们可以参照下面的流程做一次,也可以叫我要源代码,不过要做完,我觉得花 ...

随机推荐

  1. LeetCode02-两数相加

    ''' 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来表示 ...

  2. Android学习2013年10月11日

    1.LinearLayout http://www.cnblogs.com/salam/archive/2010/10/20/1856793.html LinearLayout是线性布局控件,它包含的 ...

  3. OO第二次作业

    第一次作业: 由于第一次作业的调度较为简单,采用FIFO策略,以及不支持捎带功能,因此我的第一次电梯作业并没有设置单独的调度器,而会直接将任务交给电梯,电梯进行调度策略也仅为先运动到people的In ...

  4. You Are the One (区间DP)

    The TV shows such as You Are the One has been very popular. In order to meet the need of boys who ar ...

  5. [luoguP3694] 邦邦的大合唱站队/签到题(状压DP)

    传送门 来自kkk的题解: 70分做法:枚举每个学校顺序,暴力. 100分:状压dp.从队列头到尾DP, 状态:f[i]表示i状态下最小的出列(不一致)的个数. 比如f[1101]表示从头到位为1/3 ...

  6. ACDream:1210:Chinese Girls' Amusement【水题】

    Chinese Girls' Amusement Time Limit: 2000/1000MS (Java/Others)Memory Limit: 128000/64000KB (Java/Oth ...

  7. 【板+背包】多重背包 HDU Coins

    http://acm.hdu.edu.cn/showproblem.php?pid=2844 [题意] 给定n种价值为Ci,个数为Wi的硬币,问在1~V中的这些数中哪些数能由这些硬币组成? [思路] ...

  8. selenide01---截图

    1.junit:import com.codeborne.selenide.junit.ScreenShooter; @Rule public ScreenShooter makeScreenshot ...

  9. [NOIP2003] 提高组 洛谷P1038 神经网络

    题目背景 人工神经网络(Artificial Neural Network)是一种新兴的具有自我学习能力的计算系统,在模式识别.函数逼近及贷款风险评估等诸多领域有广泛的应用.对神经网络的研究一直是当今 ...

  10. poj2243+poj1915骑士问题

    2243是骑士问题,八个格子的,BFS,因为要最短路经,所以没有用A*,A*跑不出来,太慢了,因为要搜索到所有解啊!一直更新最优,而BFS,一层一层搜索,第一次得到的便是最短的了!300格子,标记的话 ...