本文转载自: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. C#sql语句如何使用占位符

    背景:在程序中,写sql语句时,可能要根据变量的值不同,SQL语句产生相应的变化.比如说存在变量StuName,根据变量值的不同,检索不同姓名的学生记录,这时需用到占位符的知识. 1,{0}占位符,代 ...

  2. [Android]使用button切换TextView可见和不可见

    <Button android:id="@+id/btnTest" android:layout_width="match_parent" android ...

  3. XV6文件系统

    文件系统 文件系统的目的是组织和存储数据,典型的文件系统支持用户和程序间的数据共享,并提供数据持久化的支持(即重启之后数据仍然可用). xv6 的文件系统中使用了类似 Unix 的文件,文件描述符,目 ...

  4. 两个很实用很方便的函数核心及用法{(lower_bound)+(max_element))~~

    (1)            关于 lower_bound(a,a+n,x)-a的用法:                                                求x在数组a中的 ...

  5. HDU 4499

    题目大意: N*M的棋盘上摆了一些棋子,在剩余位置上尽可能多的摆上炮,使所有炮不能互吃 dfs+回溯 #include <iostream> #include <cstdio> ...

  6. 2016 Multi-University Training Contest 2 solutions BY zimpha

    Acperience 展开式子, \(\left\| W-\alpha B \right\|^2=\displaystyle\alpha^2\sum_{i=1}^{n}b_i^2-2\alpha\su ...

  7. hihocoder 1873 ACM-ICPC北京赛区2018重现赛 D Frog and Portal

    http://hihocoder.com/problemset/problem/1873 时间限制:1000ms 单点时限:1000ms 内存限制:512MB 描述 A small frog want ...

  8. 【Java源码】集合类-优先队列PriorityQueue

    一.类继承关系 public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serial ...

  9. [Bzoj2286][Sdoi2011]消耗战(虚树模板题附讲解)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4896  Solved: 1824[Submit][Statu ...

  10. Mysql 数据库允许远程连接 服务器连接错误 Host 'XXX' is not allowed to connect to this MySQL server

    如果连接数据库的时候出现这个问题 Host 'XXX' is not allowed to connect to this MySQL server 说明 Mysql数据库 不允许远程连接, 需要修改 ...