• Alarm(硬件时钟) init


    http://blog.csdn.net/angle_birds/article/details/17302297

    Alarm就是一个硬件时钟,前面我们已经知道它提供了一个定时器,用于把设备从睡眠状态唤醒,同时它也提供了一个在设备睡眠时仍然会运行的时钟基准。在应用层上,有关时间的应用都需要Alarm的支持,源代码位于“drivers/rtc/alarm.c”。

      Alarm的设备名为“/dev/alarm”。该设备的实现非常简单,我们首先打开源码,可以看到include,其中定义了一些Alarm的相关信息。Alarm的类型枚举如下:

    enum android_alarm_type {
        ANDROID_ALARM_RTC_WAKEUP,
        ANDROID_ALARM_RTC,
        ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
        ANDROID_ALARM_ELAPSED_REALTIME,
        ANDROID_ALARM_SYSTEMTIME,
        ANDROID_ALARM_TYPE_COUNT,
    };

      主要包括了5种类型的Alarm,_WAKEUP类型表示在触发Alarm时需要唤醒设备,反之则不需要唤醒设备;ANDROID_ALARM_RTC类型表示在指定的某一时刻出发Alarm;ANDROID_ALARM_ELAPSED_REALTIME表示在设备启动后,流逝的时间达到总时间之后触发Alarm;ANDROID_ALARM_SYSTEMTIME类型则表示系统时间;ANDROID_ALARM_ TYPE_COUNT则是Alram类型的计数。

      注意 流逝的时间也包括设备睡眠的时间,流逝时间的计算点从它最后一次启动算起。

      Alarm返回标记的枚举类型如下:

    enum android_alarm_return_flags {
        ANDROID_ALARM_RTC_WAKEUP_MASK = 1U << ANDROID_ALARM_RTC_WAKEUP,
        ANDROID_ALARM_RTC_MASK = 1U << ANDROID_ALARM_RTC,
        ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK =
                    1U << ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
        ANDROID_ALARM_ELAPSED_REALTIME_MASK =
                    1U << ANDROID_ALARM_ELAPSED_REALTIME,
        ANDROID_ALARM_SYSTEMTIME_MASK = 1U << ANDROID_ALARM_SYSTEMTIME,
        ANDROID_ALARM_TIME_CHANGE_MASK = 1U << 16
    };

      Alarm返回标记会随着Alarm的类型而改变。最后还定义了一些宏,主要包括禁用Alarm、Alarm等待、设置Alarm等。下面我们来分析Alarm驱动的具体实现。

      首先,Alarm的初始化及退出由以下三个函数来完成:

      late_initcall(alarm_late_init);

      module_init(alarm_init);

      module_exit(alarm_exit);

      其中alarm_init函数对Alarm执行初始化操作,alarm_late_init需要在初始化完成之后进行调用,最后退出时需要调用alarm_exit来销毁和卸载Alarm接口及驱动。

    1.alarm_init

      在初始化过程中,首先需要初始化系统时间,通过platform_driver_register函数来注册Alarm驱动的相关参数,具体如下所示:

    static struct platform_driver alarm_driver = {
        .suspend = alarm_suspend,
        .resume = alarm_resume,
        .driver = {
            .name = "alarm"
        }
    };

      该参数主要指定了当系统挂起(suspend)和唤醒(Desume)所需要实现的分别为alarm_suspend和alarm_resume,同时将Alarm设备驱动的名称设置为了“alarm”。

      如果设置正确,那么继续通过如下代码来初始化SUSPEND lock,因为在使用它们之前必须执行初始化操作。

      wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm");

      wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc");

      紧接着通过class_interface_register函数来注册Alarm接口信息,主要包括设备的添加和移除操作,内容如下:

    static struct class_interface rtc_alarm_interface = {
        .add_dev = &rtc_alarm_add_device,
        .remove_dev = &rtc_alarm_remove_device,
    };

      如果在此过程中出现错误,那么需要销毁已经注册的SUSPEND lock,并且卸载Alarm驱动,代码如下:

      wake_lock_destroy(&alarm_rtc_wake_lock);

      wake_lock_destroy(&alarm_wake_lock);

      platform_driver_unregister(&alarm_driver);

      注意 wake lock是一种锁机制,只要有用户持有该锁,系统就无法进入休眠状态,该锁可以被用户态程序和内核获得。这个锁可以是超时的或者是没有超时的,超时的锁会在时间过期以后自动解锁。如果没有锁或者超时了,内核就会启动休眠机制进入休眠状态,后面在讲电源管理时还会进一步讲解该机制。

     2.alarm_late_init

      当Alarm启动之后,我们需要读取当前的RCT和系统时间,由于需要确保在这个操作过程中不被中断,或者在中断之后能告诉其他进程该过程没有读取完成,不能被请求,因此这里需要通过spin_lock_irqsave和spin_unlock_irqrestore来对其执行锁定和解锁操作。实现代码如下:

    static int __init alarm_late_init(void)
    {
        unsigned long   flags;
        struct timespec system_time;
        spin_lock_irqsave(&alarm_slock, flags);
        getnstimeofday(&elapsed_rtc_delta);
        ktime_get_ts(&system_time);
        elapsed_rtc_delta = timespec_sub(elapsed_rtc_delta, system_time);
        spin_unlock_irqrestore(&alarm_slock, flags);
        ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO,
    "alarm_late_init: rtc to elapsed realtime delta %ld.%09ld ",
            elapsed_rtc_delta.tv_sec, elapsed_rtc_delta.tv_nsec);
        return 0;
    }

     3.alarm_exit

      当Alarm退出时,就需要通过class_interface_unregister函数来卸载在初始化时注册的Alarm接口,通过wake_lock_destroy函数来销毁SUSPEND lock,以及通过platform_driver_unregister函数来卸载Alarm驱动。实现代码如下:

    static void  __exit alarm_exit(void)
    {
        class_interface_unregister(&rtc_alarm_interface);
        wake_lock_destroy(&alarm_rtc_wake_lock);
        wake_lock_destroy(&alarm_wake_lock);
        platform_driver_unregister(&alarm_driver);
    }

     4.添加和移除设备

      接下来是rtc_alarm_add_device和rtc_alarm_remove_device函数的实现。添加设备时,首先将设备转换成rtc_device类型,然后,通过misc_register函数将自己注册成为一个Misc设备。其包括的主要特性如下面的代码所示:

    static struct file_operations alarm_fops = {
        .owner = THIS_MODULE,
        .unlocked_ioctl = alarm_ioctl,
        .open = alarm_open,
        .release = alarm_release,
    };
    static struct miscdevice alarm_device = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "alarm",
        .fops = &alarm_fops,
    };

      其中alarm_device中的“.name”表示设备文件名称,而alarm_fops则定义了Alarm的常用操作,包括打开、释放和I/O控制。这里还需要通过rtc_irq_register函数注册一个rtc_task,用来处理Alarm触发的方法,其定义如下:

    static struct rtc_task alarm_rtc_task = {
        .func = alarm_triggered_func
    };

      其中“alarm_triggered_func”则是Alarm需要触发的方法。

      注意 如果在添加设备的过程中出现错误,我们需要对已经执行的操作进行释放、销毁和卸载。但是,移除一个设备时同样需要判断设备是否是Alarm设备,然后再执行卸载等操作。另外,在处理挂起操作时,我们首先就需要对设备进行锁定,然后根据Alarm的类型执行不同的操作,同时要保存时间。

      alarm_open和alarm_release的实现很简单。最后需要说明的是,对于I/O操作而言,主要需要实现:设置时间、设置RTC、获取时间、设置Alarm等待等。

      本小节主要对Android中最简单的设备驱动——Alarm的实现流程进行了分析,大家应该可以自己绘制出一个流程图来了吧。对于Alarm的具体实现,大家可以参考源代码“drivers/rtc/alarm.c”中的实现方式。

  • 相关阅读:
    hibernate关联总结
    hibernate关联映射之多对多
    hibernate关联映射之一对多&多对一
    hibernate集合映射
    Hibernate3疑惑解决
    Hibernate3运行原理
    Hibernate3映射数据类型
    Hibernate3主键生成策略
    Hibernate3核心API简介-Transaction接口
    Hibernate3核心API-Session接口
  • 原文地址:https://www.cnblogs.com/muhuacat/p/5279261.html
Copyright © 2020-2023  润新知