一、前言

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

二、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

[cpp] view plaincopy

  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

[cpp] view plaincopy

  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

[cpp] view plaincopy

  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

[cpp] view plaincopy

  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

[cpp] view plaincopy

  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

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

转载于:https://blog.51cto.com/jinchao/1615605

Android震动vibrator系统开发全过程相关推荐

  1. android 震动Vibrator

    /*** 这里使用的是一个长整型数组,数组的a[0]表示静止的时间,a[1]代表的是震动的时间,然后数组的a[2]表示静止的时间,* a[3]代表的是震动的时间--依次类推下去,然后这里的代码有一点小 ...

  2. Android震动vibrator(马达)--系统到驱动的流程【转】

    本文转载自:https://blog.csdn.net/tianshiyalin/article/details/17136723 一.前言 本人刚学习安卓驱动开发,水平不能说菜,是根本没有水平,在这 ...

  3. android mtk 系统架构,Android振动器(Vibrator)系统详解

    平台 MTK6573 Android 振动器系统架构 驱动层 硬件抽象层 JNI框架层 Java应用层 一.驱动层 Android修改.新增Linux内核文件 /kernel/drivers/stag ...

  4. Android手机直播系统开发介绍

    近两年直播热的兴起也带动了直播开发行业的崛起,每个人都想要去吃直播开发这块蛋糕.但这块蛋糕也不是这么容易吃到的,在激烈的市场竞争下,有几个大的直播平台始终占据着市场中较大的份额,也有一些小的公司承受不 ...

  5. Android 驱动和系统开发 1. 一个简单的例子(原创)

    首先,边学习边记录点自己的代码,希望看了我写的代码觉得不怎么样的,多多提出来,让我也学习学习,我一定会虚心接受大家的指导. 这里我们是来学习android 驱动和android系统框架的,这里我只针对 ...

  6. 基于android的新闻系统开发,基于Android的新闻推荐系统的设计与实现

    摘要: 随着智能手机的普及,当前互联网的入口逐渐从传统的电脑端各大门户网站分流到移动互联网当中,在新闻阅读方面,用户也更多的依赖于手机端新闻阅读应用.然而互联网数据的爆炸增长,使得用户在海量新闻中快速 ...

  7. Android 驱动和系统开发 2. 解析模拟器GPS模块 (原创)

    好久没有写技术博客了,恰逢今天还感冒了,这破天气,晚上凉风一吹,就感冒了,要加强锻炼呀. 好了,废话不多说,由于工作需要,我要移植一个虚拟的gps模块,于是乎,我就参考了Android模拟器的gps模 ...

  8. Android 驱动和系统开发 2. 解析模拟器GPS模块

    好久没有写技术博客了,恰逢今天还感冒了,这破天气,晚上凉风一吹,就感冒了,要加强锻炼呀. 好了,废话不多说,由于工作需要,我要移植一个虚拟的gps模块,于是乎,我就参考了android模拟器的gps模 ...

  9. android锁屏软件开发,Android一键锁屏开发全过程

    一.项目简介: 项目:<Android 一键锁屏> 开发周期:4天 代码量:100行 二.项目流程: 三.项目代码 1.主程序代码: 1.private DevicePolicyManag ...

最新文章

  1. 你会去创建一个线程去处理压缩日志并删除吗?
  2. 用python画玫瑰花代码-用python画一朵玫瑰给你
  3. 如何正确使用穿线管 穿线管布局解析
  4. 深度学习 用户画像_一文告诉你什么是用户画像
  5. Visual Studio 2015开发Android App启动调试始终无法完成应用部署的解决方案
  6. 一个人不孤单,想一个人才孤单
  7. JS图片压缩预览/下载
  8. perl socket初步
  9. Deformation Transfer for Triangle Meshes
  10. animate inater插件_基于animate.css动画库的全屏滚动小插件,适用于vue.js(移动端、pc)项目...
  11. C++面向对象的程序开发
  12. visio画图小技巧记录
  13. 考上985的研究生了!
  14. 获取Windows聚焦的图片
  15. esp8266 阿里云 加湿器 天猫精灵
  16. www 53ff com劫持IE,广告网页图标常驻桌面,删了又来
  17. [转]高分一号的落后与特色
  18. radio默认选中并显示相应信息 php,php selectradio和checkbox默认选择的实现方法详解...
  19. 详解Windows PE(Windows预安装环境)
  20. 反燃油车占位方案:AI识别+EasyCVR解决燃油车占位问题

热门文章

  1. 服务器维护以后只有辅助能进,通过服务器信息维护进行“半自动化”运维
  2. android属性动画作用范围,Android开发之动画效果浅析(一)
  3. 电饼锅的样式图片价格_2020年三明治机/电饼铛推荐选购指南,电饼档那个牌子好?有哪些好用的三明治机/早餐机/电饼铛?...
  4. oracle10数据库链接失败,PLSQL Developer连接Oracle 10g或Oracle 11g失败
  5. java 面试题三十三 子类父类方法执行顺序的问题
  6. (六) shiro在web中自定义Realm
  7. linux篇:CenterOS6和Center的区别
  8. 深夜,我偷听到程序员要对session下手.......
  9. Shiro学习记录(详细)
  10. 算法训练 最长字符串 c语言