#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(); // 关闭振动
#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(); // 关闭振动