android timed output vibrator driver

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值