上一篇讲到vibrate-系统服务,这一篇讲一下架构,
https

文章目录

  • 1app
  • 2fwk
  • 3jni
  • 4 hidl
  • 5hal
  • 4 driver

1app

import android.os.Vibrator;
import android.widget.ToggleButton;public class MainActivity extends Activity {@Overrideprotected 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);/*短震动*/toggleButton1.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {if(isChecked){Log.i(TAG,"toggleButton1 enter vibrator.vibrate");//设置震动周期,第二个参数为 -1表示只震动一次vibrator.vibrate(new long[]{1000, 10, 100, 1000},-1);}else{//取消震动Log.i(TAG,"toggleButton1 enter vibrator.cancel()");vibrator.cancel();}}});}
}

2fwk


//    代码路径:frameworks\base\services\core\java\com\android\server\VibratorService.java
@Override // Binder call
public 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 our current vibration is longer than the new vibration and is the same amplitude,// then just let the current one finish.synchronized (mLock) {。。。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);}
}

接口里面会判断一下权限,根据应用层传递的不同effect值,有不同的震动效果。然后就调用到JNI层,调用顺序大概如下:
startVibrationLocked->startVibrationInnerLocked->doVibratorOn->vibrateOn->VibratorThread->playWaveform->doVibratorOn->vibrateOn

3jni


//代码路径:frameworks\base\services\core\jni\com_android_server_VibratorService.cpp
static void vibratorInit(JNIEnv /* env */, jobject /* clazz */)
{halCall(&V1_0::IVibrator::ping).isOk();
}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));
}

4 hidl

//代码路径:hardware\interfaces\vibrator\1.0\default\Vibrator.cpp
//采用直通式hidl,HIDL_FETCH_IVibrator函数中执行hw_get_module#define LOG_TAG "VibratorService"#include <inttypes.h>#include <log/log.h>#include <hardware/hardware.h>
#include <hardware/vibrator.h>#include "Vibrator.h"namespace android {namespace hardware {namespace vibrator {namespace V1_0 {namespace implementation {Vibrator::Vibrator(vibrator_device_t *device) : mDevice(device) {}// Methods from ::android::hardware::vibrator::V1_0::IVibrator follow.
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;
}IVibrator* HIDL_FETCH_IVibrator(const char * /*hal*/) {vibrator_device_t *vib_device;const hw_module_t *hw_module = nullptr;int ret = hw_get_module(VIBRATOR_HARDWARE_MODULE_ID, &hw_module);if (ret == 0) {ret = vibrator_open(hw_module, &vib_device);if (ret != 0) {ALOGE("vibrator_open failed: %d", ret);}} else {ALOGE("hw_get_module %s failed: %d", VIBRATOR_HARDWARE_MODULE_ID, ret);}if (ret == 0) {return new Vibrator(vib_device);} else {ALOGE("Passthrough failed to open legacy HAL.");return nullptr;}
}} // namespace implementation
}  // namespace V1_0
}  // namespace vibrator
}  // namespace hardware
}  // namespace android

5hal

//代码路径: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;
}

其实开启和关闭马达的工作很简单,就是往节点"/sys/class/timed_output/vibrator/enable"写入震动时间,所以可以想得到驱动层只需要提供一个节点供上层操作就好

4 driver

马达的驱动是基于kernel提供的timed_output框架完成的:

代码比较简单,提供接口给驱动在"/sys/class/timed_output/"路径下面建立自己的节点,并提供节点的device attribute的操作接口,当我们写节点的时候就会调用到enable_store函数,并调用注册驱动的enable函数。

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;
}

看一下基于上面框架书写的马达驱动:

static void vibrator_off(void)
{gpio_direction_output(gpio, !en_value);       wake_unlock(&vibdata.wklock); //震动关闭就可以释放 wake_lock锁
}void 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(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);}}elsevibrator_off();mutex_unlock(&vibdata.lock);
}struct timed_output_dev motot_driver = {.name ="vibrator", //注意这个名字,由于HAL层里面的设备为//"/sys/class/timed_output/vibrator/enable"//因此这个名字必须为"vibrator".enable= motor_enable,.get_time= get_time,
};static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) //定时器结束时候的回调函数
{schedule_work(&vibdata.work); //定时器完成了 执行work队列回调函数来关闭电机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. 驱动接收上层传递过来的是震动时长,单位为毫秒。在驱动里注册一个定时器,定时器倒计时到期后会唤醒注册的工作队列,最终会执行vibrator_work()函数去关闭马达震动。
  2. 调用timed_output框架提供的timed_output_dev_register()接口将我们的马达驱动注册进系统,这里的关键就是我们需要自定义struct timed_output_dev结构体,填充enable和get_time函数。enable函数用来开启马达的震动:
void 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(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);}}elsevibrator_off();mutex_unlock(&vibdata.lock);
}

开启震动的操作也很简单,只是写一下GPIO,然后重新开启定时器,倒计时的时间就是写入节点的值,到时间再把马达关闭就好。

2. vibrate-arch相关推荐

  1. arch linux安装_如何从头开始安装Arch Linux

    arch linux安装 by Andrea Giammarchi 由Andrea Giammarchi In this article, you'll learn how to install Ar ...

  2. Arch安装zsh以及通过 Oh-My-ZSH! 开源项目的配置

    Arch安装zsh以及通过 Oh-My-ZSH! 开源项目的配置 首先:安装官方源的 zsh $ sudo pacman -S zsh 安装 wget .git 并获取开源项目的安装脚本 $ sudo ...

  3. arch更新失败的办法

    使用arch 的日常更新的时候,由于软件包巨大或者镜像不稳定的时候,会发现失败的场景,这个时候可以安装一部分更新,选择忽略哪些不能更新的部分. 在上面加 – ignore packagename

  4. 为什么应该安装使用 Arch Linux

    Arch Linux 无疑是 Linux 高级用户最好的发行版之一.但是在安装 Arch 之前,您应该了解一些关于 Arch 的事情. 安装大多数 Linux 发行版时,您只需下载 ISO,创建可启动 ...

  5. ICCV 2021 | ARCH++: 可直接用于动画的穿衣服人体重建

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨人脸人体重建 来源丨人脸人体重建 元宇宙时代,什么最关键?当然是虚拟化身的获取像拍摄一张照片一样 ...

  6. 安装完Arch后,要安装的软件

    输入法 sunpinyin intellij idea.QTcreator 文字编辑器 LaTex.texstudio libreoffice wps-office没有公式编辑器,但是libreoff ...

  7. Arch Linux 指南——安装基本系统

    目录 Arch Linux 指南--安装基本系统 安装前准备 环境检查 分区 安装 配置基础系统 完成安装 Arch Linux 指南--安装基本系统 本文是自己安装 Arch Linux 的过程,记 ...

  8. linux arch 软件管理工具 pacman 简介

    目录 一,什么是pacman 二,pacman参数详细说明 三,一些常規用法 一,什么是pacman Pacman 是一个 软件包管理器, 作为 ArchLinux发行版的一部分. 它最早由 Arch ...

  9. arch linux引导不启动_Linux 内核源代码的目录结构

    内核技术点合集 Linux 内核源代码包括三个主要部分: 1. 内核核心代码,包括第 3 章所描述的各个子系统和子模块,以及其它的支撑子系统,例 如电源管理.Linux 初始化等 2. 其它非核心代码 ...

  10. linux 版本 arch,Arch Linux是什么

    Arc Linux(或称Arc)是一种以轻量简洁为设计理念的Linux发行版.其开发团队秉承简洁.优雅.正确和代码最小化的设计宗旨.Arc Linux项目受CRUX启发,由Judd Vinet于200 ...

最新文章

  1. Spark版本定制第12天:Executor容错安全性
  2. MySQL集群(一)之主从复制
  3. 10张图带你深入理解Docker容器和镜像
  4. Dreamweaver 2020安装教程
  5. Web前端初学者,需用了解的7大HTML知识点
  6. php 在线api文档生成,一键生成API文档
  7. mysql8初始化 2021-12-18版本 设置mysql大写
  8. 牛顿二项式定理(广义二项式定理)
  9. Kmeans、Kmeans++、Birch和KNN四种聚类算法对二维坐标点的聚类分析对比实验
  10. 从入门到放弃,50G编程视频免费送!
  11. php fopen 追加,PHP文件写入或追加
  12. scss exceeded maximum budget. Budget 4.00 kB was not met by 130 bytes with a total of 4.13 kB.
  13. 虚拟网络的无损保证-zOVN
  14. SQLServer之创建唯一非聚集索引
  15. 【Python 22】52周存钱挑战2.0(列表list和math函数)
  16. spring工作机制及原理
  17. kudu on impala 基本用法。
  18. 如何将优酷KUX格式转换为MP4格式?
  19. Python第三方库tabulate简单使用说明
  20. 三维数据入到arcgis平台的解决方案

热门文章

  1. 区块链概念正宗龙头股
  2. 第七天 03.python环境安装
  3. 【多线程/线程池】项目中实际应用场景
  4. 技术网站 常用的技术网站
  5. 将计算机放在什么地方英语,怎么把电脑设置为英文版的系统
  6. Matlab基本函数-floor函数
  7. git学习——上传项目代码到github
  8. 2021年安全员-A证最新解析及安全员-A证试题及解析
  9. 单测利器——PowerMockito使用心得
  10. 大数据总结【第四章:Hbase】