安卓9.0马达框架分析

前言

最近需要将之前的一些驱动接口转为安卓标准接口,方便上层应用或者第三方应用去适配。这篇文章先从简单的马达框架入手进行讲解。

正文

整个马达框架比较简单,安卓官方已经帮我们实现了framework到HAL层,我们需要实现的就只有驱动层。这篇文章我们梳理一下从上层到底层怎么流程。

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);
/*短震动*/
toggleButton1.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() {@Override
public 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();
}
}
});
}
}

上面展示了一个最简单的马达震动应用代码,获得服务后即可调用接口进行驱动。

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

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

startVibrationLocked
startVibrationInnerLocked
doVibratorOn
vibratorOn

3、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));
}

以马达的On和off为例,会调用到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;
}

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

6、驱动层

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

代码路径:kernel-4.4\drivers\staging\android\timed_output.c

代码比较简单,提供接口给驱动在"/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);
}
}
else
vibrator_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);
}
}
else
vibrator_off();mutex_unlock(&vibdata.lock);
}

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

参考链接

https://blog.csdn.net/qq_34211365/article/details/105556842

嵌入式Linux

微信扫描二维码,关注我的公众号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/467613.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PYQT4 Python GUI 编写与 打包.exe程序

工作中需要开发一个小工具&#xff0c;简单的UI界面可以很好的提高工具的实用性&#xff0c;由此开启了我的第一次GUI开发之旅&#xff0c;下面将自己学习的心得记录一下&#xff0c;也做为学习笔记吧&#xff01;&#xff01;&#xff01; 参考&#xff1a;http://www.qaulau.…

你知道嵌入式,那你看过这个吗?

大家好&#xff0c;因为最近各种原因&#xff0c;我身边的很多同事都转行摆地摊了&#xff0c;可能因为那是一份自由的职业&#xff0c;摆地摊可以从事的范围很广&#xff0c;也不用起早贪黑了&#xff0c;而且收入并不低。也是因为这样&#xff0c;很多嵌入式方面的岗位越来越…

mvc一对多模型表单的快速构建

功能需求描述 Q:在实际的开发中&#xff0c;经常会遇到一个模型中包含有多个条目的表单。如何将数据提交到后台&#xff1f; A: 以数组的形式提交到后台就Ok了(真的那么简单么&#xff0c;如果再嵌套一层呢&#xff1f;) A2&#xff1a;拆分多个模型&#xff0c;映射就没啥问题…

c语言中 if(x) 、if(0) 、if(1)

解释if 语句里面包含真和非真&#xff0c;但是如果我们没有写清楚真和非真的话&#xff0c;会如何呢&#xff1f;if(x)相当于if(x ! 0)如果是指针的话&#xff0c;相当于if(x ! NULL)而if(1)相当于if(1 ! 0)还有if(0)相当于if(0 ! 0)举个例子#include<stdio.h> int main(…

看Linus骂人,真解气

感受下Linus骂人的感觉吧&#xff0c; 这样你会觉得工作中遇到的那些不愉快就算个鸟事背景一个Linux主线的内核维护者提交了一份patch&#xff0c;并说明问题产生的原因是因为应用传的音频有问题。Linus回复如下你他娘的给老子闭嘴&#xff01;这是一个内核bug好不好&#xff0…

不就是要个30K的薪资,他还问我Nginx调优

我是一个运维“老鸟”&#xff0c;目前在到处找工作阶段。周三刚面试完一家公司&#xff0c;还是非常中意的公司。结果是我中意公司&#xff0c;公司不中意我&#xff0c;妥妥的黄了。面试完我才知道&#xff0c;Linux云计算工程师必须能精通20多个企业级服务器优化。我之前不是…

android导出apk文件_Android测试工具入门介绍(三)

介绍一款牛逼的测试框架Drozer&#xff0c;一款可以检测Android一些公共漏洞的工具&#xff08;可能远不止这些、还可以继续挖掘&#xff09;&#xff0c;还可以生成shellcode&#xff0c;进行安卓设备的远程exploit。附下载地址&#xff1a;https://github.com/mwrlabs/drozer…

bomb炸弹

今天看到的一个Linux shell命令&#xff0c;但是我先说下&#xff0c;这个命令是危险的&#xff0c;所以没事的时候不要随便执行&#xff0c;出现了各种危险不要怪我没有提前告诉你哈。DANGER!命令代码:(){ :|: & };:命令解析1:() 意思是定义了一个函数&#xff0c;这个函数…

kindle的xray怎么用_Xray简单使用教程

Xray简单使用教程0X00下载xray 为单文件二进制文件&#xff0c;无依赖&#xff0c;也无需安装&#xff0c;下载后直接使用。下载地址为&#xff1a;注意&#xff1a; 不要直接 clone 仓库&#xff0c;xray 并不开源&#xff0c;仓库内不含源代码&#xff0c;直接下载构建的二进…

文件方式实现完整的英文词频统计实例(9.27)

1.读入待分析的字符串 2.分解提取单词 3.计数字典 4.排除语法型词汇 5.排序 6.输出TOP(20) 文本代码如下&#xff1a; girlRemembering me, Discover and see All over the world, Shes known as a girl To those who a free, The mind shall be key Forgotten as the past Ca…

UNUSED参数,这个宏,很秀

前言你们有没有在写代码的时候&#xff0c;遇到有的参数&#xff0c;从函数体里面传进来&#xff0c;但是又用不上&#xff0c;所以就不引用&#xff0c;但是不引用&#xff0c;在编译的时候&#xff0c;就会提示错误。是不是很尴尬&#xff0c;我们不使用&#xff0c;并不是错…

利用Python对文件进行批量重命名——以图片文件为例

效果如下&#xff1a;0001号用户的第 i 张图片 代码&#xff1a; import osclass ImageRename():def __init__(self):self.path C:/Users/lbpeng/Desktop/test/chictopia2/images1/fashioninmysoul/fulldef rename(self):filelist os.listdir(self.path)totalnum len(fileli…

mysql双重分组没有值也要显示_mysql 统计数据,按照日期分组,把没有数据的日期也展示出来...

因为业务需求&#xff0c;要统计每天的新增用户并且要用折线图的方式展示。如果其中有一天没有新增用户的话&#xff0c;这一天就是空缺的&#xff0c;在绘制折线图的时候是不允许的&#xff0c;所有要求把没有数据的日期也要在图表显示。查询2019-01-10------2019-01-20日的新…

我一个专科生,还有未来吗?

今天分享一个星球里面的讨论你好&#xff0c;我加入这个星球也算比较久了在此之前也一直都是在观望&#xff0c;我是一个19年因为高考失利而没有选择复读的专科生&#xff0c;我选择的专业是嵌入式技术与应用&#xff0c;最近不知道为什么特别迷茫&#xff0c;在选择读专科之前…

mysql查询数据库第一条记录_SQL获取第一条记录的方法(sqlserver、oracle、mysql数据库)...

Sqlserver 获取每组中的第一条记录在日常生活方面&#xff0c;我们经常需要记录一些操作&#xff0c;类似于日志的操作&#xff0c;最后的记录才是有效数据&#xff0c;而且可能它们属于不同的方面、功能下面&#xff0c;从数据库的术语来说&#xff0c;就是查找出每组中的一条…

Linux select/poll机制原理分析

转载一篇文章&#xff0c;讲解select和poll机制的&#xff0c;分享给大家。前言Read the fucking source code! --By 鲁迅A picture is worth a thousand words. --By 高尔基1. 概述Linux系统在访问设备的时候&#xff0c;存在以下几种IO模型&#xff1a;Blocking IO Model&am…

SSM框架的搭建学习(1)---MyBatis的环境搭建

SSM(SpringSpringMVCMyBatis)框架为当今最为流行的WEB开发框架之一,基本上涉及数据库的一些增删改查操作都可以借用此框架,本尊此前接的一个小公司关于楼宇空调监控指标的项目就是基于此框架,只不过当时框架已经被别人搭建好,只等我去添砖加瓦,并没有从头开始对此框架进行着手搭…

argv python 提示输入_Python解释器

一、Python解释器我们编写的Python代码都要放在Python解释器上运行&#xff0c;解释器是代码与计算机硬件之间的软件逻辑层。当我们在操作系统上安装好Python之后&#xff0c;它就包含了保证Python运行的最小化组件&#xff1a;解释器 和 标准库。根据选用的Python版本的不同&a…

高阶篇:8.1)开模前评审及提交资料;

本章目的&#xff1a;明确开模前需要提交的资料&#xff0c;确保开模一次性成功。 在现有机械行业制作技术的大环境下&#xff0c;除却必要的机加工零件之外&#xff0c;大部分的零件量产都需要开模制作。如塑胶、钣金、压铸、粉末冶金、锻造等。 但是模具是很贵的&#xff08;…

Android系统充电系统介绍-预防手机充电爆炸

1、锂电池介绍锂离子电池由日本索尼公司于1990年最先开发成功。它是把锂离子嵌入碳&#xff08;石油焦炭和石墨&#xff09;中形成负极&#xff08;传统锂电池用锂或锂合金作负极&#xff09;。正极材料常用LixCoO2 ,也用 LixNiO2 &#xff0c;和LixMnO4 &#xff0c;电解液用L…