Vibrator Kernel driver 实现

一,基于Qualcomm Blue 平台 Vibrator 分析
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的震动幅度


你可能感兴趣的:(Worknote)