1. Vibrator driver: /kernel/drivers/misc/pm8xxx-vibrator.c
2. 手机中注册驱动路径: /sys/devices/platform/msm_ssbi.0/pm8921-core/pm8xxx-vib
3. 注册"msm_ssbi"device, @kernerl/arch/arm/mach-msm/devices-8960.c
struct platform_device msm8960_device_ssbi_pmic = {
.name = "msm_ssbi",
.id = 0,
.resource = resources_ssbi_pmic,
.num_resources = ARRAY_SIZE(resources_ssbi_pmic),
};
@kernel/arch/arm/mach-msm/board-semc_blue.c
static struct platform_device *common_devices[] = {
...
&msm8960_device_ssbi_pmic,
...
}
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));-- platform_device_register(devs[i]);
注册"msm_ssbi"driver, @kernel/drivers/platform/msm/ssbi.c
static struct platform_driver msm_ssbi_driver = {
.probe = msm_ssbi_probe,
.remove = __exit_p(msm_ssbi_remove),
.driver = {
.name = "msm_ssbi",
.owner = THIS_MODULE,
},
};
platform_driver_register(&msm_ssbi_driver);
4. 注册"pm8921-core" device, @kernel/arch/arm/mach-msm/board-semc_blue.c
static struct msm_ssbi_platform_data msm8960_ssbi_pm8921_pdata __devinitdata = {
.controller_type = MSM_SBI_CTRL_PMIC_ARBITER,
.slave = {
.name = "pm8921-core",
.platform_data = &pm8921_platform_data,
},
};
msm8960_device_ssbi_pmic.dev.platform_data = &msm8960_ssbi_pm8921_pdata;
?????
在msm_ssbi_probe@kernel/drivers/platform/msm/ssbi.c中有
ret = msm_ssbi_add_slave(ssbi, &pdata->slave);-->ret = platform_device_add(slave_pdev) //此处注册"pm8921-core" device
注册"pm8921-core" driver, @kernel/drivers/mfd/pm8921-core.c
static struct platform_driver pm8921_driver = {
.probe = pm8921_probe,
.remove = __devexit_p(pm8921_remove),
.driver = {
.name = "pm8921-core",
.owner = THIS_MODULE,
},
};
platform_driver_register(&pm8921_driver);
5. 注册"pm8xxx-vib" device, @kernel/drivers/mfd/pm8921-core.c
static struct mfd_cell vibrator_cell __devinitdata = {
.name = PM8XXX_VIBRATOR_DEV_NAME,//"pm8xxx-vib"
.id = -1,
};
ret = mfd_add_devices(pmic->dev, 0, &vibrator_cell, 1, NULL, 0);
注册"pm8xxx-vib" driver, @kernel/drivers/misc/pm8xxx-vibrator.c
static struct platform_driver pm8xxx_vib_driver = {
.probe = pm8xxx_vib_probe,
.remove = __devexit_p(pm8xxx_vib_remove),
.driver = {
.name = PM8XXX_VIBRATOR_DEV_NAME, //"pm8xxx-vib"
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &pm8xxx_vib_pm_ops,
#endif
},
};
platform_driver_register(&pm8xxx_vib_driver);
6. 到此为止分析完了 vibrator 的注册路径:/sys/devices/platform/msm_ssbi.0/pm8921-core/pm8xxx-vib
7. 如何注册/sys/class/timed_output/vibrator/enable?
分析Vibrator的文件操作接口, 在pm8xxx_vib_probe@kernel/drivers/misc/pm8xxx-vibrator.c中有
vib->timed_dev.name = "vibrator";
vib->timed_dev.get_time = pm8xxx_vib_get_time;
vib->timed_dev.enable = pm8xxx_vib_enable;
rc = timed_output_dev_register(&vib->timed_dev);//注册为/sys/class/timed_output/vibrator
在@kernel/drivers/staging/android/timed_output.c
timed_output_dev_register-->ret = device_create_file(tdev->dev, &dev_attr_enable);//创建.../vibrator/enable接口
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
最终 #echo 1000 > /sys/class/timed_output/vibrator/enable //调用的是pm8xxx_vib_enable
最终 #cat /sys/class/timed_output/vibrator/enable //调用的是pm8xxx_vib_get_time
8. 用到了hrtimer 定时器控制 vibrator 的输出时间
a. hrtimer_init(&vib->vib_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
b. vib->vib_timer.function = pm8xxx_vib_timer_func;
c. static enum hrtimer_restart pm8xxx_vib_timer_func(struct hrtimer *timer)
{
struct pm8xxx_vib *vib = container_of(timer, struct pm8xxx_vib,
vib_timer);
vib->state = 0;
schedule_work(&vib->work);
return HRTIMER_NORESTART;
}
9. 用到了工作队列做异步控制(enable操作 或定时器到点触发操作)
a. INIT_WORK(&vib->work, pm8xxx_vib_update);
b. 在pm8xxx_vib_timer_func中会启动schedule_work(&vib->work);
10. 控制Vibrator的震动能量,和最长震动时间等, @kernel/arch/arm/mach-msm/board-semc_blue_cdb.c
struct pm8xxx_vibrator_platform_data pm8xxx_vibrator_pdata = {
.initial_vibrate_ms = 0, //留给Vibrator的准备时间
. max_timeout_ms = 15000, //最长震动时间
.level_mV = 3100, //震动能量 ///这个就是控制马达的震动幅度的变量
};
11. 主要操作硬件寄存器的函数,static int pm8xxx_vib_set(struct pm8xxx_vib *vib, int on)@kernel/drivers/misc/pm8xxx-vibrator.c
12. 小结:
a. 都依据android定义的架构设计 @kernel/drivers/staging/android/timed_output.c, 实现如下结构
struct timed_output_dev {
const char *name;
/* enable the output and set the timer */
void (*enable)(struct timed_output_dev *sdev, int timeout);
/* returns the current number of milliseconds remaining on the timer */
int (*get_time)(struct timed_output_dev *sdev);
/* private data */
struct device *dev;
int index;
int state;
};
b. #echo 1000 > /sys/class/timed_output/vibrator/enable //以毫秒为单位,调用的是timed_output_dev->(*enable)
#cat /sys/class/timed_output/vibrator/enable //调用的是timed_output_dev->(*get_time)
c. 在Vibrator的kernel driver中,通过work quence实现异步控制
d. 在Vibrator的kernel driver中,通过hrtimer实现震动时间的精确控制
e. 通过控制流经Vibrator的电压差来控制Vibrator的震动幅度;Vibrator连这两路电压VDD和Vout, Vout可以被控制(VIB_DRV_N),所以Vibrator的压差Vm=VDD-Vout
二、基于STE Riogrande 平台的 vibrator驱动实现
1. 驱动代码kernel/drivers/staging/android/ste_timed_vibra.c
2. 注册的驱动位于 /sys/devices/platform/ste_timed_output_vibra.0/
a. @ste_timed_vibra.c, 有
static struct platform_driver ste_timed_vibra_driver = {
.driver = {
.name = "ste_timed_output_vibra",
.owner = THIS_MODULE,
},
.probe = ste_timed_vibra_probe,
.remove = __devexit_p(ste_timed_vibra_remove)
};
b. platform_driver_register(&ste_timed_vibra_driver); 如此注册vibrator为platform驱动/sys/devices/platform/ste_timed_output_vibra.0/
3. 注册vibrator设备@kernel/arch/arm/mach-ux500/board-mop500-vibra.c
static struct platform_device ux500_vibra_device = {
.name = "ste_timed_output_vibra",
};
platform_device_register(&ux500_vibra_device);
4. 如何注册/sys/class/timed_output/vibrator/enable
a. ste_timed_vibra_probe@ste_timed_vibra.c有
vinfo->tdev.name = "vibrator";
vinfo->tdev.enable = vibra_enable;
vinfo->tdev.get_time = vibra_get_time;
ret = timed_output_dev_register(&vinfo->tdev);
b. timed_output_dev_register@kernel/drivers/staging/android/timed_output.c
ret = create_timed_output_class();-->timed_output_class = class_create(THIS_MODULE, "timed_output"); //创建sys/class/timed_output
tdev->dev = device_create(timed_output_class, NULL,MKDEV(0, tdev->index), NULL, tdev->name);//创建"vibrator" device
ret = device_create_file(tdev->dev, &dev_attr_enable);
5. #echo 1000 > /sys/class/timed_output/vibrator/enable; 调用函数的call stack
a. enable_store@timed_output.c; 有tdev->enable(tdev, value);
b. vibra_enable@ste_timed_vibra.c; 有queue_work(vinfo->enable_workqueue, &vinfo->enable_work);vinfo->queued_timeout = timeout(为-1);
c. vibra_enable_work@ste_timed_vibra.c; [vinfo->vibra_state = STE_VIBRA_IDLE]
d. vibra_control(vinfo); [vinfo->vibra_state = STE_VIBRA_BOOST]
vinfo->pdata->timed_vibra_control(100, -100, 100, -100); -->ux500_ab8500_audio_pwm_vibra@kernel/sound/soc/ux500/ux500_ab8500.c
hrtimer_start(&vinfo->vibra_timer, ktime, HRTIMER_MODE_REL); //比如设置的60ms表示vibrator启动时间, 则60ms后会启动hrtimer 的运行函数vibra_timer_expired
e. vibra_timer_expired@ste_timed_vibra.c; [vinfo->vibra_state = STE_VIBRA_BOOST]
queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work);
f. vibra_control_work-->vibra_control; [vinfo->vibra_state = STE_VIBRA_ON]
vinfo->pdata->timed_vibra_control(50, -50, 50, -50);
hrtimer_start(&vinfo->vibra_timer, ktime, HRTIMER_MODE_REL); //此时设置的hrtimer延时为0了,表示马上执行vibra_timer_expired; 如果设置enable的时间10000,就是100000ms-60ms后再启动vibra_timer_expired
g. vibra_timer_expired@ste_timed_vibra.c; [vinfo->vibra_state = STE_VIBRA_ON]
queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work);
h. vibra_control_work-->vibra_control; [vinfo->vibra_state = STE_VIBRA_OFF]
vinfo->pdata->timed_vibra_control(-50, 50, -50, 50);
hrtimer_start(&vinfo->vibra_timer, ktime, HRTIMER_MODE_REL); //比如设置的60ms表示vibrator的关闭时间, 则60ms后会启动hrtimer 的运行函数vibra_timer_expired
i. vibra_timer_expired@ste_timed_vibra.c; [vinfo->vibra_state = STE_VIBRA_OFF]
queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work);
j. vibra_control_work-->vibra_control; [vinfo->vibra_state = STE_VIBRA_IDLE]
vinfo->pdata->timed_vibra_control(0,0,0,0); //表示关闭vibrator
k. 到此一次完整的vibrator 震动过程完成了,另外该震动模式是用的no linear vibrator
开始操作Vibrator ;启动一工作队列workquence[enable work]
Vibrator 启动(60ms),起震 ;启动一定时器hrtimer,开始60ms计时,启动另一工作队列 workquence[contol work]
Vibrator 运行(震动时间 = 总时间-60ms),平稳震动 ;启动一定时器hrtimer,开始计时
Vibrator 停止(60ms),减震 ;定时器到点触发,启动另一工作队列 workquence[contol work]
Vibrator 关闭 ;完全关闭
6. hrtimer 高精度定时器的使用
a. void hrtimer_init(struct hrtimer *timer, clockid_t clock_id,enum hrtimer_mode mode)
hrtimer_init(&vinfo->vibra_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);//初始化
b. vinfo->vibra_timer.function = vibra_timer_expired; 设置timer的执行函数
c. ktime_t hrtimer_get_remaining(const struct hrtimer *timer) //得到启动hrtimer还需要等待的时间
remain = hrtimer_get_remaining(&vinfo->vibra_timer);
d. int hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode)//启动hrtimer,开始计时
ktime_t ktime;
ktime = ktime_set((val / MSEC_PER_SEC), (val % MSEC_PER_SEC) * NSEC_PER_MSEC),
hrtimer_start(&vinfo->vibra_timer, ktime, HRTIMER_MODE_REL);
e. static inline int hrtimer_restart(struct hrtimer *timer)//重新设置延时时间
f. int hrtimer_cancel(struct hrtimer *timer)//取消该定时器,如果定时器的函数已经运行,则等到运行完后再取消
7. 通常基于Android的Timed Output驱动框架实现,Vibrator的驱动程序只需要实现振动的接口即可,这是一个输出设备,需要接受振动时间作为参数。
由于比较简单,因此Vibrator的驱动程序可以使用多种方式来实现。在Android中,推荐基于Android内核定义Timed Output驱动程序框架来实现Vibrator的驱动程序。Timed Output的含义为定时输出,用于定时发出某个输出。
实际上,这种驱动程序依然是基于sys文件系统来完成的。drivers/staging/android/目录timed_output.h中定义timed_output_dev结构体,其中包含enable和get_time这两个函数指针,实现结构体后,使用timed_output_dev_register()和timed_output_dev_unregister()函数注册和注销即可。
Timed Output驱动程序框架将为每个设备在/sys/class/timed_output/目录中建立一个子目录,设备子目录中的enable文件就是设备的控制文件。读enable文件表示获得剩余时间,写这个文件表示根据时间振动。Timed Output驱动的设备调试,通过sys文件系统即可。
对于Vibrator设备,其实现的Timed Output驱动程序的名称应该为“vibrator”。因此Vibrator设备在sys文件系统中的方法如下所示:
# echo "10000" > /sys/class/timed_output/vibrator/enable
# cat /sys/class/timed_output/vibrator/enable
3290
# echo "0" > /sys/class/timed_output/vibrator/enable
对于enable文件,“写”表示使能指定的时间,“读”表示获取剩余时间。
8. 该Vibrator的驱动电压差是用通过 PWM来实现的,通过平均电压可看到 输入压差,从而控制Vibrator的震动幅度