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\n",
        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”中的实现方式。

Alarm(硬件时钟) init的更多相关文章

  1. RTC硬件时钟设置修改【转】

    转自:http://os.chinaunix.net/a2008/0526/981/000000981211.shtml 这两天一直在做i2c设备驱动的理解,所以很少更新文章. 由于对于表计来说,RT ...

  2. Linux 系统时钟(date) 硬件时钟(hwclock)

    /********************************************************************* * Linux 系统时钟(date) 硬件时钟(hwclo ...

  3. Linux命令之hwclock - 查询和设置硬件时钟

    常用参数 -r, --show         读取并打印硬件时钟(read hardware clock and print result ) -s, --hctosys      将硬件时钟同步到 ...

  4. ntp/系统时钟/硬件时钟/双系统下计算机时间读取的问题

    http://blog.chinaunix.net/uid-182041-id-3464524.html       //linux系统时间和硬件时钟问题(date和hwclock) http://j ...

  5. linux系统时钟和硬件时钟不一致

    在做DB2 集群复制的时候要求两台主机时间相互一致. 但是在一台主机上系统时间和硬件时间相差12个小时左右:手动同步后,重启后又相差12个小时左右. 为什么会是这样的,先介绍下系统时钟和硬件时钟的区别 ...

  6. linux系统时间同步,硬件时钟和系统时间同步,时区的设置

           1.时间同步(手动): date -s "2015-07-15 22:13:30" hwclock --systohc   (表示系统时间同步到硬件时钟) hwclo ...

  7. Linux中有硬件时钟与系统时钟

    在Linux中有硬件时钟与系统时钟等两种时钟.硬件时钟是指主机板上的时钟设备,也就是通常可在BIOS画面设定的时钟.系统时钟则是指kernel中的时钟.当Linux启动时,系统时钟会去读取硬件时钟的设 ...

  8. Linux系统时钟与硬件时钟

    linux系统有两个时钟:一个是由主板电池驱动的硬件时钟(Real Time Clock),也叫做RTC或者叫CMOS时钟.当操作系统关机的时候,用这个来记录时间,但是对于运行的系统是不用这个时间的: ...

  9. 使用hwclock同步RTC(硬件时钟)和OS Clock(操作系统时钟)

    Linux下面有个命令叫做hwclock,其主要的功能是用来在RTC和操作系统之间进行时钟同步.既可以将RTC的时钟同步到操作系统,也可以在通过hwclock将当前系统时间.Internet时间.本地 ...

随机推荐

  1. C# 匿名方法

    每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不 ...

  2. MySQL-profiling的使用

    分析SQL执行带来的开销是优化SQL的重要手段.在MySQL数据库中,可以通过配置profiling参数来启用SQL剖析.该参数可以在全局和session级别来设置.对于全局级别则作用于整个MySQL ...

  3. linux:磁碟与档案系统管理

    档案系统特性:为什么磁碟分割完需要格式化(format)才能使用吗? 答:因为每种作业系统所设定的档案属性和权限并不相同,为了存放这些档案所需的资料(所以需要格式化成作业系统能够利用的档案系统格式fi ...

  4. ADO.net 扩展属性

    扩展属性 处理:有外键关系时将代号化信息处理成原始文字,让用户可看懂的(粗略解释) 利用扩展属性 如:users表中的民族列显示的是民族代号处理成Nation表中的民族名称 需要在users类里面扩展 ...

  5. Nginx简介

    序言Nginx 是 lgor Sysoev 为俄罗斯访问量第二的 rambler.ru 站点设计开发的.从 2004 年发布至今,凭借开源的力量,已经接近成熟与完善.Nginx 功能丰富,可作为 HT ...

  6. Linux 系统结构

    Linux的系统结构一般由四部分组成 内核 1)内核 操作系统的核心,具有最基本的功能:内存管理.进程管理.设备驱动管理.文件系统管理,网络管理 内核版本(kernel)查看的三种方法 cat /pr ...

  7. hdu5255 魔法因子

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5255 首先先预处理出一个p,使得p*因子X等于一个整数,且p最小,设q=p*X. 则题目则可以看成存在 ...

  8. WEB-INF/views/menu/list.jsp (line: 26, column: 58) equal symbol expected

    根本原因是由于单引号和双引号的混乱使用导致的. 解决办法: 将双引号里面的双引号改成单引号: 单引号里面的双引号该成单引号. 我的问题好像又不是这样的,<c:forEach var=" ...

  9. 夺命雷公狗---node下的一聊天室-首发

    今晚感觉挺蛋疼,就用了点时间,在node下开发了个聊天室来玩玩,不过之是简单的开发了套而已,并没多做什么考虑,, 但是发现了一个好处就是用node来写聊天室代码居然少得可怜,这个不佩服node都不行, ...

  10. [Ubuntu] Ubuntu13.04, the desktop freezed after login

    My os version is Ubuntu13.04, today, after started and logined, my desktop freezed. But i can still ...