Android震动vibrator系统开发全过程
一、前言
本人剛學習安卓驅動開發,水平不能說菜,是根本沒有水平,在這里把學習過程貼出來,跟大家一起學習交流,還望大家多多指正,轉載的請標明出處。
二、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
#define?LOG_TAG"VibratorService"??
???
#include"jni.h"??
#include"JNIHelp.h"??
#include"android_runtime/AndroidRuntime.h"??
???
#include<utils/misc.h>??
#include<utils/Log.h>??
#include<hardware_legacy/vibrator.h>??
???
#include<stdio.h>??
???
namespace?android??
{??
???
static?jbooleanvibratorExists(JNIEnv?*env,?jobject?clazz)???????//判斷振動器是否存在??
{??
????return?vibrator_exists()?>?0???JNI_TRUE:?JNI_FALSE;??
}??
???
static?voidvibratorOn(JNIEnv?*env,?jobject?clazz,?jlong?timeout_ms)//打開振動器??
{??
????//?LOGI("vibratorOn\n");??
????vibrator_on(timeout_ms);??
}??
???
static?voidvibratorOff(JNIEnv?*env,?jobject?clazz)//關閉振動器??
{??
????//?LOGI("vibratorOff\n");??
????vibrator_off();??
}??
???
staticJNINativeMethod?method_table[]?=?{??
????{?"vibratorExists","()Z",?(void*)vibratorExists?},??
????{?"vibratorOn",?"(J)V",(void*)vibratorOn?},??
????{?"vibratorOff",?"()V",(void*)vibratorOff?}??
};??
???
intregister_android_server_VibratorService(JNIEnv?*env)????//注冊vibrator服務??
{??
????return?jniRegisterNativeMethods(env,"com/android/server/VibratorService",??
????????????method_table,?NELEM(method_table));??
} ?
從上面代碼可以看出,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
#include<hardware_legacy/vibrator.h>??
#include"qemu.h"??
???
#include<stdio.h>??
#include<unistd.h>??
#include<fcntl.h>??
#include<errno.h>??
???
#define?THE_DEVICE"/sys/class/timed_output/vibrator/enable"??
???
intvibrator_exists()?????????//判斷?振動器是否存在??
{??
????int?fd;??
???
#ifdefQEMU_HARDWARE?????????????????//模擬器情況下實現此功能??
????if?(qemu_check())?{??
????????return?1;??
????}??
#endif??
???
????fd?=?open(THE_DEVICE,?O_RDWR);??
????if(fd?<?0)??
????????return?0;??
????close(fd);??
????return?1;??
}??
???
static?intsendit(int?timeout_ms)???????//打開振動器?timeout_ms?毫秒??
{??
????int?nwr,?ret,?fd;??
????char?value[20];??
???
#ifdefQEMU_HARDWARE????????//模擬器情況下實現次功能??
????if?(qemu_check())?{??
????????return?qemu_control_command("vibrator:%d",?timeout_ms?);??
????}??
#endif??
???
????fd?=?open(THE_DEVICE,?O_RDWR);??
????if(fd?<?0)??
????????return?errno;??
???
????nwr?=?sprintf(value,?"%d\n",timeout_ms);??
????ret?=?write(fd,?value,?nwr);??
???
????close(fd);??
???
????return?(ret?==?nwr)???0?:?-1;??
}??
???
intvibrator_on(int?timeout_ms)??
{??
????/*?constant?on,?up?to?maximum?allowed?time*/??
????return?sendit(timeout_ms);??
}??
???
int?vibrator_off()?????//關閉振動器就是設置振動器打開時間為0??
{??
????return?sendit(0);????????
} ?
分析上面代碼可以看出,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
#include<linux/module.h>??
#include<linux/types.h>??
#include<linux/device.h>??
#include<linux/fs.h>??
#include<linux/err.h>??
???
#include"timed_output.h"??
???
static?structclass?*timed_output_class;??
static?atomic_tdevice_count;??
???
static?ssize_t?enable_show(structdevice?*dev,?struct?device_attribute?*attr,??
?????????????char?*buf)??
{??
????struct?timed_output_dev?*tdev?=dev_get_drvdata(dev);??
????int?remaining?=?tdev->get_time(tdev);??
???
????return?sprintf(buf,?"%d\n",remaining);??
}??
???
static?ssize_tenable_store(??
?????????????struct?device?*dev,?structdevice_attribute?*attr,??
?????????????const?char?*buf,?size_t?size)??
{??
????struct?timed_output_dev?*tdev?=dev_get_drvdata(dev);??
????int?value;??
???
????if?(sscanf(buf,?"%d",?&value)!=?1)??
?????????????return?-EINVAL;??
???
????tdev->enable(tdev,?value);??
???
????return?size;??
}??
???
static?DEVICE_ATTR(enable,S_IRUGO?|?S_IWUSR,?enable_show,?enable_store);??
???
static?intcreate_timed_output_class(void)??
{??
????if?(!timed_output_class)?{??
?????????????timed_output_class?=class_create(THIS_MODULE,?"timed_output");??
?????????????if?(IS_ERR(timed_output_class))??
???????????????????????return?PTR_ERR(timed_output_class);??
?????????????atomic_set(&device_count,?0);??
????}??
???
????return?0;??
}??
???
inttimed_output_dev_register(struct?timed_output_dev?*tdev)??
{??
????int?ret;??
???
????if?(!tdev?||?!tdev->name?||!tdev->enable?||?!tdev->get_time)??
?????????????return?-EINVAL;??
???
????ret?=?create_timed_output_class();??
????if?(ret?<?0)??
?????????????return?ret;??
???
????tdev->index?=atomic_inc_return(&device_count);??
????tdev->dev?=device_create(timed_output_class,?NULL,??
?????????????MKDEV(0,?tdev->index),?NULL,tdev->name);??
????if?(IS_ERR(tdev->dev))??
?????????????return?PTR_ERR(tdev->dev);??
???
????ret?=?device_create_file(tdev->dev,&dev_attr_enable);??
????if?(ret?<?0)??
?????????????goto?err_create_file;??
???
????dev_set_drvdata(tdev->dev,?tdev);??
????tdev->state?=?0;??
????return?0;??
???
err_create_file:??
????device_destroy(timed_output_class,?MKDEV(0,tdev->index));??
????printk(KERN_ERR?"timed_output:?Failedto?register?driver?%s\n",??
???????????????????????tdev->name);??
???
????return?ret;??
}??
EXPORT_SYMBOL_GPL(timed_output_dev_register);??
???
voidtimed_output_dev_unregister(struct?timed_output_dev?*tdev)??
{??
????device_remove_file(tdev->dev,&dev_attr_enable);??
????device_destroy(timed_output_class,?MKDEV(0,tdev->index));??
????dev_set_drvdata(tdev->dev,?NULL);??
}??
EXPORT_SYMBOL_GPL(timed_output_dev_unregister);??
???
static?int?__inittimed_output_init(void)??
{??
????return?create_timed_output_class();??
}??
???
static?void?__exittimed_output_exit(void)??
{??
????class_destroy(timed_output_class);??
}??
???
module_init(timed_output_init);??
module_exit(timed_output_exit);??
???
MODULE_AUTHOR("MikeLockwood?<lockwood@android.com>");??
MODULE_DESCRIPTION("timedoutput?class?driver");??
MODULE_LICENSE("GPL"); ?
\kernel\drivers\staging\android\timed_output.h
[cpp]?view plaincopy
#ifndef?_LINUX_TIMED_OUTPUT_H??
#define?_LINUX_TIMED_OUTPUT_H??
???
struct?timed_output_dev?{??
?????????constchar??*name;??
???
?????????/*?enablethe?output?and?set?the?timer?*/??
?????????void???(*enable)(struct?timed_output_dev?*sdev,?inttimeout);??
???
?????????/*returns?the?current?number?of?milliseconds?remaining?on?the?timer?*/??
?????????int??????????????(*get_time)(structtimed_output_dev?*sdev);??
???
?????????/*private?data?*/??
?????????structdevice???????*dev;??
?????????int??????????????index;??
?????????int??????????????state;??
};??
???
extern?int?timed_output_dev_register(struct?timed_output_dev*dev);??
extern?void?timed_output_dev_unregister(structtimed_output_dev?*dev);??
???
#endif??
? ?
分析上面代碼可以看出,我們的驅動是要實現timed_output_dev結構體,然后注冊這個結構體就行了。下面我們開始真正動手。由于本人水平有限,參考了samung一個公開kernel的代碼里的馬達驅動。寫出了自己的驅動:
本人硬件的馬達通過P4.17腳控制 高打開 低關閉
\kernel_imx\drivers\vibrator\vibrator.c
[cpp]?view plaincopy
#include?<linux/hrtimer.h>??
#include?<linux/err.h>??
#include?<linux/gpio.h>??
#include?<linux/wakelock.h>??
#include?<linux/mutex.h>??
#include?<linux/clk.h>??
#include?<linux/workqueue.h>??
#include?<asm/mach-types.h>??
#include?<linux/kernel.h>??
#include?<linux/module.h>??
???
#include<../drivers/staging/android/timed_output.h>??
???
#define?IMX_GPIO_NR(bank,?nr)?????????????(((bank)?-?1)?*?32?+?(nr))????????//IO定義??
#define?SABRESD_VIBRATOR_CTL???????????????????IMX_GPIO_NR(4,?17)???//電機通過P4.17腳控制?高打開?低關閉??
#define?MAX_TIMEOUT????????10000/*?10s?*/??//最長可打開10s??
???
?static?structvibrator?{??
?????????structwake_lock?wklock;??????//wake_lock?防止震動過程中系統休眠,線程不釋放此設備,造成不必要錯誤??
?????????structhrtimer?timer;????//高精度定時器??
?????????structmutex?lock;????????//互斥鎖,防止多線程同時訪問這個設備.??
?????????structwork_struct?work;?//設備操作隊列,用于一次操作完成和下一次開始同步用?(三星這么用的,具體為什么不直接用回調函數,我也不懂,還望大神們私信給個說明?感激不盡)??
}?vibdata;??
???
static?void?mx6_vibrator_off(void)??
{??
???
?????????gpio_direction_output(SABRESD_VIBRATOR_CTL,0);?????????
?????????wake_unlock(&vibdata.wklock);??????????????//震動關閉就可以釋放?wake_lock鎖??
??????????
}??
void?mx6_motor_enable(struct?timed_output_dev?*sdev,int?value)??
{??
?????????mutex_lock(&vibdata.lock);?????????????????????//關鍵代碼段,同一時間只允許一個線程執行??
??????????
?????????/*?cancelprevious?timer?and?set?GPIO?according?to?value?*/??
?????????hrtimer_cancel(&vibdata.timer);????????????//當先前定時器完成后?關閉這個定時器??
?????????cancel_work_sync(&vibdata.work);?????????//當上次震動完成后?關閉這次動作??
?????????if(value)??
?????????{??
???????????????????wake_lock(&vibdata.wklock);?????????//開始震動打開wake?lock鎖不允許休眠??
???????????????????gpio_direction_output(SABRESD_VIBRATOR_CTL,1);??
??????????
???????????????????if(value?>?0)??
???????????????????{??
????????????????????????????if(value?>?MAX_TIMEOUT)??
?????????????????????????????????????value=?MAX_TIMEOUT;??
????????????????????????????value+=?45;????????????????????????????????????//為了使震動變得明顯,固定增加一個時間.跟硬件有關系??
????????????????????????????hrtimer_start(&vibdata.timer,?????????????????//開始定時器??
?????????????????????????????????????ns_to_ktime((u64)value*?NSEC_PER_MSEC),??
?????????????????????????????????????HRTIMER_MODE_REL);??
???????????????????}??
?????????}??
?????????else??
???????????????????mx6_vibrator_off();??
???
?????????mutex_unlock(&vibdata.lock);?????????????????//關鍵代碼段執行完成,釋放互斥鎖??
???
???
}??
int?????mx6_get_time(structtimed_output_dev?*sdev)??
{??
?????????if(hrtimer_active(&vibdata.timer))??
?????????{??
???????????????????ktime_tr?=?hrtimer_get_remaining(&vibdata.timer);?????????????????//讀取剩余時間按并返回??
???????????????????returnktime_to_ms(r);??
?????????}??
??????????
?????????return?0;??
}??
struct?timed_output_dev?mx6_motot_driver={??
.name?="vibrator",??????????????????????????????????//注意這個名字,由于HAL層里面的設備為//"/sys/class/timed_output/vibrator/enable"??
?????????????????????????????????????????????????????????????????//因此這個名字必須為"vibrator"??
???????????????????.enable=?mx6_motor_enable,??
???????????????????.get_time=?mx6_get_time,??
};??
???
static?enum?hrtimer_restartmx6_vibrator_timer_func(struct?hrtimer?*?timer)?//定時器結束時候的回調函數??
{??
?????????schedule_work(&vibdata.work);??????????????//定時器完成了?執行work隊列回調函數來關閉電機??
?????????returnHRTIMER_NORESTART;??
}??
static?void?mx6_vibrator_work(struct?work_struct?*work)//工作隊列處理函數,當工作隊列執行?當??
//schedule_work時候執行??
{??
?????????mx6_vibrator_off();??
}??
???
???
void?__init?mx6_motor_init()??
{??
?????????int?ret?=0;??
?????????hrtimer_init(&vibdata.timer,CLOCK_MONOTONIC,?HRTIMER_MODE_REL);//初始化定時器??
?????????vibdata.timer.function=?mx6_vibrator_timer_func;???????????//設置回調函數??
?????????INIT_WORK(&vibdata.work,mx6_vibrator_work);????//初始化工作隊列??
?????????ret?=gpio_request(SABRESD_VIBRATOR_CTL,?"vibrator-en");????//申請IO??
?????????if?(ret<?0)??
?????????{??
???????????????????printk("vibratorrequest?IO?err!:%d\n",ret);??
???????????????????returnret;??
?????????}??
?????????wake_lock_init(&vibdata.wklock,WAKE_LOCK_SUSPEND,?"vibrator");?//初始化?wake_lock??
?????????mutex_init(&vibdata.lock);?????????????//初始化?互斥鎖??
?????????ret=timed_output_dev_register(&mx6_motot_driver);//注冊timed_output?設備??
?????????if?(ret<?0)??
???????????????????gotoerr_to_dev_reg;??
?????????return?0;??
err_to_dev_reg:???????????//錯誤了?就釋放所有資源??
?????????mutex_destroy(&vibdata.lock);??
?????????wake_lock_destroy(&vibdata.wklock);??
???
?????????gpio_free(SABRESD_VIBRATOR_CTL);??
?????????printk("vibrator???err!:%d\n",ret);??
?????????returnret;??
??????????
}??
void?mx6_motor_exit()??
{??
?????????mutex_destroy(&vibdata.lock);??
?????????wake_lock_destroy(&vibdata.wklock);??
?????????gpio_free(SABRESD_VIBRATOR_CTL);??
?????????printk("vibrator??exit!\n");??
?????????timed_output_dev_register(&mx6_motot_driver);??
}??
module_init(mx6_motor_init);??
module_exit(mx6_motor_exit);??
???
MODULE_AUTHOR("<lijianzhang>");??
MODULE_DESCRIPTION("Motor?Vibrator?driver");??
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系统开发全过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows和linux下的spice
- 下一篇: Learn Python the Har