RTC 层次结构

来源:互联网 发布:淘宝空中充值店怎么开 编辑:程序博客网 时间:2024/05/28 05:18

     alarm.c   rtc-dev.c

            |  /

            | /

            |/

     interface.c

            |

            |

    rtc-m41t80.c

 

rtc-m41t80.c

static struct rtc_class_ops m41t80_rtc_ops = {
 .read_time = m41t80_rtc_read_time,
 .set_time = m41t80_rtc_set_time,
 .read_alarm = m41t80_rtc_read_alarm,
 .set_alarm = m41t80_rtc_set_alarm,
 .proc = m41t80_rtc_proc,
 .ioctl = m41t80_rtc_ioctl,
};

 

static int m41t80_probe(struct i2c_client *client,
   const struct i2c_device_id *id)
{
 
 rtc = rtc_device_register(client->name, &client->dev,
      &m41t80_rtc_ops, THIS_MODULE);
   rc = m41t80_sysfs_register(&client->dev);

}

static struct i2c_driver m41t80_driver = {
 .driver = {
  .name = "rtc-m41t80",
 },
 .probe = m41t80_probe,
 .remove = m41t80_remove,
 .id_table = m41t80_id,
};

static int __init m41t80_rtc_init(void)
{
 return i2c_add_driver(&m41t80_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-m41t80.c)