本文轉載自: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
- #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
- #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
- #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
- #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
- #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
這里總結一下:通過這個例程學會了安卓驅動開發的一般步驟,對安卓每個層的認識都有深入。是個非常好的開始。這種從上往下的分析方法只適合於簡單的系統,和項目時間要求不高的情況下,我在分析上就浪費了不少時間。項目比較緊張的話,直接百度一個歷程按照說明改一下就成了,復雜的系統涉及的東西太多,比如視頻之類的,一路分析下去的話可能半個月不一定能搞定。這個驅動屬於非常簡單,但是實際動手時候,還是多參考別人的例程,畢竟水平不高,再簡單的驅動也不一定能想的周全。