RTC m48t59 层次结构

本文详细解析了RTC芯片的工作原理及Alarm机制,包括如何通过设置RTC芯片实现精确的闹钟功能,以及系统睡眠和唤醒过程中RTC Alarm的具体操作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用程序设置了闹钟后,机器睡眠时候调用alarm_suspend,再调用interface.c ----》rtc-m41t80.c将alarm的DW DM hour minute和对应的EA位(设置到0使能闹钟),设置到rtc 芯片,alarm时间到后,产生中断将cpu唤醒。cpu处理rtc的中断将alarm flag清除,并通知上层。

m48t59_rtc_interrupt--->rtc_update_irq(m48t59->rtc, 1, (RTC_AF | RTC_IRQF));-->

-->rtc->irq_task->func(rtc->irq_task->private_data);
wake_up_interruptible(&rtc->irq_queue);

--> alarm_timer_triggered(struct hrtimer *timer) <alarm_enabled &= ~alarm_type_mask;>

alarm.c rtc-dev.c

| /

| /

|/

interface.c

|

|

rtc-m48t59.c

static const struct rtc_class_ops m48t59_rtc_ops = {
.ioctl= m48t59_rtc_ioctl,
.read_time= m48t59_rtc_read_time,
.set_time= m48t59_rtc_set_time,
.read_alarm= m48t59_rtc_readalarm,
.set_alarm= m48t59_rtc_setalarm,
.proc= m48t59_rtc_proc,
};

static const struct rtc_class_ops m48t02_rtc_ops = {
.read_time= m48t59_rtc_read_time,
.set_time= m48t59_rtc_set_time,
};

static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
{

m48t59->rtc = rtc_device_register(name, &pdev->dev, ops, THIS_MODULE);
m48t59_nvram_attr.size = pdata->offset;

ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr);
platform_set_drvdata(pdev, m48t59);

}

static struct platform_driver m48t59_rtc_driver = {
.driver= {
.name= "rtc-m48t59",
.owner= THIS_MODULE,
},
.probe= m48t59_rtc_probe,
.remove= __devexit_p(m48t59_rtc_remove),
};

static int __init m48t59_rtc_init(void)
{
return platform_driver_register(&m48t59_rtc_driver);
}

/rtc/class.c

struct rtc_device *rtc_device_register(const char *name, struct device *dev,
const struct rtc_class_ops *ops,
struct module *owner)
{

strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
dev_set_name(&rtc->dev, "rtc%d", id);

rtc_dev_prepare(rtc);

err = device_register(&rtc->dev);
if (err)
goto exit_kfree;

rtc_dev_add_device(rtc);
rtc_sysfs_add_device(rtc);
rtc_proc_add_device(rtc);


}

rtc-dev.c

static const struct file_operations rtc_dev_fops = {
.owner= THIS_MODULE,
.llseek= no_llseek,
.read= rtc_dev_read,
.poll= rtc_dev_poll,
.unlocked_ioctl= rtc_dev_ioctl,
.open= rtc_dev_open,
.release= rtc_dev_release,
.fasync= rtc_dev_fasync,
};

rtc/interface.c

#include <linux/rtc.h>
#include <linux/log2.h>

int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err;

memset(tm, 0, sizeof(struct rtc_time));
err = rtc->ops->read_time(rtc->dev.parent, tm);
return err;
}
EXPORT_SYMBOL_GPL(rtc_read_time);

int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int 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);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_time);

int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)
{
int err;
rtc_time_to_tm(secs, &new);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_mmss);

static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err;
err = rtc->ops->read_alarm(rtc->dev.parent, alarm);
return err;
}

int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err;
struct rtc_time before, now;
int first_time = 1;
unsigned long t_now, t_alm;
enum { none, day, month, year } missing = none;
unsigned days;


/* Get the "before" timestamp */
err = rtc_read_time(rtc, &before);

/* get the RTC alarm values, which may be incomplete */
err = rtc_read_alarm_internal(rtc, alarm);
if (err)
return err;

/* get the "after" timestamp, to detect wrapped fields */
err = rtc_read_time(rtc, &now);

/* with luck, no rollover is needed */
rtc_tm_to_time(&now, &t_now);
rtc_tm_to_time(&alarm->time, &t_alm);
if (t_now < t_alm)
goto done;

switch (missing) {

/* 24 hour rollover ... if it's now 10am Monday, an alarm that
* that will trigger at 5am will do so at 5am Tuesday, which
* could also be in the next month or year. This is a common
* case, especially for PCs.
*/
case day:
dev_dbg(&rtc->dev, "alarm rollover: %s/n", "day");
t_alm += 24 * 60 * 60;
rtc_time_to_tm(t_alm, &alarm->time);
break;

/* Month rollover ... if it's the 31th, an alarm on the 3rd will
* be next month. An alarm matching on the 30th, 29th, or 28th
* may end up in the month after that! Many newer PCs support
* this type of alarm.
*/
case month:
dev_dbg(&rtc->dev, "alarm rollover: %s/n", "month");
do {
if (alarm->time.tm_mon < 11)
alarm->time.tm_mon++;
else {
alarm->time.tm_mon = 0;
alarm->time.tm_year++;
}
days = rtc_month_days(alarm->time.tm_mon,
alarm->time.tm_year);
} while (days < alarm->time.tm_mday);
break;

/* Year rollover ... easy except for leap years! */
case year:
dev_dbg(&rtc->dev, "alarm rollover: %s/n", "year");
do {
alarm->time.tm_year++;
} while (rtc_valid_tm(&alarm->time) != 0);
break;

default:
dev_warn(&rtc->dev, "alarm rollover not handled/n");
}

done:
return 0;
}
EXPORT_SYMBOL_GPL(rtc_read_alarm);

int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err;

err = rtc->ops->set_alarm(rtc->dev.parent, alarm);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_alarm);

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->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);

int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled)
{
int err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err;

#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
if (enabled == 0 && rtc->uie_irq_active) {
mutex_unlock(&rtc->ops_lock);
return rtc_dev_update_irq_enable_emul(rtc, enabled);
}
#endif

if (!rtc->ops)
err = -ENODEV;
else if (!rtc->ops->update_irq_enable)
err = -EINVAL;
else
err = rtc->ops->update_irq_enable(rtc->dev.parent, enabled);

mutex_unlock(&rtc->ops_lock);

#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
/*
* Enable emulation if the driver did not provide
* the update_irq_enable function pointer or if returned
* -EINVAL to signal that it has been configured without
* interrupts or that are not available at the moment.
*/
if (err == -EINVAL)
err = rtc_dev_update_irq_enable_emul(rtc, enabled);
#endif
return err;
}
EXPORT_SYMBOL_GPL(rtc_update_irq_enable);

/**
* rtc_update_irq - report RTC periodic, alarm, and/or update irqs
* @rtc: the rtc device
* @num: how many irqs are being reported (usually one)
* @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF
* Context: in_interrupt(), irqs blocked
*/
void rtc_update_irq(struct rtc_device *rtc,
unsigned long num, unsigned long events)
{
spin_lock(&rtc->irq_lock);
rtc->irq_data = (rtc->irq_data + (num << 8)) | events;
spin_unlock(&rtc->irq_lock);

spin_lock(&rtc->irq_task_lock);
if (rtc->irq_task)
rtc->irq_task->func(rtc->irq_task->private_data);
spin_unlock(&rtc->irq_task_lock);

wake_up_interruptible(&rtc->irq_queue);
kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);
}
EXPORT_SYMBOL_GPL(rtc_update_irq);

static int __rtc_match(struct device *dev, void *data)
{
char *name = (char *)data;

if (strcmp(dev_name(dev), name) == 0)
return 1;
return 0;
}

struct rtc_device *rtc_class_open(char *name)
{
struct device *dev;
struct rtc_device *rtc = NULL;

dev = class_find_device(rtc_class, NULL, name, __rtc_match);
if (dev)
rtc = to_rtc_device(dev);

if (rtc) {
if (!try_module_get(rtc->owner)) {
put_device(dev);
rtc = NULL;
}
}

return rtc;
}
EXPORT_SYMBOL_GPL(rtc_class_open);

void rtc_class_close(struct rtc_device *rtc)
{
module_put(rtc->owner);
put_device(&rtc->dev);
}
EXPORT_SYMBOL_GPL(rtc_class_close);

int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task)
{
int retval = -EBUSY;

if (task == NULL || task->func == NULL)
return -EINVAL;

/* Cannot register while the char dev is in use */
if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags))
return -EBUSY;

spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task == NULL) {
rtc->irq_task = task;
retval = 0;
}
spin_unlock_irq(&rtc->irq_task_lock);

clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);

return retval;
}
EXPORT_SYMBOL_GPL(rtc_irq_register);

void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task)
{
spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task == task)
rtc->irq_task = NULL;
spin_unlock_irq(&rtc->irq_task_lock);
}
EXPORT_SYMBOL_GPL(rtc_irq_unregister);

/**
* rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs
* @rtc: the rtc device
* @task: currently registered with rtc_irq_register()
* @enabled: true to enable periodic IRQs
* Context: any
*
* Note that rtc_irq_set_freq() should previously have been used to
* specify the desired frequency of periodic IRQ task->func() callbacks.
*/
int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled)
{
int err = 0;
unsigned long flags;

if (rtc->ops->irq_set_state == NULL)
return -ENXIO;

spin_lock_irqsave(&rtc->irq_task_lock, flags);
if (rtc->irq_task != NULL && task == NULL)
err = -EBUSY;
if (rtc->irq_task != task)
err = -EACCES;
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);

if (err == 0)
err = rtc->ops->irq_set_state(rtc->dev.parent, enabled);

return err;
}
EXPORT_SYMBOL_GPL(rtc_irq_set_state);

/**
* rtc_irq_set_freq - set 2^N Hz periodic IRQ frequency for IRQ
* @rtc: the rtc device
* @task: currently registered with rtc_irq_register()
* @freq: positive frequency with which task->func() will be called
* Context: any
*
* Note that rtc_irq_set_state() is used to enable or disable the
* periodic IRQs.
*/
int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)
{
int err = 0;
unsigned long flags;

if (rtc->ops->irq_set_freq == NULL)
return -ENXIO;

spin_lock_irqsave(&rtc->irq_task_lock, flags);
if (rtc->irq_task != NULL && task == NULL)
err = -EBUSY;
if (rtc->irq_task != task)
err = -EACCES;
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);

if (err == 0) {
err = rtc->ops->irq_set_freq(rtc->dev.parent, freq);
if (err == 0)
rtc->irq_freq = freq;
}
return err;
}
EXPORT_SYMBOL_GPL(rtc_irq_se

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

alarm.c

static int rtc_alarm_add_device(struct device *dev,
struct class_interface *class_intf)
{

err = misc_register(&alarm_device);
alarm_platform_dev =
platform_device_register_simple("alarm", -1, NULL, 0);
err = rtc_irq_register(rtc, &alarm_rtc_task);
return err;
}

static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{

switch (ANDROID_ALARM_BASE_CMD(cmd)) {
case ANDROID_ALARM_CLEAR(0):
alarm_enabled &= ~alarm_type_mask;
break;

case ANDROID_ALARM_SET_OLD:
case ANDROID_ALARM_SET_AND_WAIT_OLD:
if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) {
rv = -EFAULT;
goto err1;
}
new_alarm_time.tv_nsec = 0;
goto from_old_alarm_set;

case ANDROID_ALARM_SET_AND_WAIT(0):
case ANDROID_ALARM_SET(0):
if (copy_from_user(&new_alarm_time, (void __user *)arg,
sizeof(new_alarm_time))) {
rv = -EFAULT;
goto err1;
}
from_old_alarm_set:
spin_lock_irqsave(&alarm_slock, flags);
ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO,
"alarm %d set %ld.%09ld/n", alarm_type,
new_alarm_time.tv_sec, new_alarm_time.tv_nsec);
alarm_time[alarm_type] = new_alarm_time;
alarm_enabled |= alarm_type_mask;
alarm_start_hrtimer(alarm_type);
spin_unlock_irqrestore(&alarm_slock, flags);
if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)
&& cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)
break;
/* fall though */
case ANDROID_ALARM_WAIT:
spin_lock_irqsave(&alarm_slock, flags);

break;
case ANDROID_ALARM_SET_RTC:
if (copy_from_user(&new_rtc_time, (void __user *)arg,
sizeof(new_rtc_time))) {
rv = -EFAULT;
goto err1;
}
rtc_time_to_tm(new_rtc_time.tv_sec, &rtc_new_rtc_time);


mutex_lock(&alarm_setrtc_mutex);
spin_lock_irqsave(&alarm_slock, flags);
for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++)
hrtimer_try_to_cancel(&alarm_timer[i]);
getnstimeofday(&tmp_time);
elapsed_rtc_delta = timespec_sub(elapsed_rtc_delta,
timespec_sub(tmp_time, new_rtc_time));
spin_unlock_irqrestore(&alarm_slock, flags);
rv = do_settimeofday(&new_rtc_time);
spin_lock_irqsave(&alarm_slock, flags);
for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++)
alarm_start_hrtimer(i);
spin_unlock_irqrestore(&alarm_slock, flags);
if (rv < 0) {
ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_ERRORS,
"Failed to set time/n");
mutex_unlock(&alarm_setrtc_mutex);
goto err1;
}
rv = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time);
alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;

break;
case ANDROID_ALARM_GET_TIME(0):
mutex_lock(&alarm_setrtc_mutex);
spin_lock_irqsave(&alarm_slock, flags);
if (alarm_type != ANDROID_ALARM_SYSTEMTIME) {
getnstimeofday(&tmp_time);
if (alarm_type >= ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP)
tmp_time = timespec_sub(tmp_time,
elapsed_rtc_delta);
} else
ktime_get_ts(&tmp_time);
spin_unlock_irqrestore(&alarm_slock, flags);
mutex_unlock(&alarm_setrtc_mutex);
if (copy_to_user((void __user *)arg, &tmp_time,
sizeof(tmp_time))) {
rv = -EFAULT;
goto err1;
}
break;

default:
rv = -EINVAL;
goto err1;
}
err1:
return rv;
}

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

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

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

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,
};

static int __init alarm_init(void)
{

err = platform_driver_register(&alarm_driver);
rtc_alarm_interface.class = rtc_class;
err = class_interface_register(&rtc_alarm_interface);
return err;
}

alarm_ioctl---->alarm_enabled |= alarm_type_mask;
| alarm_start_hrtimer(alarm_type);

|

alarm_suspend-->rtc_alarm.enabled = 1;
| rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);
|rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time);
|rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time);

|(alarm.c)

|

|

rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);------>err = rtc->ops->set_alarm(rtc->dev.parent, alarm);

|(interface.c)

|

|

m41t80_rtc_set_alarm

(rtc-m48t59.c)

rtc_time_to_tm(rtc_alarm_time, &rtc_alarm.time);
rtc_alarm.enabled = 1;
rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);
rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time);
rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time);

if (rtc_current_time + 1 >= rtc_alarm_time) {
ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INFO,
"alarm about to go off/n");
memset(&rtc_alarm, 0, sizeof(rtc_alarm));
rtc_alarm.enabled = 0;
rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);

}

MMI设置alarm时间

应用程序设置alarm的时候通过调用alarm_ioctl的case ANDROID_ALARM_SET(0):,通过copy_from_user将用户空间的时间参数(从1970年1月1日开始的绝对秒)拷贝到内核空间,通过

alarm_time[alarm_type] = new_alarm_time;
alarm_enabled |= alarm_type_mask;
alarm_start_hrtimer(alarm_type);

case ANDROID_ALARM_SET_OLD:
case ANDROID_ALARM_SET_AND_WAIT_OLD:
if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) {
rv = -EFAULT;
goto err1;
}
new_alarm_time.tv_nsec = 0;
goto from_old_alarm_set;

case ANDROID_ALARM_SET_AND_WAIT(0):
case ANDROID_ALARM_SET(0):
if (copy_from_user(&new_alarm_time, (void __user *)arg,
sizeof(new_alarm_time))) {
rv = -EFAULT;
goto err1;
}
from_old_alarm_set:
spin_lock_irqsave(&alarm_slock, flags);
ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO,
"alarm %d set %ld.%09ld/n", alarm_type,
new_alarm_time.tv_sec, new_alarm_time.tv_nsec);
alarm_time[alarm_type] = new_alarm_time;
alarm_enabled |= alarm_type_mask;
alarm_start_hrtimer(alarm_type);
spin_unlock_irqrestore(&alarm_slock, flags);
if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)
&& cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)
break;

设置alarm的时间到结构变量alarm_time[alarm_type] ,和设置alarm_enabled标志,启动定时器计数

alarm_start_hrtimer(alarm_type);

开始计时间,

同时应用程序发ioctrl命令ANDROID_ALARM_WAIT,等待定时器计时完成

case ANDROID_ALARM_WAIT:
spin_lock_irqsave(&alarm_slock, flags);
ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_IO, "alarm wait/n");
if (!alarm_pending && wait_pending) {
wake_unlock(&alarm_wake_lock);
wait_pending = 0;
}
spin_unlock_irqrestore(&alarm_slock, flags);
rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
if (rv)
goto err1;
spin_lock_irqsave(&alarm_slock, flags);
rv = alarm_pending;
wait_pending = 1;
alarm_pending = 0;
if (rv & ANDROID_ALARM_WAKEUP_MASK)
wake_unlock(&alarm_rtc_wake_lock);
spin_unlock_irqrestore(&alarm_slock, flags);
break;

在等待定时器时间到的过程停留在函数rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
当alarm时间到了就调用,hrtimer的回调函数

static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer)
{
unsigned long flags;
enum android_alarm_type alarm_type = (timer - alarm_timer);
uint32_t alarm_type_mask = 1U << alarm_type;


ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INT,
"alarm_timer_triggered type %d/n", alarm_type);
spin_lock_irqsave(&alarm_slock, flags);
if (alarm_enabled & alarm_type_mask) {
wake_lock_timeout(&alarm_wake_lock, 5 * HZ);
alarm_enabled &= ~alarm_type_mask;
alarm_pending |= alarm_type_mask;
wake_up(&alarm_wait_queue);
}
spin_unlock_irqrestore(&alarm_slock, flags);
return HRTIMER_NORESTART;
}

由wake_up(&alarm_wait_queue);
唤醒alarm_wait_queue,继续执行case ANDROID_ALARM_WAIT:的
函数rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);下面的代码

用户空间的alarm服务收到后广播,启动alarm图标和铃声,震动

ANDROID_ALARM_SET(0):---》mmi设置绝对的秒到hrtimer定时器

ANDROID_ALARM_WAIT--》 mmi发等待hrtimer定时器timeout

rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);--》暂停在这里等待定时器timeout

alarm_timer_triggered--》定时器timeout执行定时器回调函数

wake_up(&alarm_wait_queue);---》定时器回调函数唤醒等待队列

rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);--》继续往下执行

系统睡眠alarm

系统睡眠时通过判断ioctl设置的alarm_enabled标志,如果为真就读出定时器的值和rtc的时间的差设置alarm时间

rtc_alarm.enabled = 1;
rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);

对比rtc的当前时间和alarm的时间,判断是否时间已经过,如果alarm时间过了就通过

rtc_alarm.enabled = 0;
rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);

发命令到rtc芯片将alarm关闭,取消rtc的alarm设置。

如果没过期就将绝对秒睡眠时间转换为year,month,hour,minute,设置rtc

当alarm时间到,RTC芯片的中断引脚产生int将cpu唤醒,执行唤醒流程,恢复系统时间,在alarm_resume取消RTC芯片的alarm,启动hrtimer定时器

int alarm_resume(struct platform_device *pdev)
{
struct rtc_wkalrm alarm;
ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW,
"alarm_resume(%p)/n", pdev);
if (alarm_enabled & ANDROID_ALARM_WAKEUP_MASK) {
memset(&alarm, 0, sizeof(alarm));
alarm.enabled = 0;
rtc_set_alarm(alarm_rtc_dev, &alarm);
alarm_start_hrtimer(ANDROID_ALARM_RTC_WAKEUP);
alarm_start_hrtimer(ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP);
}
return 0;
}

上层的alarm服务程序也恢复到监控开始工作,对比时间是否到,到了就广播,alarm 的ui就将alarm图标和闹铃,震动启动。

当alarm时间到了就调用

static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer)
{
unsigned long flags;
enum android_alarm_type alarm_type = (timer - alarm_timer);
uint32_t alarm_type_mask = 1U << alarm_type;


ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_INT,
"alarm_timer_triggered type %d/n", alarm_type);
spin_lock_irqsave(&alarm_slock, flags);
if (alarm_enabled & alarm_type_mask) {
wake_lock_timeout(&alarm_wake_lock, 5 * HZ);
alarm_enabled &= ~alarm_type_mask;
alarm_pending |= alarm_type_mask;
wake_up(&alarm_wait_queue);
}
spin_unlock_irqrestore(&alarm_slock, flags);
return HRTIMER_NORESTART;
}

由wake_up(&alarm_wait_queue);
唤醒alarm_wait_queue,

rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);--》继续往下执行

用户空间的alarm服务收到后广播,启动alarm图标和铃声,震动

alarm_resume

alarm唤醒后通过

rtc_alarm.enabled = 0;
rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);

取消rtc IC的alarm设置

**

用户空间是绝对秒,

struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};

rtc模块是year,month,hour,minute,second ,dayofmonth,dayofweek的格式


struct rtc_time {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};

/*
* This data structure is inspired by the EFI (v0.92) wakeup
* alarm API.
*/
struct rtc_wkalrm {
unsigned char enabled;/* 0 = alarm disabled, 1 = alarm enabled */
unsigned char pending; /* 0 = alarm not pending, 1 = alarm pending */
struct rtc_time time;/* time the alarm is set to */
};

kernel唤醒后调用alarm_resume,alarm.enabled = 0;关闭alarm,启动定时器计时间。

int alarm_resume(struct platform_device *pdev)
{
struct rtc_wkalrm alarm;
ANDROID_ALARM_DPRINTF(ANDROID_ALARM_PRINT_FLOW,
"alarm_resume(%p)/n", pdev);
if (alarm_enabled & ANDROID_ALARM_WAKEUP_MASK) {
memset(&alarm, 0, sizeof(alarm));
alarm.enabled = 0;
rtc_set_alarm(alarm_rtc_dev, &alarm);
alarm_start_hrtimer(ANDROID_ALARM_RTC_WAKEUP);
alarm_start_hrtimer(ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP);
}
return 0;
}

kernel on时用定时器计时间,sleep时用rtc的alarm计时间

rtc的alarm的on和off由rtc ic的寄存器的alarm_min ,alarm_hour ,alarm_wday,alarm_mday的最高位决定,为0时是enable,1为disable

rtc m48t51 除了rtc电子表,alarm闹钟功能外,还有timer定时器功能,

1 ,alarm闹钟功能

将alarm的min hour wday mday设置到寄存器和对应的alarm enable设置为0(enable),设置alarm interrupt enable ,和int脚功能

为alarm interrupt,当alarm时间和rtc时间match就产生中断将cpu唤醒或产生中断

2,timer定时器功能

将定时器的计数时钟频率设置到分频寄存器,要定时的秒设置到countdown寄存器,设置timer interrupt enable,和int脚功能

为timer interrupt,当定时时间到了就产生中断,将cpu唤醒或产生中断。

alarm 或者timer产生中断后,如果int引脚设置为中断输出,int脚为低电平,要往status寄存器的AF或TF位写0清除后才变高电平。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值