本文转载自:https://blog.csdn.net/tianshiyalin/article/details/17136723

一、前言

本人刚学习安卓驱动开发,水平不能说菜,是根本没有水平,在这里把学习过程贴出来,跟大家一起学习交流,还望大家多多指正,转载的请标明出处。http://blog.csdn.net/angle_birds/article/details/16801533

二、android驱动介绍

安卓总体架构是在 Linux内核基础上,增加硬件抽象层(HAL),运行库,java虚拟机,程序框架等组成的,具体如下图。

安卓的应用程序是从application framework层架构上建立的。所有APK应用程序都是通过framework层来运行的。application framework是google写好的,除非自己深度定制,一般是不会更改这个层的。对于驱动开发来讲,我们要做的就是让framework层能认识并操作我们的硬件设备就OK了。因此我们关心主要有3个层面:

linux Kernel层

HAL层

JNI层

1.       linuxKernel:是google在linux内核基础上,专门为移动设备优化后的内核,增加修改一些东西,担修改的不多,对于内核驱动来讲,基本没有修改,做过linux驱动开发的人应该很容易理解。

2.       HAL,硬件抽象层:简单来说,就是对Linux 内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间。用户空间不属于内核不必遵守GPL协议,各个厂商可以把与自己硬件设备相关,具有商业机密的一些代码放在HAL层。

3.       JNI层:提供java和底层C、C++的动态链接库的接口。我理解的是JNI就是一个代理,可以把C和C++生成的接口函数翻译成Java可用,提供给framework层。

三、振动系统开发过程

1.       硬件平台

CPU:IMX6Q4核1G

RAM:1G

FLASH:8G板载

这次开发用的代码都是google和飞思卡尔提供的具体的就不再说明了,因为每个平台代码都有所不同,而且买开发板时候都会带相应的资料。

2.       震动系统是android里面比较简单的一个系统, 我采用的是从高层到底层的学习方式。因为我们的驱动最终是给应用程序用的,从application的需求分析JNI,然后分析HAL最后在我们写linux kernel驱动时候,很容易理解为什么要这么写。好了开始正式分析。

3.       Application层:通过google我找到关于APK访问震动的如下说明:

A、通过系统服务获得手机震动服务,Vibrator vibrator =(Vibrator)getSystemService(VIBRATOR_SERVICE);

B、得到震动服务后检测vibrator是否存在:

vibrator.hasVibrator();

检测当前硬件是否有vibrator,如果有返回true,如果没有返回false。

C、根据实际需要进行适当的调用,

vibrator.vibrate(longmilliseconds);

开始启动vibrator持续milliseconds毫秒。

vibrator.vibrate(long[]pattern, int repeat);

以pattern方式重复repeat次启动vibrator。

(pattern的形式为new long[]{arg1,arg2,arg3,arg4......},其中以两个一组的如arg1 和arg2为一组、arg3和arg4为一组,每一组的前一个代表等待多少毫 秒启动vibrator,后一个代表vibrator持续多少毫秒停止,之后往复即 可。Repeat表示重复次数,当其为-1时,表示不重复只以pattern的方 式运行一次)。

D、vibrator.cancel();

Vibrator停止。

从上面的说明,可以看出应用程序调用震动系统,是调用一个叫VIBRATOR_SERVICE的服务,这个服务有3个函数,分别是hasVibrator(),r.vibrate,.cancel();当然这个三个函数可能在framework层进行的另一层的封装,我没有去深究。但可以推测出JNI层要做的是与注册VIBRATOR_SERVICE服务和实现这三个函数相关的.

4.       HAL层:这一层我找到了具体的代码我们会好分析很多,其代码是:

android\frameworks\base\services\jni\ com_android_server_VibratorService.cpp

  1. #define LOG_TAG"VibratorService"
  2. #include"jni.h"
  3. #include"JNIHelp.h"
  4. #include"android_runtime/AndroidRuntime.h"
  5. #include<utils/misc.h>
  6. #include<utils/Log.h>
  7. #include<hardware_legacy/vibrator.h>
  8. #include<stdio.h>
  9. namespace android
  10. {
  11. static jbooleanvibratorExists(JNIEnv *env, jobject clazz)       //判断振动器是否存在
  12. {
  13. return vibrator_exists() > 0 ? JNI_TRUE: JNI_FALSE;
  14. }
  15. static voidvibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms)//打开振动器
  16. {
  17. // LOGI("vibratorOn\n");
  18. vibrator_on(timeout_ms);
  19. }
  20. static voidvibratorOff(JNIEnv *env, jobject clazz)//关闭振动器
  21. {
  22. // LOGI("vibratorOff\n");
  23. vibrator_off();
  24. }
  25. staticJNINativeMethod method_table[] = {
  26. { "vibratorExists","()Z", (void*)vibratorExists },
  27. { "vibratorOn", "(J)V",(void*)vibratorOn },
  28. { "vibratorOff", "()V",(void*)vibratorOff }
  29. };
  30. intregister_android_server_VibratorService(JNIEnv *env)    //注册vibrator服务
  31. {
  32. return jniRegisterNativeMethods(env,"com/android/server/VibratorService",
  33. method_table, NELEM(method_table));
  34. }

从上面代码可以看出,JNI做了两件事:其一注册vibrator服务,其二,实现了vibratorExists,vibratorOn,vibratorOff三个服务函数。 进而我们可以分析出HAL层主要的就是实现次代码里调用的三个函数vibrator_exists(),vibrator_on(timeout_ms),vibrator_off()。

5.       HAL层:经过各种查找我们找到了vibrator的hal层代码:

\android40\hardware\libhardware_legacy\vibrator\vibrator.c

  1. #include<hardware_legacy/vibrator.h>
  2. #include"qemu.h"
  3. #include<stdio.h>
  4. #include<unistd.h>
  5. #include<fcntl.h>
  6. #include<errno.h>
  7. #define THE_DEVICE"/sys/class/timed_output/vibrator/enable"
  8. intvibrator_exists()         //判断 振动器是否存在
  9. {
  10. int fd;
  11. #ifdefQEMU_HARDWARE                 //模拟器情况下实现此功能
  12. if (qemu_check()) {
  13. return 1;
  14. }
  15. #endif
  16. fd = open(THE_DEVICE, O_RDWR);
  17. if(fd < 0)
  18. return 0;
  19. close(fd);
  20. return 1;
  21. }
  22. static intsendit(int timeout_ms)       //打开振动器 timeout_ms 毫秒
  23. {
  24. int nwr, ret, fd;
  25. char value[20];
  26. #ifdefQEMU_HARDWARE        //模拟器情况下实现次功能
  27. if (qemu_check()) {
  28. return qemu_control_command("vibrator:%d", timeout_ms );
  29. }
  30. #endif
  31. fd = open(THE_DEVICE, O_RDWR);
  32. if(fd < 0)
  33. return errno;
  34. nwr = sprintf(value, "%d\n",timeout_ms);
  35. ret = write(fd, value, nwr);
  36. close(fd);
  37. return (ret == nwr) ? 0 : -1;
  38. }
  39. intvibrator_on(int timeout_ms)
  40. {
  41. /* constant on, up to maximum allowed time*/
  42. return sendit(timeout_ms);
  43. }
  44. int vibrator_off()     //关闭振动器就是设置振动器打开时间为0
  45. {
  46. return sendit(0);
  47. }

分析上面代码可以看出,HAL访问这个设备是打开/sys/class/timed_output/vibrator/enable,这个设备文件,然后向文件中写入打开时间来完成设备操作的。因此很容易我们可以推断出,linux kernel层是要生成这个设备文件然后,实现相应的函数。

6.       Linuxkernel层:通过上面分析我们大概了解了内核驱动所要实现的功能。通过各种参考资料,我查到了这个设备驱动是通过timed_output框架来实现的,有框架在又简单了不少,我们找到timed_output框架实现的函数在:

\kernel\drivers\staging\android\timed_output.c

  1. #include<linux/module.h>
  2. #include<linux/types.h>
  3. #include<linux/device.h>
  4. #include<linux/fs.h>
  5. #include<linux/err.h>
  6. #include"timed_output.h"
  7. static structclass *timed_output_class;
  8. static atomic_tdevice_count;
  9. static ssize_t enable_show(structdevice *dev, struct device_attribute *attr,
  10. char *buf)
  11. {
  12. struct timed_output_dev *tdev =dev_get_drvdata(dev);
  13. int remaining = tdev->get_time(tdev);
  14. return sprintf(buf, "%d\n",remaining);
  15. }
  16. static ssize_tenable_store(
  17. struct device *dev, structdevice_attribute *attr,
  18. const char *buf, size_t size)
  19. {
  20. struct timed_output_dev *tdev =dev_get_drvdata(dev);
  21. int value;
  22. if (sscanf(buf, "%d", &value)!= 1)
  23. return -EINVAL;
  24. tdev->enable(tdev, value);
  25. return size;
  26. }
  27. static DEVICE_ATTR(enable,S_IRUGO | S_IWUSR, enable_show, enable_store);
  28. static intcreate_timed_output_class(void)
  29. {
  30. if (!timed_output_class) {
  31. timed_output_class =class_create(THIS_MODULE, "timed_output");
  32. if (IS_ERR(timed_output_class))
  33. return PTR_ERR(timed_output_class);
  34. atomic_set(&device_count, 0);
  35. }
  36. return 0;
  37. }
  38. inttimed_output_dev_register(struct timed_output_dev *tdev)
  39. {
  40. int ret;
  41. if (!tdev || !tdev->name ||!tdev->enable || !tdev->get_time)
  42. return -EINVAL;
  43. ret = create_timed_output_class();
  44. if (ret < 0)
  45. return ret;
  46. tdev->index =atomic_inc_return(&device_count);
  47. tdev->dev =device_create(timed_output_class, NULL,
  48. MKDEV(0, tdev->index), NULL,tdev->name);
  49. if (IS_ERR(tdev->dev))
  50. return PTR_ERR(tdev->dev);
  51. ret = device_create_file(tdev->dev,&dev_attr_enable);
  52. if (ret < 0)
  53. goto err_create_file;
  54. dev_set_drvdata(tdev->dev, tdev);
  55. tdev->state = 0;
  56. return 0;
  57. err_create_file:
  58. device_destroy(timed_output_class, MKDEV(0,tdev->index));
  59. printk(KERN_ERR "timed_output: Failedto register driver %s\n",
  60. tdev->name);
  61. return ret;
  62. }
  63. EXPORT_SYMBOL_GPL(timed_output_dev_register);
  64. voidtimed_output_dev_unregister(struct timed_output_dev *tdev)
  65. {
  66. device_remove_file(tdev->dev,&dev_attr_enable);
  67. device_destroy(timed_output_class, MKDEV(0,tdev->index));
  68. dev_set_drvdata(tdev->dev, NULL);
  69. }
  70. EXPORT_SYMBOL_GPL(timed_output_dev_unregister);
  71. static int __inittimed_output_init(void)
  72. {
  73. return create_timed_output_class();
  74. }
  75. static void __exittimed_output_exit(void)
  76. {
  77. class_destroy(timed_output_class);
  78. }
  79. module_init(timed_output_init);
  80. module_exit(timed_output_exit);
  81. MODULE_AUTHOR("MikeLockwood <lockwood@android.com>");
  82. MODULE_DESCRIPTION("timedoutput class driver");
  83. MODULE_LICENSE("GPL");

\kernel\drivers\staging\android\timed_output.h

  1. #ifndef _LINUX_TIMED_OUTPUT_H
  2. #define _LINUX_TIMED_OUTPUT_H
  3. struct timed_output_dev {
  4. constchar  *name;
  5. /* enablethe output and set the timer */
  6. void   (*enable)(struct timed_output_dev *sdev, inttimeout);
  7. /*returns the current number of milliseconds remaining on the timer */
  8. int              (*get_time)(structtimed_output_dev *sdev);
  9. /*private data */
  10. structdevice       *dev;
  11. int              index;
  12. int              state;
  13. };
  14. extern int timed_output_dev_register(struct timed_output_dev*dev);
  15. extern void timed_output_dev_unregister(structtimed_output_dev *dev);
  16. #endif

分析上面代码可以看出,我们的驱动是要实现timed_output_dev结构体,然后注册这个结构体就行了。下面我们开始真正动手。由于本人水平有限,参考了samung一个公开kernel的代码里的马达驱动。写出了自己的驱动:

本人硬件的马达通过P4.17脚控制 高打开 低关闭

\kernel_imx\drivers\vibrator\vibrator.c

  1. #include <linux/hrtimer.h>
  2. #include <linux/err.h>
  3. #include <linux/gpio.h>
  4. #include <linux/wakelock.h>
  5. #include <linux/mutex.h>
  6. #include <linux/clk.h>
  7. #include <linux/workqueue.h>
  8. #include <asm/mach-types.h>
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include<../drivers/staging/android/timed_output.h>
  12. #define IMX_GPIO_NR(bank, nr)             (((bank) - 1) * 32 + (nr))        //IO定义
  13. #define SABRESD_VIBRATOR_CTL                   IMX_GPIO_NR(4, 17)   //电机通过P4.17脚控制 高打开 低关闭
  14. #define MAX_TIMEOUT        10000/* 10s */  //最长可打开10s
  15. static structvibrator {
  16. structwake_lock wklock;      //wake_lock 防止震动过程中系统休眠,线程不释放此设备,造成不必要错误
  17. structhrtimer timer;    //高精度定时器
  18. structmutex lock;        //互斥锁,防止多线程同时访问这个设备.
  19. structwork_struct work; //设备操作队列,用于一次操作完成和下一次开始同步用 (三星这么用的,具体为什么不直接用回调函数,我也不懂,还望大神们私信给个说明 感激不尽)
  20. } vibdata;
  21. static void mx6_vibrator_off(void)
  22. {
  23. gpio_direction_output(SABRESD_VIBRATOR_CTL,0);
  24. wake_unlock(&vibdata.wklock);              //震动关闭就可以释放 wake_lock锁
  25. }
  26. void mx6_motor_enable(struct timed_output_dev *sdev,int value)
  27. {
  28. mutex_lock(&vibdata.lock);                     //关键代码段,同一时间只允许一个线程执行
  29. /* cancelprevious timer and set GPIO according to value */
  30. hrtimer_cancel(&vibdata.timer);            //当先前定时器完成后 关闭这个定时器
  31. cancel_work_sync(&vibdata.work);         //当上次震动完成后 关闭这次动作
  32. if(value)
  33. {
  34. wake_lock(&vibdata.wklock);         //开始震动打开wake lock锁不允许休眠
  35. gpio_direction_output(SABRESD_VIBRATOR_CTL,1);
  36. if(value > 0)
  37. {
  38. if(value > MAX_TIMEOUT)
  39. value= MAX_TIMEOUT;
  40. value+= 45;                                    //为了使震动变得明显,固定增加一个时间.跟硬件有关系
  41. hrtimer_start(&vibdata.timer,                 //开始定时器
  42. ns_to_ktime((u64)value* NSEC_PER_MSEC),
  43. HRTIMER_MODE_REL);
  44. }
  45. }
  46. else
  47. mx6_vibrator_off();
  48. mutex_unlock(&vibdata.lock);                 //关键代码段执行完成,释放互斥锁
  49. }
  50. int     mx6_get_time(structtimed_output_dev *sdev)
  51. {
  52. if(hrtimer_active(&vibdata.timer))
  53. {
  54. ktime_tr = hrtimer_get_remaining(&vibdata.timer);                 //读取剩余时间按并返回
  55. returnktime_to_ms(r);
  56. }
  57. return 0;
  58. }
  59. struct timed_output_dev mx6_motot_driver={
  60. .name ="vibrator",                                  //注意这个名字,由于HAL层里面的设备为//"/sys/class/timed_output/vibrator/enable"
  61. //因此这个名字必须为"vibrator"
  62. .enable= mx6_motor_enable,
  63. .get_time= mx6_get_time,
  64. };
  65. static enum hrtimer_restartmx6_vibrator_timer_func(struct hrtimer * timer) //定时器结束时候的回调函数
  66. {
  67. schedule_work(&vibdata.work);              //定时器完成了 执行work队列回调函数来关闭电机
  68. returnHRTIMER_NORESTART;
  69. }
  70. static void mx6_vibrator_work(struct work_struct *work)//工作队列处理函数,当工作队列执行 当
  71. //schedule_work时候执行
  72. {
  73. mx6_vibrator_off();
  74. }
  75. void __init mx6_motor_init()
  76. {
  77. int ret =0;
  78. hrtimer_init(&vibdata.timer,CLOCK_MONOTONIC, HRTIMER_MODE_REL);//初始化定时器
  79. vibdata.timer.function= mx6_vibrator_timer_func;           //设置回调函数
  80. INIT_WORK(&vibdata.work,mx6_vibrator_work);    //初始化工作队列
  81. ret =gpio_request(SABRESD_VIBRATOR_CTL, "vibrator-en");    //申请IO
  82. if (ret< 0)
  83. {
  84. printk("vibratorrequest IO err!:%d\n",ret);
  85. returnret;
  86. }
  87. wake_lock_init(&vibdata.wklock,WAKE_LOCK_SUSPEND, "vibrator"); //初始化 wake_lock
  88. mutex_init(&vibdata.lock);             //初始化 互斥锁
  89. ret=timed_output_dev_register(&mx6_motot_driver);//注册timed_output 设备
  90. if (ret< 0)
  91. gotoerr_to_dev_reg;
  92. return 0;
  93. err_to_dev_reg:           //错误了 就释放所有资源
  94. mutex_destroy(&vibdata.lock);
  95. wake_lock_destroy(&vibdata.wklock);
  96. gpio_free(SABRESD_VIBRATOR_CTL);
  97. printk("vibrator   err!:%d\n",ret);
  98. returnret;
  99. }
  100. void mx6_motor_exit()
  101. {
  102. mutex_destroy(&vibdata.lock);
  103. wake_lock_destroy(&vibdata.wklock);
  104. gpio_free(SABRESD_VIBRATOR_CTL);
  105. printk("vibrator  exit!\n");
  106. timed_output_dev_register(&mx6_motot_driver);
  107. }
  108. module_init(mx6_motor_init);
  109. module_exit(mx6_motor_exit);
  110. MODULE_AUTHOR("<lijianzhang>");
  111. MODULE_DESCRIPTION("Motor Vibrator driver");
  112. MODULE_LICENSE("GPL");

自此完成了驱动的所有内容,编译,烧写!

有两种方法可以测试是否成功:

其一就是 系统启动后,打开一个带振动的APP看能否实现震动功能。

其二调试口中 向设备文件中写数据.列如:

echo "1000">>/sys/class/timed_output/vibrator/enable          //震动1S中

试验成功! 大功告成!

这里补充一下,关于android 应用程序中震动的的调用方法:在别人博客看到了一个写的很好贴出网址,供大家参考

http://blog.csdn.net/czh4869623/article/details/8956370

这里总结一下:通过这个例程学会了安卓驱动开发的一般步骤,对安卓每个层的认识都有深入。是个非常好的开始。这种从上往下的分析方法只适合于简单的系统,和项目时间要求不高的情况下,我在分析上就浪费了不少时间。项目比较紧张的话,直接百度一个历程按照说明改一下就成了,复杂的系统涉及的东西太多,比如视频之类的,一路分析下去的话可能半个月不一定能搞定。这个驱动属于非常简单,但是实际动手时候,还是多参考别人的例程,毕竟水平不高,再简单的驱动也不一定能想的周全。

Android震动vibrator(马达)--系统到驱动的流程【转】的更多相关文章

  1. Android 12(S) 图形显示系统 - BufferQueue的工作流程(八)

    题外话 最近总有一个感觉:在不断学习中,越发的感觉自己的无知,自己是不是要从"愚昧之巅"掉到"绝望之谷"了,哈哈哈 邓宁-克鲁格效应 一.前言 前面的文章中已经 ...

  2. Android 12(S) 图形显示系统 - BufferQueue的工作流程(九)

    题外话 Covid-19疫情的强烈反弹,小区里检测出了无症状感染者.小区封闭管理,我也不得不居家办公了.既然这么大把的时间可以光明正大的宅家里,自然要好好利用,八个字 == 努力工作,好好学习 一.前 ...

  3. Android 12(S) 图形显示系统 - BufferQueue的工作流程(十)

    题外话 疫情隔离在家,周末还在努力学习的我  ..... 一.前言 上一篇文章中,有基本讲清楚Producer一端的处理逻辑,最后也留下了一个疑问: Consumer是什么时候来消费数据的?他是自己主 ...

  4. Android 12(S) 图形显示系统 - BufferQueue的工作流程(十一)

    题外话 我竟然已经写了这个系列的十一篇文章了,虽然内容很浅显,虽然内容很枯燥,虽然内容也许没营养,但我为自己的坚持点赞! 一.前言 前面的两篇文章,分别讲解了Producer的处理逻辑和queue b ...

  5. Android 12(S) 图形显示系统 - 解读Gralloc架构及GraphicBuffer创建/传递/释放(十四)

    必读: Android 12(S) 图形显示系统 - 开篇 一.前言 在前面的文章中,已经出现过 GraphicBuffer 的身影,GraphicBuffer 是Android图形显示系统中的一个重 ...

  6. Android 12(S) 图像显示系统 - GraphicBuffer同步机制 - Fence

    必读: Android 12(S) 图像显示系统 - 开篇 一.前言 前面的文章中讲解Android BufferQueue的机制时,有遇到过Fence,但没有具体讲解.这篇文章,就针对Fence这种 ...

  7. Android之 看“马达”如何贯通Android系统 (从硬件设计 --> 驱动 --> HAL --> JNI --> Framework --> Application)

    在Android 2.3(Gingerbread) 系统的时候,我写过一篇关于“Android 震动马达系统“的文章,当时的Linux内核还是2.6版本的.写那篇文章的目的,是想彻底的了解从硬件到驱动 ...

  8. Android(java)学习笔记54:Android 调用Vibrator震动功能

    1. 之前我编写的代码是如下: package com.himi.vibrate; import android.app.Activity; import android.app.Service; i ...

  9. 实现android手机来电拦截系统页面弹出自定义页面特效

    如何实现android手机来电拦截系统页面弹出自定义页面特效, 首先:    我们需要注册一个监听来电的广播PhoneStateReceiver 类:其次:    在onReceive里面我们获取an ...

随机推荐

  1. (推荐JsonConvert )序列化和反序列化Json

    在Json文本和.Net对象之间转换最快的方法是试用JsonSerializer. JsonSerializer通过将.Net对象属性名称映射到Json属性名称,并为其复制值,将.Net对象转换为其J ...

  2. Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded.

    EF6进行Insert操作的时候提示错误 Store update, insert, or delete statement affected an unexpected number of rows ...

  3. Django中字典在html中的遍历

    Django中字典在html中的遍历 如:在view中定义的函数 info = {'} context = {"info": info} 要在html中变量出info的信息(注意i ...

  4. js事件委托和jQuery事件绑定on , off , one , bind , unbind , die

    一. 事件委托什么是事件委托?用现实中的理解就是:有100 个学生同时在某天中午收到快递,但这100 个学生不可能同时站在学校门口等,那么都会委托门卫去收取,然后再逐个交给学生.而在jQuery 中, ...

  5. logging/re - 总结

    logging 模块 很多程序都有记录日志的需求 logging的日志可以分为 debug(), info(), warning(), error() and critical()5个级别 1.输出到 ...

  6. SpringMVC 之 RESTful 风格的增删改查

    1. 视图和视图解析器 视图解析器 请求处理方法执行完成后,最终返回一个ModelAndView对象,对于返回String,View 或 ModelMap 等类型的处理方法, SpringMVC 也会 ...

  7. Java 之综合练习

    // 练习一: 写出程序结果 interface A{} class B implements A { public String func() { return "func"; ...

  8. 编译Elasticsearch源码

    1.从github上clone  es的源码 git clone https://github.com/elastic/elasticsearch.git 2.如果没有安装gradle的话,需要安装g ...

  9. python进程锁

    import time import threading import multiprocessing lock = multiprocessing.RLock() def task(arg): pr ...

  10. Django - 权限(2)- 动态显示单级权限菜单

    一.权限组件 1.上篇随笔中,我们只是设计好了权限控制的表结构,有三个模型,五张表,两个多对多关系,并且简单实现了对用户的权限控制,我们会发现那样写有一个问题,就是权限控制写死在了项目中,并且没有实现 ...