您的位置:首页 > 移动开发 > Android开发

android timed output vibrator driver

2014-05-05 19:08 495 查看
#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/types.h>

#include <linux/device.h>

#include <linux/workqueue.h>

#include "timed_output.h"

#include <linux/hrtimer.h>

#include <linux/err.h>

#include <linux/platform_device.h>

#include <linux/spinlock.h>

#include <linux/jiffies.h>

#include <linux/timer.h>

#include <linux/mutex.h>

#include <mach/mt_typedefs.h>

#define VIB_DEVICE "vibrator"

static struct workqueue_struct *vibrator_queue;

static struct work_struct work_vibrator;

static int vibe_state;

static struct hrtimer vibe_timer;

static DEFINE_MUTEX(vibe_mtx);

static spinlock_t vibe_lock;

extern void motor_enable(bool enable);

static int vibr_Enable(void)

{

motor_enable(1);

return 0;

}

static int vibr_Disable(void)

{

motor_enable(0);

return 0;

}

static void update_vibrator(struct work_struct *work)

{

set_vibrator(vibe_state);

}

static void vibrator_enable(struct timed_output_dev *dev, int value)

{

unsigned long flags;

spin_lock_irqsave(&vibe_lock, flags);

hrtimer_cancel(&vibe_timer);

if (value == 0)

vibe_state = 0;

else {

value = (value > 15000 ? 15000 : value);

vibe_state = 1;

hrtimer_start(&vibe_timer,

ktime_set(value / 1000, (value % 1000) * 1000000),

HRTIMER_MODE_REL);

}

spin_unlock_irqrestore(&vibe_lock, flags);

queue_work(vibrator_queue,&work_vibrator);

}

static int vibrator_get_time(struct timed_output_dev *dev)

{

if (hrtimer_active(&vibe_timer)) {

ktime_t r = hrtimer_get_remaining(&vibe_timer);

return r.tv.sec * 1000 + r.tv.nsec / 1000000;

} else

return 0;

}

static void set_vibrator(int on)

{

if(mutex_lock_interruptible(&vibe_mtx))

return;

if (on)

vibr_Enable();

else

vibr_Disable();

mutex_unlock(&vibe_mtx);

}

static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)

{

vibe_state = 0;

queue_work(vibrator_queue,&work_vibrator);

return HRTIMER_NORESTART;

}

static struct timed_output_dev timed_vibrator = {

.name = "vibrator",

.get_time = vibrator_get_time,

.enable = vibrator_enable,

};

static int vib_probe(struct platform_device *pdev)

{

return 0;

}

static int vib_remove(struct platform_device *pdev)

{

return 0;

}

static void vib_shutdown(struct platform_device *pdev)

{

unsigned long flags;

printk("[vibrator]vib_shutdown: enter!\n");

spin_lock_irqsave(&vibe_lock, flags);

if(vibe_state) {

printk("[vibrator]vib_shutdown: vibrator will disable \n");

vibe_state = 0;

spin_unlock_irqrestore(&vibe_lock, flags);

vibr_Disable();

goto vibr_end; /*don't spinunlock twice*/

}

spin_unlock_irqrestore(&vibe_lock, flags);

vibr_end:

return;

}

/******************************************************************************

Device driver structure

*****************************************************************************/

static struct platform_driver vibrator_driver =

{

.probe = vib_probe,

.remove
= vib_remove,

.shutdown = vib_shutdown,

.driver = {

.name = VIB_DEVICE,

.owner = THIS_MODULE,

},

};

static struct platform_device vibrator_device =

{

.name = "vibrator",

.id = -1,

};

static ssize_t store_vibr_on(struct device *dev,struct device_attribute *attr, const char *buf, size_t size)

{

if(buf != NULL && size != 0)

{

printk("[vibrator]buf is %s and size is %d \n",buf,size);

if(buf[0]== '0')

{

vibr_Disable();

}else

{

vibr_Enable();

}

}

return size;

}

static DEVICE_ATTR(vibr_on, 0220, NULL, store_vibr_on);

static void __init init_vibrator(void)

{

int ret;

INIT_WORK(&work_vibrator, update_vibrator);

spin_lock_init(&vibe_lock);

vibe_state = 0;

hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);

vibe_timer.function = vibrator_timer_func;

timed_output_dev_register(&timed_vibrator);

ret = platform_device_register(&vibrator_device);

if (ret != 0){

printk("[vibrator]Unable to register vibrator device (%d)\n", ret);

return ret;

}

vibrator_queue = create_singlethread_workqueue(VIB_DEVICE);

if(!vibrator_queue) {

printk("[vibrator]Unable to create workqueue\n");

return -ENODATA;

}

if(ret)

{

printk("[vibrator]Unable to register vibrator driver (%d)\n", ret);

return ret;

}

ret = device_create_file(timed_vibrator.dev,&dev_attr_vibr_on);

if(ret)

{

printk("[vibrator]device_create_file vibr_on fail! \n");

}

}

static void __exit exit_vibrator(void)

{

if(vibrator_queue) {

destroy_workqueue(vibrator_queue);

}

printk("[vibrator]vib_mod_exit Done \n");

}

module_init(init_vibrator);

module_exit(exit_vibrator);

MODULE_AUTHOR("bsp meizu");

MODULE_DESCRIPTION("timed output vibrator device");

MODULE_LICENSE("GPL");

写enable文件时调用vibrator_enable;

读enable文件时调用vibrator_get_time。

init_vibrator是模块入口, vibrator_enable是写enable文件的入口。

vibrator_enable中的处理非常有意思,这个函数“正常”的步骤应该是:设置voltage以开震动----->延迟需要震动的时间----->把volt设为0以关闭震动,

然而上面的步骤却没有这样处理,而是启动了高精度定时器并在退出前通过schedule_work(&work_vibrator)调度work,

调度work即是执行set_pmic_vibrator,这里通过RPC通信操作VIB_DRV_N pin,控制输出电压。

即写enable文件时并没有对motor 电压的任何直接操作,而是延后进行的。

为何采取这种策略,一种解释是:enable的调用将形成一种持续时间的效果,但是调用本身不宜阻塞,

因此实现就让vibrator_enable函数退出后通过定时器实现效果。我反正不太理解上面这个解释:-(

白话一些,我的理解:

那种“正常”的步骤,最关键的是有个延时,而在函数中这种长时间的延时(震动可能很长时间如闹钟或来电)是不好的而且是很不好,

于是策略改变,采用Timer +work,具体步骤如下

引入关键的全局变量 vibe_state,只有在向enable文件写入不为0时才将 vibe_state 置为1,同时启动定时器,并调度work;

定时器超时的callback函数中又将 vibe_state 置0并调度work。而调度work时的执行函数set_pmic_vibrator(int on)其传入参数即是vibe_state,

会根据这一值来设置motor的pin电压为需要的值(开震动)或者0(关震动)。如此通过迂回的方式就避开了“在函数中延长很长时间”的问题,不会造成阻塞。

通过这个例子可以理解work将“工作推后延迟执行”的特点,TouchPanel中的work是将读点的工作延后到中断服务退出,

不同的是TouchPanel中还要创建独立的工作队列(create_singlethread_workqueue),而不是像这个使用内核缺省的队列。

没有搞清楚 msm_rpc_call 这个函数如何使用的?!

2.hardware/libhardware_legacy/vibrator.c 这是硬件抽象层,其实就是对/sys/class/timed_output/vibrator/enable文件的写操作,

提供给上层JNI的两个接口: int vibrator_on(int timeout_ms); // 开始振动

int vibrator_off(); // 关闭振动
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: