zoukankan      html  css  js  c++  java
  • Linux时间子系统之二:Alarm Timer

    一、前言

    严格来讲Alarm Timer也算POSIX Timer一部分,包含两种类型CLOCK_REALTIME_ALARM和CLOCK_BOOTTIME_ALARM。分别是在CLOCK_REALTIME和CLOCK_BOOTTIME后面加上_ALARM。Alarm Timer之外的POSIX Timer在内核进入cpuidle或者suspend之后,都会因为省电关闭ClockEvent设备而停止计时。而Alarm Timer恰恰借助RTC设备的长供电且具备唤醒功能,在系统进入suspend过程中,将最近一次超时expires写入RTC设备,超时后会将系统从suspend状态唤醒,执行timer超市函数。

    这样在程序执行过程中,就不需要一直持有wakelock。

    二、背景介绍

    Alarm Timer可以说工作在两种状态下,一种是和其他Timer一样的基于hrtimer;另一种是在系统进入suspend后基于RTC设备。

    RTC设备在系统外独立供电,RTC具备Alarm功能。在Alarm触发后,通过中断唤醒suspend的系统。

    在device_initcall-->alarmtimer_init时,注册一个alarmtimer的platform_device,驱动为alarmtimer_driver。将alarmtimer_suspend作为钩子函数插入系统suspend流程,这样就将suspend和Alarm Timer功能挂钩了。

    三、重要数据结构

    struct alarm_base作为AlarmTimer时钟类型结构体,包含ALARM_REALTIME和ALARM_BOOTTIME两种。

    static struct alarm_base {
      spinlock_t lock;---------------------------------互斥访问锁
      struct timerqueue_head timerqueue;-------AlarmTimer自己维护了expires红黑树。
      struct hrtimer timer;--------------------------将其加入到hrtimer_bases对应的红黑树中。
      ktime_t (*gettime)(void);--------------------获取对应类型时钟的时间函数
      clockid_t base_clockid;------------------------时钟类型ID,CLOCK_REALTIME和CLOCK_BOOTTIME
    } alarm_bases[ALARM_NUMTYPE];

    CLOCK_REALTIME_ALARM和CLOCK_REALTIME、CLOCK_BOOTTIME_ALARM和CLOCK_BOOTTIME都是用同样的base_clockid,但是_ALARM维护的alarm_bases[ALARM_NUMTYPE].timerqueue将他们与其他hrtimer区分开了。

    struct k_clock alarm_clock作为两种类型共用的时钟/Timer函数:

        struct k_clock alarm_clock = {
            .clock_getres    = alarm_clock_getres,
            .clock_get    = alarm_clock_get,
            .timer_create    = alarm_timer_create,
            .timer_set    = alarm_timer_set,
            .timer_del    = alarm_timer_del,
            .timer_get    = alarm_timer_get,
            .nsleep        = alarm_timer_nsleep,
        };
    

    static struct rtc_timer rtctimer;--------------------RTC Timer 

    static struct rtc_device *rtcdev;-------------------RTC设备对应的结构体

    struct rtc_time是RTC设备表示的时间格式:

     1 struct rtc_time {
     2     int tm_sec;
     3     int tm_min;
     4     int tm_hour;
     5     int tm_mday;
     6     int tm_mon;
     7     int tm_year;
     8     int tm_wday;
     9     int tm_yday;
    10     int tm_isdst;
    11 };

     struct ktime_t是内核时间格式。

    这两种时间格式的转换,rtc_time到ktime_t通过rtc_tm_to_ktime;ktime_t到rtc_time通过rtc_ktime_to_tm。

    四、AlarmTimer正常工作状态下运行

    static int __init alarmtimer_init(void)
    {
        struct platform_device *pdev;
        int error = 0;
        int i;
        struct k_clock alarm_clock = {--------------------------------------AlarmTimer的Clock/Timer/Sleep函数。
            .clock_getres    = alarm_clock_getres,
            .clock_get    = alarm_clock_get,
            .timer_create    = alarm_timer_create,
            .timer_set    = alarm_timer_set,
            .timer_del    = alarm_timer_del,
            .timer_get    = alarm_timer_get,
            .nsleep        = alarm_timer_nsleep,
        };
    
        alarmtimer_rtc_timer_init();---------------------------------------初始化一个struct rtc_timer,将其加入struct rtc_device的timerqueue红黑树里面。
    
        posix_timers_register_clock(CLOCK_REALTIME_ALARM, &alarm_clock);---填充posix_clocks[MAX_CLOCKS]的_ALARM部分
        posix_timers_register_clock(CLOCK_BOOTTIME_ALARM, &alarm_clock);
    
        /* Initialize alarm bases */
        alarm_bases[ALARM_REALTIME].base_clockid = CLOCK_REALTIME;---------初始化alarm_bases[ALARM_NUMTYPE]
        alarm_bases[ALARM_REALTIME].gettime = &ktime_get_real;
        alarm_bases[ALARM_BOOTTIME].base_clockid = CLOCK_BOOTTIME;
        alarm_bases[ALARM_BOOTTIME].gettime = &ktime_get_boottime;
        for (i = 0; i < ALARM_NUMTYPE; i++) {
            timerqueue_init_head(&alarm_bases[i].timerqueue);
            spin_lock_init(&alarm_bases[i].lock);
            hrtimer_init(&alarm_bases[i].timer,
                    alarm_bases[i].base_clockid,
                    HRTIMER_MODE_ABS);
            alarm_bases[i].timer.function = alarmtimer_fired;
        }
    
        error = alarmtimer_rtc_interface_setup();-------------------------获取系统的struct rtc_device设备,给rtcdev。
        if (error)
            return error;
    
        error = platform_driver_register(&alarmtimer_driver);-------------注册alarmtimer_driver,主要就是suspend钩子函数。
        if (error)
            goto out_if;
    
        pdev = platform_device_register_simple("alarmtimer", -1, NULL, 0);-注册alarmtimer设备。
        if (IS_ERR(pdev)) {
            error = PTR_ERR(pdev);
            goto out_drv;
        }
        ws = wakeup_source_register("alarmtimer");------------------------返回注册的Wakeup Source,alarmtimer_suspend使用。
        return 0;
    
    out_drv:
        platform_driver_unregister(&alarmtimer_driver);
    out_if:
        alarmtimer_rtc_interface_remove();
        return error;
    }

    AlarmTimer是struct k_itimer中的alarmtimer成员:

    struct k_itimer {
    ...
        union {
            ...
            struct {
                struct alarm alarmtimer;
                ktime_t interval;
            } alarm;
            struct rcu_head rcu;
        } it;
    };

    struct alarm如下: 

    struct alarm {
        struct timerqueue_node    node;----------------------------------------红黑树节点
        enum alarmtimer_restart    (*function)(struct alarm *, ktime_t now);---Alarm超时函数
        enum alarmtimer_type    type;------------------------------------------ALARM_REALTIME、ALARM_BOOTTIME
        int            state;--------------------------------------------------#define ALARMTIMER_STATE_INACTIVE 0x00、#define ALARMTIMER_STATE_ENQUEUED	0x01、#define ALARMTIMER_STATE_CALLBACK	0x02
    void *data;
    };

      

    在了解了AlarmTimer初始化和基本数据结构之后,和其他POSIX Timer一样,重点在struct k_clock提供的函数。

    1. AlarmTimer定时器

    alarm_timer_create主要填充当前timer的struct alarm结构体:

    static int alarm_timer_create(struct k_itimer *new_timer)
    {
        enum  alarmtimer_type type;
        struct alarm_base *base;
    
        if (!alarmtimer_get_rtcdev())--------------------------是否有可用RTC设备
            return -ENOTSUPP;
    if (!capable(CAP_WAKE_ALARM))--------------------------当前进程是否具有CAP_WAKE_ALARM能力,需要root权限。
            return -EPERM;
    
        type = clock2alarm(new_timer->it_clock);---------------从Timer类型到alarm_base进行转换。
        base = &alarm_bases[type];
        alarm_init(&new_timer->it.alarm.alarmtimer, type, alarm_handle_timer);-----初始化当前timer的struct alarm结构
        return 0;
    }

    在填充好结构体之后,设置expires,并且启动一个hrtimer。

    static int alarm_timer_set(struct k_itimer *timr, int flags,
                    struct itimerspec *new_setting,
                    struct itimerspec *old_setting)
    {
        ktime_t exp;
    
        if (!rtcdev)
            return -ENOTSUPP;
    if (flags & ~TIMER_ABSTIME)
            return -EINVAL;
    
        if (old_setting)
            alarm_timer_get(timr, old_setting);
    
        /* If the timer was already set, cancel it */
        if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < 0)
            return TIMER_RETRY;
    
        /* start the timer */
        timr->it.alarm.interval = timespec_to_ktime(new_setting->it_interval);---------间隔定时
        exp = timespec_to_ktime(new_setting->it_value);--------------------------------首次超时值
        /* Convert (if necessary) to absolute time */
        if (flags != TIMER_ABSTIME) {--------------------------------------------------如果不是绝对时间,需要转换成绝对时间
            ktime_t now;
    
            now = alarm_bases[timr->it.alarm.alarmtimer.type].gettime();
            exp = ktime_add(now, exp);
        }
    
        alarm_start(&timr->it.alarm.alarmtimer, exp);--------------------------------启动AlarmTimer,插入当前alarm_base的timerqueue,如有需要设置一个hrtimer。
        return 0;
    }

    alarm_timer_del是alarm_timer_set的逆操作,用于删除一个AlarmTimer。将其从alarm_base的timerqueue移除,如果已经被插入hrtimer,则取消。

    static int alarm_timer_del(struct k_itimer *timr)
    {
        if (!rtcdev)
            return -ENOTSUPP;
    
        if (alarm_try_to_cancel(&timr->it.alarm.alarmtimer) < 0)
            return TIMER_RETRY;
    
        return 0;
    }

      alarm_timer_get获取timr的超时itimerspec。

    /**
     * alarm_timer_get - posix timer_get interface
     * @new_timer: k_itimer pointer
     * @cur_setting: itimerspec data to fill
     *
     * Copies out the current itimerspec data
     */
    static void alarm_timer_get(struct k_itimer *timr,
                    struct itimerspec *cur_setting)
    {
        ktime_t relative_expiry_time =
            alarm_expires_remaining(&(timr->it.alarm.alarmtimer));
    
        if (ktime_to_ns(relative_expiry_time) > 0) {
            cur_setting->it_value = ktime_to_timespec(relative_expiry_time);
        } else {
            cur_setting->it_value.tv_sec = 0;
            cur_setting->it_value.tv_nsec = 0;
        }
    
        cur_setting->it_interval = ktime_to_timespec(timr->it.alarm.interval);
    }

    新增的struct alarm的note作为一个节点插入到alarm_base的timerqueue中。

    struct timerqueue_node {
        struct rb_node node;
        ktime_t expires;---------------红黑树按照expires大小排列
    };

    节点的插入、删除的典型路径是,

    alarm_timer_set-->alarm_start-->alarmtimer_enqueue-->timerqueue_add

    alarm_timer_del-->alarm_try_to_cancel-->alarmtimer_remove-->timerqueue_del

    操作alarm_base的timerqueue有如下几个地方,这样保证无论是删除、插入、超时都是最新的alarm_base->timerqueue插入到hrtimer中。

    alarmtimer_init---------初始化红黑树头
    alarmtimer_enqueue------将timer加入timerqueue。如果当前timer是最新timer,则创建hrtimer alarmtimer_remove-------将timer移除timerqueue。如果当前timer是最新timer,删除hrtimer。取最近timer重新设置hrtimer。 alarmtimer_fired--------将超时timer移除出timerqueue,如果是restart类型,则重新插入。如果timerqueue不为空,则设置下一次expires,返回HRTIMER_RESTART。
    static int __init alarmtimer_init(void)
    {
    ...
        for (i = 0; i < ALARM_NUMTYPE; i++) {
            timerqueue_init_head(&alarm_bases[i].timerqueue);
            spin_lock_init(&alarm_bases[i].lock);
            hrtimer_init(&alarm_bases[i].timer,------------------------------在alarmtimer_init中初始化hrtimer,ALARM_REALTIME和ALARM_BOOTTIME共两个hrtimer。这两个timer的主要区别在于不同的base_clockid。
                    alarm_bases[i].base_clockid,
                    HRTIMER_MODE_ABS);
            alarm_bases[i].timer.function = alarmtimer_fired;
        }
    ...
    }
    
    
    static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm)
    {
    ...
    
        if (&alarm->node == timerqueue_getnext(&base->timerqueue)) {
            hrtimer_try_to_cancel(&base->timer);-----------------------------努力取消alarm_base->timer,0:timer不在hrtimer_clock_base->active里面,1:将timer从hrtimer_clock_base->active里面移除,-1:timer的callback函数正在被执行,不能被停止。
            hrtimer_start(&base->timer, alarm->node.expires,-----------------将最近expires赋给alarm_base->timer,注意这里不同base的区别。alarm_base的timerqueue红黑树和hrtimer_clock_base的active红黑树区别。
                    HRTIMER_MODE_ABS);
        }
    }
    
    
    
    static void alarmtimer_remove(struct alarm_base *base, struct alarm *alarm)
    {
    ...
    
        if (next == &alarm->node) {
            hrtimer_try_to_cancel(&base->timer);----------------------------取消alarm_base->timer
            next = timerqueue_getnext(&base->timerqueue);-------------------取最近expires
            if (!next)
                return;
            hrtimer_start(&base->timer, next->expires, HRTIMER_MODE_ABS);---更新alarm_base->timer的expires
        }
    }
    
    
    
    static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
    {
    ...
    
        if (next) {
            hrtimer_set_expires(&base->timer, next->expires);----------在处理完expired timer之后,设置最近一次expires的alarm_base->timer。
            ret = HRTIMER_RESTART;
        }
    ...
    }
    
    static int alarm_clock_getres(const clockid_t which_clock, struct timespec *tp)
    {
    ...
    
        return hrtimer_get_res(baseid, tp);---------------------------获取base_clockid的精度
    }

     AlarmTimer中关于timerqueue的操作,一个timerqueue节点共用一个hrtimer。

    timerqueue_init_head(&alarm_bases[i].timerqueue);-------初始化alarm_base->timerqueue,红黑树根节点
    timerqueue_init(&alarm->node);--------------------------初始化一个timerqueue节点
    next
    = timerqueue_getnext(&base->timerqueue);-----------获取alarm_base->timerqueue的最左侧节点
    timerqueue_add(
    &base->timerqueue, &alarm->node);--------将节点插入alarm_base->timerqueue
    timerqueue_del(
    &base->timerqueue, &alarm->node);--------从alarm_base->timerqueue中删除

    五、AlarmTimer在进入Suspend时、Suspend中、Resume时状态分析

    AlarmTimer被当做一个platform_device,主要是为了提供suspend钩子函数。在系统执行suspend流程的时候,针对AlarmTimer进行转移到RTC。

    static int __init alarmtimer_init(void)
    {
    ...
        error = platform_driver_register(&alarmtimer_driver);
        if (error)
            goto out_if;
    
        pdev = platform_device_register_simple("alarmtimer", -1, NULL, 0);
    ...
    }
    
    /* Suspend hook structures */
    static const struct dev_pm_ops alarmtimer_pm_ops = {
        .suspend = alarmtimer_suspend,
    };
    
    static struct platform_driver alarmtimer_driver = {
        .driver = {
            .name = "alarmtimer",
            .pm = &alarmtimer_pm_ops,
        }
    };

    alarmtimer_suspend函数的核心功能是在进入睡眠之前,遍历alarm_bases->timerqueue,取最近一次timer的expires;将此expires写入RTC定时器,RTC超时后会唤醒系统。

    static int alarmtimer_suspend(struct device *dev)
    {
    ...
        rtc = alarmtimer_get_rtcdev();-------------------------------------获取RTC设备
        /* If we have no rtcdev, just return */
        if (!rtc)
            return 0;
    
        /* Find the soonest timer to expire*/
        for (i = 0; i < ALARM_NUMTYPE; i++) {------------------------------遍历ALARM_REALTIME和ALARM_BOOTTIME两个alarm_base,取各自最近expires
            struct alarm_base *base = &alarm_bases[i];
            struct timerqueue_node *next;
            ktime_t delta;
    
            spin_lock_irqsave(&base->lock, flags);
            next = timerqueue_getnext(&base->timerqueue);
            spin_unlock_irqrestore(&base->lock, flags);
            if (!next)
                continue;
            delta = ktime_sub(next->expires, base->gettime());
            if (!min.tv64 || (delta.tv64 < min.tv64))---------------------比较两次expires,取最小者
                min = delta;
        }
        if (min.tv64 == 0)
            return 0;
    
        if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) {-----------------------如果expires小于2秒,保持系统唤醒2秒,并中断suspend流程。
            __pm_wakeup_event(ws, 2 * MSEC_PER_SEC);
            return -EBUSY;
        }
    
        /* Setup an rtc timer to fire that far in the future */
        rtc_timer_cancel(rtc, &rtctimer);-------------------------------取消当前rtctimer
        rtc_read_time(rtc, &tm);
        now = rtc_tm_to_ktime(tm);
        now = ktime_add(now, min);--------------------------------------获取RTC时间,将rtc_timer转换成ktimer_t,将RTC时间加上AlarmTimer超时。
    
        /* Set alarm, if in the past reject suspend briefly to handle */
        ret = rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0));---设置rtctimer
        if (ret < 0)
            __pm_wakeup_event(ws, 1 * MSEC_PER_SEC);
        return ret;
    }

    在此之后系统继续suspend流程,然后RTC到期进行resume唤醒。

    六、RTC相关

    AlarmTimer区别与其他的POSIX Timer就在于其和RTC设备的关联。

    AlarmTimer和RTC的关联有几处:

    1. alarmtimer_init中初始化rtctimer,通过rtc_class接口获得rtcdev:alarmtimer_rtc_timer_init、alarmtimer_rtc_interface_setup

    2. 使用alarmtimer_get_rtcdev获取当前rtc_device

    3. RTC定时器相关操作:rtc_timer_init、rtc_timer_cancel、rtc_read_time、rtc_tm_to_ktime、rtc_timer_start、

    alarmtimer_rtc_interface_setup通过rtc_class接口获取RTC设备: 

    static int alarmtimer_rtc_add_device(struct device *dev,--------------根据rtc_class全局变量,找到其下面的RTC设备。如果有多个,取最后一个。
                    struct class_interface *class_intf)
    {
        unsigned long flags;
        struct rtc_device *rtc = to_rtc_device(dev);----------------------根据struct device找到对应的rtc设备。
    
        if (rtcdev)
            return -EBUSY;
    
        if (!rtc->ops->set_alarm)
            return -1;
        if (!device_may_wakeup(rtc->dev.parent))
            return -1;
    
        spin_lock_irqsave(&rtcdev_lock, flags);
        if (!rtcdev) {
            rtcdev = rtc;------------------------------------------------局部全局变量rtcdev
            /* hold a reference so it doesn't go away */
            get_device(dev);
        }
        spin_unlock_irqrestore(&rtcdev_lock, flags);
        return 0;
    }
    
    static struct class_interface alarmtimer_rtc_interface = {
        .add_dev = &alarmtimer_rtc_add_device,---------------------class_interface_register中会调用此函数
    };
    
    static int alarmtimer_rtc_interface_setup(void)
    {
        alarmtimer_rtc_interface.class = rtc_class;
        return class_interface_register(&alarmtimer_rtc_interface);
    }
    
    static void alarmtimer_rtc_interface_remove(void)
    {
        class_interface_unregister(&alarmtimer_rtc_interface);
    }

      

    alarmtimer_rtc_timer_init初始化一个rtctimer:

    static inline void alarmtimer_rtc_timer_init(void)
    {
        rtc_timer_init(&rtctimer, NULL, NULL);
    }

    RTC设备对外接口主要在drivers/rtc/interface.c中,其中RTC Timer相关包括:rtc_timer_init、rtc_timer_start、rtc_timer_cancel

    /* rtc_timer_init - Initializes an rtc_timer
     * @timer: timer to be intiialized
     * @f: function pointer to be called when timer fires
     * @data: private data passed to function pointer
     *
     * Kernel interface to initializing an rtc_timer.
     */
    void rtc_timer_init(struct rtc_timer *timer, void (*f)(void* p), void* data)----------初始化timer的node、task.func、task.private_date,node将会被插入到RTC设备的timerqueue中。
    {
        timerqueue_init(&timer->node);
        timer->enabled = 0;
        timer->task.func = f;
        timer->task.private_data = data;
    }
    
    /* rtc_timer_start - Sets an rtc_timer to fire in the future
     * @ rtc: rtc device to be used
     * @ timer: timer being set
     * @ expires: time at which to expire the timer
     * @ period: period that the timer will recur
     *
     * Kernel interface to set an rtc_timer
     */
    int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer,-----------------将timer插入rtc->timerqueue,如有需要选择最近的timer设置Alarm。
                ktime_t expires, ktime_t period)
    {
        int ret = 0;
        mutex_lock(&rtc->ops_lock);
        if (timer->enabled)-------------------------------------------------------------是否已经被使能?已经被使能则移除
            rtc_timer_remove(rtc, timer);
    
        timer->node.expires = expires;
        timer->period = period;---------------------------------------------------------expires是超时点,period是容许的宽限
    
        ret = rtc_timer_enqueue(rtc, timer);--------------------------------------------将timer->node插入rtc->timerqueue
    
        mutex_unlock(&rtc->ops_lock);
        return ret;
    }
    
    /* rtc_timer_cancel - Stops an rtc_timer
     * @ rtc: rtc device to be used
     * @ timer: timer being set
     *
     * Kernel interface to cancel an rtc_timer
     */
    int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer* timer)------------rtc_timer_start的反操作
    {
        int ret = 0;
        mutex_lock(&rtc->ops_lock);
        if (timer->enabled)
            rtc_timer_remove(rtc, timer);
        mutex_unlock(&rtc->ops_lock);
        return ret;
    }

    rtc_timer_start和rtc_timer_cancel通过rtc_timer_enqueue和rtc_timer_remove来主动插入/删除timer节点,操作节点的还有一个地方是超时函数rtc_timer_do_work。这三个函数能保证RTC设备的timer及时更新。

    rtc->timerqueue也是红黑树,基本操作也是timerqueue_add、timerqueue_del、timerqueue_getnext。

    针对RTC设备的操作都是通过rtc_device->ops来执行。

    读取RTC时间:

    static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
    {
        int err;
        if (!rtc->ops)
            err = -ENODEV;
        else if (!rtc->ops->read_time)
            err = -EINVAL;
        else {
            memset(tm, 0, sizeof(struct rtc_time));
            err = rtc->ops->read_time(rtc->dev.parent, tm);
        }
        return err;
    }
    
    int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
    {
        int err;
    
        err = mutex_lock_interruptible(&rtc->ops_lock);
        if (err)
            return err;
    
        err = __rtc_read_time(rtc, tm);
        mutex_unlock(&rtc->ops_lock);
        return err;
    }

    设置RTC时间:

    int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
    {
        int err;
    
        err = rtc_valid_tm(tm);
        if (err != 0)
            return err;
    
        err = mutex_lock_interruptible(&rtc->ops_lock);
        if (err)
            return err;
    
        if (!rtc->ops)
            err = -ENODEV;
        else if (rtc->ops->set_time)
            err = rtc->ops->set_time(rtc->dev.parent, tm);
        else if (rtc->ops->set_mmss) {
            unsigned long secs;
            err = rtc_tm_to_time(tm, &secs);
            if (err == 0)
                err = rtc->ops->set_mmss(rtc->dev.parent, secs);
        } else
            err = -EINVAL;
    
        mutex_unlock(&rtc->ops_lock);
        /* A timer might have just expired */
        schedule_work(&rtc->irqwork);
        return err;
    }
    EXPORT_SYMBOL_GPL(rtc_set_time);
    
    int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)
    {
        int err;
    
        err = mutex_lock_interruptible(&rtc->ops_lock);
        if (err)
            return err;
    
        if (!rtc->ops)
            err = -ENODEV;
        else if (rtc->ops->set_mmss)
            err = rtc->ops->set_mmss(rtc->dev.parent, secs);
        else if (rtc->ops->read_time && rtc->ops->set_time) {
            struct rtc_time new, old;
    
            err = rtc->ops->read_time(rtc->dev.parent, &old);
            if (err == 0) {
                rtc_time_to_tm(secs, &new);
    
                /*
                 * avoid writing when we're going to change the day of
                 * the month. We will retry in the next minute. This
                 * basically means that if the RTC must not drift
                 * by more than 1 minute in 11 minutes.
                 */
                if (!((old.tm_hour == 23 && old.tm_min == 59) ||
                    (new.tm_hour == 23 && new.tm_min == 59)))
                    err = rtc->ops->set_time(rtc->dev.parent,
                            &new);
            }
        }
        else
            err = -EINVAL;
    
        mutex_unlock(&rtc->ops_lock);
        /* A timer might have just expired */
        schedule_work(&rtc->irqwork);
    
        return err;
    }
    EXPORT_SYMBOL_GPL(rtc_set_mmss);

    读取Alarm时间:

    static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
    {
        int err;
    
        err = mutex_lock_interruptible(&rtc->ops_lock);
        if (err)
            return err;
    
        if (rtc->ops == NULL)
            err = -ENODEV;
        else if (!rtc->ops->read_alarm)
            err = -EINVAL;
        else {
            memset(alarm, 0, sizeof(struct rtc_wkalrm));
            err = rtc->ops->read_alarm(rtc->dev.parent, alarm);
        }
    
        mutex_unlock(&rtc->ops_lock);
        return err;
    }

    设置Alarm时间:

    static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
    {
        struct rtc_time tm;
        long now, scheduled;
        int err;
    
        err = rtc_valid_tm(&alarm->time);
        if (err)
            return err;
        rtc_tm_to_time(&alarm->time, &scheduled);
    
        /* Make sure we're not setting alarms in the past */
        err = __rtc_read_time(rtc, &tm);
        rtc_tm_to_time(&tm, &now);
        if (scheduled <= now)
            return -ETIME;
        /*
         * XXX - We just checked to make sure the alarm time is not
         * in the past, but there is still a race window where if
         * the is alarm set for the next second and the second ticks
         * over right here, before we set the alarm.
         */
    
        if (!rtc->ops)
            err = -ENODEV;
        else if (!rtc->ops->set_alarm)
            err = -EINVAL;
        else
            err = rtc->ops->set_alarm(rtc->dev.parent, alarm);
    
        return err;
    }

    打开关闭irq:

    int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled)
    {
        int err = mutex_lock_interruptible(&rtc->ops_lock);
        if (err)
            return err;
    
        if (rtc->aie_timer.enabled != enabled) {
            if (enabled)
                err = rtc_timer_enqueue(rtc, &rtc->aie_timer);
            else
                rtc_timer_remove(rtc, &rtc->aie_timer);
        }
    
        if (err)
            /* nothing */;
        else if (!rtc->ops)
            err = -ENODEV;
        else if (!rtc->ops->alarm_irq_enable)
            err = -EINVAL;
        else
            err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled);
    
        mutex_unlock(&rtc->ops_lock);
        return err;
    }
    EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable);
    
    static void rtc_alarm_disable(struct rtc_device *rtc)
    {
        if (!rtc->ops || !rtc->ops->alarm_irq_enable)
            return;
    
        rtc->ops->alarm_irq_enable(rtc->dev.parent, false);
    }

     七、总结

    AlarmTimer涉及到alarm_bases维护的一套数据、hrtimer、suspend流程、rtc设备、rtc timer。他的核心思想就是在系统进入睡眠,hrtimer硬件时钟都被关闭之后,能唤醒系统,执行超时动作。

  • 相关阅读:
    SQL Join的一些总结
    大型网站架构演变和知识体系
    从零开始学习jQuery (九) jQuery工具函数
    对称加密(1) 对称加密基本原理
    javascript 几个字符串截取函数
    .net 登录博客园 并且发表文章一篇
    304 Not Modified 与 IfModifiedSince 及 CacheControl
    代码设置输出缓存头的一些问题记录
    js事件冒泡,默认行为,阻止冒泡
    http协议中的一些基础知识
  • 原文地址:https://www.cnblogs.com/arnoldlu/p/7145879.html
Copyright © 2011-2022 走看看