安卓9.0马达框架分析
前言
最近需要將之前的一些驅(qū)動(dòng)接口轉(zhuǎn)為安卓標(biāo)準(zhǔn)接口,方便上層應(yīng)用或者第三方應(yīng)用去適配。這篇文章先從簡(jiǎn)單的馬達(dá)框架入手進(jìn)行講解。
正文
整個(gè)馬達(dá)框架比較簡(jiǎn)單,安卓官方已經(jīng)幫我們實(shí)現(xiàn)了framework到HAL層,我們需要實(shí)現(xiàn)的就只有驅(qū)動(dòng)層。這篇文章我們梳理一下從上層到底層怎么流程。
1、APP層
import android.os.Vibrator; import android.widget.ToggleButton;public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);private Vibrator vibrator=null; vibrator=(Vibrator)this.getSystemService(VIBRATOR_SERVICE); toggleButton1=(ToggleButton)findViewById(R.id.toggleButton1); /*短震動(dòng)*/ toggleButton1.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() {@Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(isChecked){Log.i(TAG,"toggleButton1 enter vibrator.vibrate"); //設(shè)置震動(dòng)周期,第二個(gè)參數(shù)為 -1表示只震動(dòng)一次 vibrator.vibrate(new long[]{1000, 10, 100, 1000},-1); }else{ //取消震動(dòng)Log.i(TAG,"toggleButton1 enter vibrator.cancel()"); vibrator.cancel(); } } }); } }上面展示了一個(gè)最簡(jiǎn)單的馬達(dá)震動(dòng)應(yīng)用代碼,獲得服務(wù)后即可調(diào)用接口進(jìn)行驅(qū)動(dòng)。
2、framework層
代碼路徑:frameworks\base\services\core\java\com\android\server\VibratorService.java
? @Override // Binder callpublic void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint,IBinder token) {Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate");try {if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)!= PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires VIBRATE permission");}if (token == null) {Slog.e(TAG, "token must not be null");return;}verifyIncomingUid(uid);if (!verifyVibrationEffect(effect)) {return;}// If our current vibration is longer than the new vibration and is the same amplitude,// then just let the current one finish.synchronized (mLock) {if (effect instanceof VibrationEffect.OneShot&& mCurrentVibration != null&& mCurrentVibration.effect instanceof VibrationEffect.OneShot) {VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;VibrationEffect.OneShot currentOneShot =(VibrationEffect.OneShot) mCurrentVibration.effect;if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())&& newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {if (DEBUG) {Slog.d(TAG,"Ignoring incoming vibration in favor of current vibration");}return;}}// If the current vibration is repeating and the incoming one is non-repeating,// then ignore the non-repeating vibration. This is so that we don't cancel// vibrations that are meant to grab the attention of the user, like ringtones and// alarms, in favor of one-shot vibrations that are likely quite short.if (!isRepeatingVibration(effect)&& mCurrentVibration != null&& isRepeatingVibration(mCurrentVibration.effect)) {if (DEBUG) {Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");}return;}Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);linkVibration(vib);long ident = Binder.clearCallingIdentity();try {doCancelVibrateLocked();startVibrationLocked(vib);addToPreviousVibrationsLocked(vib);} finally {Binder.restoreCallingIdentity(ident);}}} finally {Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);}}接口里面會(huì)判斷一下權(quán)限,根據(jù)應(yīng)用層傳遞的不同effect值,有不同的震動(dòng)效果。然后就調(diào)用到JNI層,調(diào)用順序大概如下:
startVibrationLocked startVibrationInnerLocked doVibratorOn vibratorOn3、JNI層
代碼路徑:frameworks\base\services\core\jni\com_android_server_VibratorService.cpp
static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms) {Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR);if (retStatus != Status::OK) {ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));} }static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */) {Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR);if (retStatus != Status::OK) {ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));} }static const JNINativeMethod method_table[] = {{ "vibratorExists", "()Z", (void*)vibratorExists },{ "vibratorInit", "()V", (void*)vibratorInit },{ "vibratorOn", "(J)V", (void*)vibratorOn },{ "vibratorOff", "()V", (void*)vibratorOff },{ "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},{ "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},{ "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect} };int register_android_server_VibratorService(JNIEnv *env) {return jniRegisterNativeMethods(env, "com/android/server/VibratorService",method_table, NELEM(method_table)); }以馬達(dá)的On和off為例,會(huì)調(diào)用到HAL層的on和off方法。
4、HIDL層
代碼路徑:hardware\interfaces\vibrator\1.0\default\Vibrator.cpp
Return<Status> Vibrator::on(uint32_t timeout_ms) {int32_t ret = mDevice->vibrator_on(mDevice, timeout_ms);if (ret != 0) {ALOGE("on command failed : %s", strerror(-ret));return Status::UNKNOWN_ERROR;}return Status::OK; }Return<Status> Vibrator::off() {int32_t ret = mDevice->vibrator_off(mDevice);if (ret != 0) {ALOGE("off command failed : %s", strerror(-ret));return Status::UNKNOWN_ERROR;}return Status::OK; }HIDL層是較新的安卓版本才引入的,是連接HAL層和JNI層的橋梁。
5、HAL層
代碼路徑:hardware\libhardware\modules\vibrator\vibrator.c
static const char THE_DEVICE[] = "/sys/class/timed_output/vibrator/enable";static int sendit(unsigned int timeout_ms) {char value[TIMEOUT_STR_LEN]; /* large enough for millions of years */snprintf(value, sizeof(value), "%u", timeout_ms);return write_value(THE_DEVICE, value); }static int vibra_on(vibrator_device_t* vibradev __unused, unsigned int timeout_ms) {/* constant on, up to maximum allowed time */return sendit(timeout_ms); }static int vibra_off(vibrator_device_t* vibradev __unused) {return sendit(0); }static int vibra_open(const hw_module_t* module, const char* id __unused,hw_device_t** device __unused) {bool use_led;if (vibra_exists()) {ALOGD("Vibrator using timed_output");use_led = false;} else if (vibra_led_exists()) {ALOGD("Vibrator using LED trigger");use_led = true;} else {ALOGE("Vibrator device does not exist. Cannot start vibrator");return -ENODEV;}vibrator_device_t *vibradev = calloc(1, sizeof(vibrator_device_t));if (!vibradev) {ALOGE("Can not allocate memory for the vibrator device");return -ENOMEM;}vibradev->common.tag = HARDWARE_DEVICE_TAG;vibradev->common.module = (hw_module_t *) module;vibradev->common.version = HARDWARE_DEVICE_API_VERSION(1,0);vibradev->common.close = vibra_close;if (use_led) {vibradev->vibrator_on = vibra_led_on;vibradev->vibrator_off = vibra_led_off;} else {vibradev->vibrator_on = vibra_on;vibradev->vibrator_off = vibra_off;}*device = (hw_device_t *) vibradev;return 0; }其實(shí)開啟和關(guān)閉馬達(dá)的工作很簡(jiǎn)單,就是往節(jié)點(diǎn)"/sys/class/timed_output/vibrator/enable"寫入震動(dòng)時(shí)間,所以可以想得到驅(qū)動(dòng)層只需要提供一個(gè)節(jié)點(diǎn)供上層操作就好。
6、驅(qū)動(dòng)層
馬達(dá)的驅(qū)動(dòng)是基于kernel提供的timed_output框架完成的:
代碼路徑:kernel-4.4\drivers\staging\android\timed_output.c
代碼比較簡(jiǎn)單,提供接口給驅(qū)動(dòng)在"/sys/class/timed_output/"路徑下面建立自己的節(jié)點(diǎn),并提供節(jié)點(diǎn)的device attribute的操作接口,當(dāng)我們寫節(jié)點(diǎn)的時(shí)候就會(huì)調(diào)用到enable_store函數(shù),并調(diào)用注冊(cè)驅(qū)動(dòng)的enable函數(shù)。
static struct class *timed_output_class;static ssize_t enable_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t size) { struct timed_output_dev *tdev = dev_get_drvdata(dev); int value; int rc;rc = kstrtoint(buf, 0, &value); if (rc != 0) return -EINVAL;tdev->enable(tdev, value);return size; } static DEVICE_ATTR_RW(enable);static struct attribute *timed_output_attrs[] = { &dev_attr_enable.attr, NULL, }; ATTRIBUTE_GROUPS(timed_output);static int create_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); timed_output_class->dev_groups = timed_output_groups; }return 0; }int timed_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, "%s", tdev->name); if (IS_ERR(tdev->dev)) return PTR_ERR(tdev->dev);dev_set_drvdata(tdev->dev, tdev); tdev->state = 0; return 0; }現(xiàn)在我們看一下基于上面框架書寫的馬達(dá)驅(qū)動(dòng):
static void vibrator_off(void) { gpio_direction_output(gpio, !en_value); ? ? ? wake_unlock(&vibdata.wklock); //震動(dòng)關(guān)閉就可以釋放 wake_lock鎖 ? ? ? ? }void motor_enable(struct timed_output_dev *sdev,int value) { mutex_lock(&vibdata.lock); //關(guān)鍵代碼段,同一時(shí)間只允許一個(gè)線程執(zhí)行/* cancelprevious timer and set GPIO according to value */ hrtimer_cancel(&vibdata.timer); //當(dāng)先前定時(shí)器完成后 關(guān)閉這個(gè)定時(shí)器 cancel_work_sync(&vibdata.work); //當(dāng)上次震動(dòng)完成后 關(guān)閉這次動(dòng)作 if(value) { wake_lock(&vibdata.wklock); //開始震動(dòng)打開wake lock鎖不允許休眠 gpio_direction_output(gpio, en_value);if(value > 0) { if(value > MAX_TIMEOUT) value= MAX_TIMEOUT; hrtimer_start(&vibdata.timer,ktime_set(value / 1000, (value % 1000) * 1000000),HRTIMER_MODE_REL); } } else vibrator_off();mutex_unlock(&vibdata.lock); }struct timed_output_dev motot_driver = { .name ="vibrator", //注意這個(gè)名字,由于HAL層里面的設(shè)備為//"/sys/class/timed_output/vibrator/enable"//因此這個(gè)名字必須為"vibrator" .enable= motor_enable, .get_time= get_time, };static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) //定時(shí)器結(jié)束時(shí)候的回調(diào)函數(shù) { schedule_work(&vibdata.work); //定時(shí)器完成了 執(zhí)行work隊(duì)列回調(diào)函數(shù)來(lái)關(guān)閉電機(jī) return HRTIMER_NORESTART; } static void vibrator_work(struct work_struct *work) { vibrator_off(); }static int motor_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; enum of_gpio_flags flags; int ret =0;hrtimer_init(&vibdata.timer,CLOCK_MONOTONIC, HRTIMER_MODE_REL); vibdata.timer.function= vibrator_timer_func; INIT_WORK(&vibdata.work,vibrator_work);...ret=timed_output_dev_register(&motot_driver); if (ret< 0) goto err_to_dev_reg; return 0;err_to_dev_reg: mutex_destroy(&vibdata.lock); wake_lock_destroy(&vibdata.wklock);printk("vibrator ? err!:%d\n",ret); return ret;}1、 驅(qū)動(dòng)接收上層傳遞過(guò)來(lái)的是震動(dòng)時(shí)長(zhǎng),單位為毫秒。在驅(qū)動(dòng)里注冊(cè)一個(gè)定時(shí)器,定時(shí)器倒計(jì)時(shí)到期后會(huì)喚醒注冊(cè)的工作隊(duì)列,最終會(huì)執(zhí)行vibrator_work()函數(shù)去關(guān)閉馬達(dá)震動(dòng)。
2、調(diào)用timed_output框架提供的timed_output_dev_register()接口將我們的馬達(dá)驅(qū)動(dòng)注冊(cè)進(jìn)系統(tǒng),這里的關(guān)鍵就是我們需要自定義struct timed_output_dev結(jié)構(gòu)體,填充enable和get_time函數(shù)。enable函數(shù)用來(lái)開啟馬達(dá)的震動(dòng):
void motor_enable(struct timed_output_dev *sdev,int value) { mutex_lock(&vibdata.lock); //關(guān)鍵代碼段,同一時(shí)間只允許一個(gè)線程執(zhí)行/* cancelprevious timer and set GPIO according to value */ hrtimer_cancel(&vibdata.timer); //當(dāng)先前定時(shí)器完成后 關(guān)閉這個(gè)定時(shí)器 cancel_work_sync(&vibdata.work); //當(dāng)上次震動(dòng)完成后 關(guān)閉這次動(dòng)作 if(value) { wake_lock(&vibdata.wklock); //開始震動(dòng)打開wake lock鎖不允許休眠 gpio_direction_output(gpio, en_value);if(value > 0) { if(value > MAX_TIMEOUT) value= MAX_TIMEOUT; hrtimer_start(&vibdata.timer,ktime_set(value / 1000, (value % 1000) * 1000000),HRTIMER_MODE_REL); } } else vibrator_off();mutex_unlock(&vibdata.lock); }開啟震動(dòng)的操作也很簡(jiǎn)單,只是寫一下GPIO,然后重新開啟定時(shí)器,倒計(jì)時(shí)的時(shí)間就是寫入節(jié)點(diǎn)的值,到時(shí)間再把馬達(dá)關(guān)閉就好。
參考鏈接
https://blog.csdn.net/qq_34211365/article/details/105556842
嵌入式Linux
微信掃描二維碼,關(guān)注我的公眾號(hào)
總結(jié)
以上是生活随笔為你收集整理的安卓9.0马达框架分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从Linus Torvalds一封发飙的
- 下一篇: 数据库变为可疑_SQL SERVER 数