基于S3C2440的Linux-3.6.6移植——实时时钟RTC

 

在arch/arm/plat-samsung/Devs.c文件内,系统定义了RTC平台设备及其资源:

static struct resource s3c_rtc_resource[] = {

       [0]= DEFINE_RES_MEM(S3C24XX_PA_RTC,SZ_256),

       [1]= DEFINE_RES_IRQ(IRQ_RTC),

       [2]= DEFINE_RES_IRQ(IRQ_TICK),

};

 

struct platform_device s3c_device_rtc = {

       .name             = "s3c2410-rtc",

       .id          = -1,

       .num_resources      = ARRAY_SIZE(s3c_rtc_resource),

       .resource = s3c_rtc_resource,

};

 

因为RTC一共有两个中断源:报警中断和时间节拍中断,所以RTC资源中也相应的定义了两个中断——IRQ_RTC和IRQ_TICK。在arch/arm/mach-s3c24xx/Mach-zhaocj2440.c文件内,系统把RTC平台设备添加到了zhaocj2440_devices数组内:

static struct platform_device *zhaocj2440_devices[]__initdata = {

……

       &s3c_device_rtc,

       ……

};

 

最后在zhaocj2440_init函数内,通过下列语句把RTC平台设备添加到了总线内:

platform_add_devices(zhaocj2440_devices,ARRAY_SIZE(zhaocj2440_devices));

 

上面介绍的是RTC平台设备,而它的平台驱动是在drivers/rtc/Rtc-s3c.c文件内定义的:

static struct platform_driver s3c_rtc_driver = {

       .probe            = s3c_rtc_probe,

       .remove          = __devexit_p(s3c_rtc_remove),

       .suspend  = s3c_rtc_suspend,

       .resume          = s3c_rtc_resume,

       .id_table  = s3c_rtc_driver_ids,

       .driver            = {

              .name      = "s3c-rtc",

              .owner    = THIS_MODULE,

              .of_match_table     = s3c_rtc_dt_match,

       },

};

 

由于定义了RTC平台设备列表s3c_rtc_driver_ids,因此平台驱动通过这个列表与平台设备相互匹配:

static struct platform_device_id s3c_rtc_driver_ids[] = {

       {

              .name             = "s3c2410-rtc",

              .driver_data    = TYPE_S3C2410,

       },{

              .name             = "s3c2416-rtc",

              .driver_data    = TYPE_S3C2416,

       },{

              .name             = "s3c2443-rtc",

              .driver_data    = TYPE_S3C2443,

       },{

              .name             = "s3c64xx-rtc",

              .driver_data    = TYPE_S3C64XX,

       },

       {}

};

 

在上面这个列表中,有s3c2410-rtc,因此RTC设备与驱动匹配上了。下面我们再来看看s3c_rtc_probe函数:

static int __devinit s3c_rtc_probe(struct platform_device *pdev)

{

       structrtc_device *rtc;

       structrtc_time rtc_tm;

       structresource *res;

       intret;

       inttmp;

 

       pr_debug("%s:probe=%p\n", __func__, pdev);

 

       /*find the IRQs */

       //得到RTC的时间节拍中断号

       s3c_rtc_tickno = platform_get_irq(pdev,1);

       if(s3c_rtc_tickno < 0) {

              dev_err(&pdev->dev,"no irq for rtc tick\n");

              return-ENOENT;

       }

 

       //得到RTC的报警中断号

       s3c_rtc_alarmno = platform_get_irq(pdev,0);

       if(s3c_rtc_alarmno < 0) {

              dev_err(&pdev->dev, "no irqfor alarm\n");

              return-ENOENT;

       }

 

       pr_debug("s3c2410_rtc: tick irq %d, alarm irq%d\n",

               s3c_rtc_tickno,s3c_rtc_alarmno);

 

       /*get the memory region */

       //得到RTC的内存资源

       res= platform_get_resource(pdev, IORESOURCE_MEM, 0);

       if(res == NULL) {

              dev_err(&pdev->dev,"failed to get memory region resource\n");

              return-ENOENT;

       }

 

       //申请内存资源

       s3c_rtc_mem =request_mem_region(res->start, resource_size(res),

                                    pdev->name);

 

       if(s3c_rtc_mem == NULL) {

              dev_err(&pdev->dev,"failed to reserve memory region\n");

              ret= -ENOENT;

              gotoerr_nores;

       }

 

       //将内存重新映射

       s3c_rtc_base = ioremap(res->start,resource_size(res));

       if(s3c_rtc_base == NULL) {

              dev_err(&pdev->dev,"failed ioremap()\n");

              ret= -EINVAL;

              gotoerr_nomap;

       }

 

       //得到RTC的时钟信号

       rtc_clk= clk_get(&pdev->dev, "rtc");

       if(IS_ERR(rtc_clk)) {

              dev_err(&pdev->dev,"failed to find rtc clock source\n");

              ret = PTR_ERR(rtc_clk);

              rtc_clk = NULL;

              goto err_clk;

       }

 

       //RTC时钟信号有效

       clk_enable(rtc_clk);

 

       /* check to seeif everything is setup correctly */

       //使能RTC,并对RTCCON寄存器进行设置;如果该函数的第二个参数为0,则无效RTC

       s3c_rtc_enable(pdev, 1);

 

       pr_debug("s3c2410_rtc: RTCCON=%02x\n",

               readw(s3c_rtc_base+ S3C2410_RTCCON));

 

       //唤醒RTC设备

       device_init_wakeup(&pdev->dev,1);

 

       /*register RTC and exit */

       //注册RTC类

       rtc= rtc_device_register("s3c",&pdev->dev, &s3c_rtcops,

                              THIS_MODULE);

 

       if(IS_ERR(rtc)) {

              dev_err(&pdev->dev,"cannot attach rtc\n");

              ret= PTR_ERR(rtc);

              gotoerr_nortc;

       }

 

       //得到当前CPU的类型,因为该驱动是通用的,也适用于其他s3c类型的处理器

       s3c_rtc_cpu_type = s3c_rtc_get_driver_data(pdev);

 

       /*Check RTC Time */

       //得到RTC的当前时间

       s3c_rtc_gettime(NULL, &rtc_tm);

       //如果RTC的当前时间无效,则重新设置

       if(rtc_valid_tm(&rtc_tm)) {

              rtc_tm.tm_year      = 100;

              rtc_tm.tm_mon      = 0;

              rtc_tm.tm_mday     = 1;

              rtc_tm.tm_hour      = 0;

              rtc_tm.tm_min       = 0;

              rtc_tm.tm_sec = 0;

              //设置RTC当前时间

              s3c_rtc_settime(NULL, &rtc_tm);

 

              dev_warn(&pdev->dev,"warning: invalid RTC value so initializing it\n");

       }

 

       //依据CPU的类型,设置RTC节拍

       if(s3c_rtc_cpu_type != TYPE_S3C2410)

              rtc->max_user_freq= 32768;

       else

              rtc->max_user_freq= 128;

 

       if(s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) {

              tmp= readw(s3c_rtc_base + S3C2410_RTCCON);

              tmp|= S3C2443_RTCCON_TICSEL;

              writew(tmp,s3c_rtc_base + S3C2410_RTCCON);

       }

 

       //保存平台总线设备的私有数据,

       platform_set_drvdata(pdev, rtc);

 

       //设置TICNT寄存器

       s3c_rtc_setfreq(&pdev->dev, 1);

 

       //申请RTC报警中断

       ret= request_irq(s3c_rtc_alarmno,s3c_rtc_alarmirq,

                       0, "s3c2410-rtcalarm", rtc);

       if(ret) {

              dev_err(&pdev->dev,"IRQ%d error %d\n", s3c_rtc_alarmno,ret);

              gotoerr_alarm_irq;

       }

 

       //申请RTC时间节拍中断

       ret= request_irq(s3c_rtc_tickno,s3c_rtc_tickirq,

                       0, "s3c2410-rtctick", rtc);

       if(ret) {

              dev_err(&pdev->dev,"IRQ%d error %d\n", s3c_rtc_tickno,ret);

              free_irq(s3c_rtc_alarmno, rtc);

              gotoerr_tick_irq;

       }

 

       //RTC时钟无效

       clk_disable(rtc_clk);

 

       return0;

 

 err_tick_irq:

       free_irq(s3c_rtc_alarmno, rtc);

 

 err_alarm_irq:

       platform_set_drvdata(pdev,NULL);

       rtc_device_unregister(rtc);

 

 err_nortc:

       s3c_rtc_enable(pdev, 0);

       clk_disable(rtc_clk);

       clk_put(rtc_clk);

 

 err_clk:

       iounmap(s3c_rtc_base);

 

 err_nomap:

       release_resource(s3c_rtc_mem);

 

 err_nores:

       returnret;

}

 

下面介绍一下2440的RTC操作集——s3c_rtcops:

static const struct rtc_class_ops s3c_rtcops = {

       .read_time      = s3c_rtc_gettime,         //读取当前时间

       .set_time = s3c_rtc_settime,                //设置当前时间

       .read_alarm    = s3c_rtc_getalarm,              //读取报警时间

       .set_alarm       = s3c_rtc_setalarm,        //设置报警时间

       .proc              = s3c_rtc_proc,            

       .alarm_irq_enable= s3c_rtc_setaie,      //用于设置RTCALM寄存器

};

 

在上面介绍过的s3c_rtc_probe函数内,用到了rtc_device_register函数来注册RTC设备,该函数在drivers/rtc/Class.c文件内被定义。Class.c文件主要定义了RTC子系统。在该文件中,有:

subsys_initcall(rtc_init);

 

说明系统启动后会执行rtc_init函数:

static int __init rtc_init(void)

{

       //创建RTC子类

       rtc_class= class_create(THIS_MODULE, "rtc");

       if(IS_ERR(rtc_class)) {

              printk(KERN_ERR"%s: couldn't create class\n", __FILE__);

              returnPTR_ERR(rtc_class);

       }

       rtc_class->suspend= rtc_suspend;

       rtc_class->resume= rtc_resume;

       //RTC设备初始化

       rtc_dev_init();

       rtc_sysfs_init(rtc_class);

       return0;

}

 

上面函数中的rtc_dev_init函数是在drivers/rtc/Rtc-dev.c文件内定义的:

void __init rtc_dev_init(void)

{

       interr;

 

       //申请一个字符设备,RTC也是一个字符设备

       err= alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");

       if(err < 0)

              printk(KERN_ERR"%s: failed to allocate char dev region\n",

                     __FILE__);

}

 

通过上面分析可看出,系统启动后会自动申请RTC,而具体的注册该设备是靠前面提到的rtc_device_register函数来完成的。我们再来看看这个函数:

struct rtc_device*rtc_device_register(const char *name, struct device *dev,

                                   conststruct rtc_class_ops *ops,

                                   structmodule *owner)

{

       structrtc_device *rtc;

       structrtc_wkalrm alrm;

       intid, err;

 

       //得到一个新的ID

       id= ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);

       if(id < 0) {

              err= id;

              gotoexit;

       }

 

       //为RTC设备分配一块内存,并清零

       rtc= kzalloc(sizeof(struct rtc_device), GFP_KERNEL);

       if(rtc == NULL) {

              err= -ENOMEM;

              gotoexit_ida;

       }

 

       //为RTC设备赋值

       rtc->id= id;

       rtc->ops= ops;

       rtc->owner= owner;

       rtc->irq_freq= 1;

       rtc->max_user_freq= 64;

       rtc->dev.parent= dev;

       rtc->dev.class= rtc_class;

       rtc->dev.release= rtc_device_release;

 

       mutex_init(&rtc->ops_lock);

       spin_lock_init(&rtc->irq_lock);

       spin_lock_init(&rtc->irq_task_lock);

       init_waitqueue_head(&rtc->irq_queue);

 

       /* Init timerqueue */

       //初始化定时器队列

       timerqueue_init_head(&rtc->timerqueue);

       //在rtc->irqwork队列中添加rtc_timer_do_work任务,也就是当程序出现schedule_work(&rtc->irqwork)这个语句中,实际是调用的rtc_timer_do_work函数

       INIT_WORK(&rtc->irqwork,rtc_timer_do_work);

       /*Init aie timer */

       rtc_timer_init(&rtc->aie_timer,rtc_aie_update_irq, (void *)rtc);

       /*Init uie timer */

       rtc_timer_init(&rtc->uie_rtctimer,rtc_uie_update_irq, (void *)rtc);

       /* Init pie timer */

       hrtimer_init(&rtc->pie_timer,CLOCK_MONOTONIC, HRTIMER_MODE_REL);

       rtc->pie_timer.function = rtc_pie_update_irq;

       rtc->pie_enabled= 0;

 

       /*Check to see if there is an ALARM already set in hw */

       //检查是否硬件已发生了报警

       err= __rtc_read_alarm(rtc, &alrm);

 

       if(!err && !rtc_valid_tm(&alrm.time))

              rtc_initialize_alarm(rtc,&alrm);

 

       strlcpy(rtc->name,name, RTC_DEVICE_NAME_SIZE);

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

 

       rtc_dev_prepare(rtc);

 

       //注册RTC设备

       err= device_register(&rtc->dev);

       if(err) {

              put_device(&rtc->dev);

              gotoexit_kfree;

       }

 

       rtc_dev_add_device(rtc);

       //在sysfs文件系统中添加RTC设备

       rtc_sysfs_add_device(rtc);

       //在proc文件系统中添加RTC设备

       rtc_proc_add_device(rtc);

 

       dev_info(dev,"rtc core: registered %s as %s\n",

                     rtc->name,dev_name(&rtc->dev));

 

       returnrtc;

 

exit_kfree:

       kfree(rtc);

 

exit_ida:

       ida_simple_remove(&rtc_ida,id);

 

exit:

       dev_err(dev,"rtc core: unable to register %s, err = %d\n",

                     name,err);

       returnERR_PTR(err);

}

 

在上面的函数中用到了rtc_dev_prepare和rtc_dev_add_device函数,这两个函数都是在drivers/rtc/Rtc-dev.c文件内定义的:

void rtc_dev_prepare(struct rtc_device*rtc)

{

       if(!rtc_devt)

              return;

 

       //RTC设备不能多于16个

       if(rtc->id >= RTC_DEV_MAX) {

              pr_debug("%s:too many RTC devices\n", rtc->name);

              return;

       }

 

       rtc->dev.devt= MKDEV(MAJOR(rtc_devt), rtc->id);

 

#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL

       INIT_WORK(&rtc->uie_task,rtc_uie_task);

       setup_timer(&rtc->uie_timer,rtc_uie_timer, (unsigned long)rtc);

#endif

 

       //初始化字符设备结构

       cdev_init(&rtc->char_dev,&rtc_dev_fops);

       rtc->char_dev.owner= rtc->owner;

}

 

voidrtc_dev_add_device(struct rtc_device *rtc)

{

       //为系统添加RTC字符设备

       if (cdev_add(&rtc->char_dev,rtc->dev.devt, 1))

              printk(KERN_WARNING"%s: failed to add char device %d:%d\n",

                     rtc->name,MAJOR(rtc_devt), rtc->id);

       else

              pr_debug("%s:dev (%d:%d)\n", rtc->name,

                     MAJOR(rtc_devt),rtc->id);

}

 

在rtc_dev_prepare函数中提到了rtc_dev_fops,它就是RTC的操作集:

static const struct file_operationsrtc_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_dev_ioctl函数:

static long rtc_dev_ioctl(struct file*file,

              unsignedint cmd, unsigned long arg)

{

       interr = 0;

       structrtc_device *rtc = file->private_data;

       conststruct rtc_class_ops *ops = rtc->ops;

       structrtc_time tm;

       structrtc_wkalrm alarm;

       void __user *uarg = (void __user*) arg;

 

       err =mutex_lock_interruptible(&rtc->ops_lock);

       if(err)

              return err;

 

       /*check that the calling task has appropriate permissions

        * for certain ioctls. doing this check here isuseful

        * to avoid duplicate code in each driver.

        */

       //对有些命令先进行预处理,如果不符合要求,就提前退出

       switch(cmd) {

       caseRTC_EPOCH_SET:

       caseRTC_SET_TIME:

              if(!capable(CAP_SYS_TIME))

                     err= -EACCES;

              break;

 

       caseRTC_IRQP_SET:

              if(arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))

                     err= -EACCES;

              break;

 

       caseRTC_PIE_ON:

              if(rtc->irq_freq > rtc->max_user_freq &&

                            !capable(CAP_SYS_RESOURCE))

                     err= -EACCES;

              break;

       }

 

       if(err)

              gotodone;

 

       /*

        * Drivers *SHOULD NOT* provide ioctlimplementations

        * for these requests.  Instead, provide methods to

        * support the following code, so that theRTC's main

        * features are accessible without usingioctls.

        *

        * RTC and alarm times will be in UTC, bypreference,

        * but dual-booting with MS-Windows impliesRTCs must

        * use the local wall clock time.

        */

 

       switch(cmd) {

       caseRTC_ALM_READ:        //读报警时间

              mutex_unlock(&rtc->ops_lock);

              //读取报警时间,最终调用的是Rtc-s3c.c文件中的s3c_rtc_getalarm函数

              err= rtc_read_alarm(rtc, &alarm);

              if(err < 0)

                     returnerr;

 

              if(copy_to_user(uarg, &alarm.time, sizeof(tm)))

                     err= -EFAULT;

              returnerr;

 

       caseRTC_ALM_SET:           //设置报警时间

              mutex_unlock(&rtc->ops_lock);

 

              if(copy_from_user(&alarm.time, uarg, sizeof(tm)))

                     return-EFAULT;

 

              alarm.enabled= 0;

              alarm.pending= 0;

              alarm.time.tm_wday= -1;

              alarm.time.tm_yday= -1;

              alarm.time.tm_isdst= -1;

 

              /*RTC_ALM_SET alarms may be up to 24 hours in the future.

               * Rather than expecting every RTC to implement"don't care"

               * for day/month/year fields, just force thealarm to have

               * the right values for those fields.

               *

               * RTC_WKALM_SET should be used instead.  Not only does it

               * eliminate the need for a separate RTC_AIE_ONcall, it

               * doesn't have the "alarm 23:59:59 in the future" race.

               *

               * NOTE: some legacy code may have used invalid fields as

               * wildcards, exposing hardware "periodicalarm" capabilities.

               * Not supported here.

               */

              {

                     unsignedlong now, then;

                     //读取当前时间,最终调用的是Rtc-s3c.c文件中的s3c_rtc_gettime函数

                     err= rtc_read_time(rtc, &tm);

                     if(err < 0)

                            returnerr;

                     //转换当前时间格式

                     rtc_tm_to_time(&tm,&now);

 

                     alarm.time.tm_mday= tm.tm_mday;

                     alarm.time.tm_mon = tm.tm_mon;

                     alarm.time.tm_year= tm.tm_year;

                     err  = rtc_valid_tm(&alarm.time);

                     if(err < 0)

                            returnerr;

                     //转换报警时间格式

                     rtc_tm_to_time(&alarm.time,&then);

 

                     /*alarm may need to wrap into tomorrow */

                     //比较报警时间和当前时间,如果报警时间小于当前时间,则设置第二天的同一时间报警

                     if(then < now) {

                            rtc_time_to_tm(now+ 24 * 60 * 60, &tm);

                            alarm.time.tm_mday= tm.tm_mday;

                            alarm.time.tm_mon = tm.tm_mon;

                            alarm.time.tm_year = tm.tm_year;

                     }

              }

              //设置报警时间,最终调用的是Rtc-s3c.c文件中的s3c_rtc_setalarm函数

              returnrtc_set_alarm(rtc, &alarm);

 

       caseRTC_RD_TIME:           //读取RTC时间

              mutex_unlock(&rtc->ops_lock);

              //读取当前时间,最终调用的是Rtc-s3c.c文件中的s3c_rtc_gettime函数

              err= rtc_read_time(rtc, &tm);

              if(err < 0)

                     returnerr;

 

              if(copy_to_user(uarg, &tm, sizeof(tm)))

                     err= -EFAULT;

              returnerr;

 

       caseRTC_SET_TIME:          //设置RTC时间

              mutex_unlock(&rtc->ops_lock);

 

              if(copy_from_user(&tm, uarg, sizeof(tm)))

                     return-EFAULT;

              //设置RTC时间,最终调用的是Rtc-s3c.c文件中的s3c_rtc_settime函数

              returnrtc_set_time(rtc, &tm);

 

       caseRTC_PIE_ON:

              err= rtc_irq_set_state(rtc, NULL, 1);

              break;

 

       caseRTC_PIE_OFF:

              err= rtc_irq_set_state(rtc, NULL, 0);

              break;

 

       caseRTC_AIE_ON:              //报警中断有效

              mutex_unlock(&rtc->ops_lock);

              //设置报警中断,最终调用的是Rtc-s3c.c文件中的s3c_rtc_setaie函数

              returnrtc_alarm_irq_enable(rtc, 1);

 

       caseRTC_AIE_OFF:           //报警中断无效

              mutex_unlock(&rtc->ops_lock);

              returnrtc_alarm_irq_enable(rtc, 0);

 

       caseRTC_UIE_ON:

              mutex_unlock(&rtc->ops_lock);

              returnrtc_update_irq_enable(rtc, 1);

 

       caseRTC_UIE_OFF:

              mutex_unlock(&rtc->ops_lock);

              returnrtc_update_irq_enable(rtc, 0);

 

       caseRTC_IRQP_SET:

              err= rtc_irq_set_freq(rtc, NULL, arg);

              break;

 

       caseRTC_IRQP_READ:

              err= put_user(rtc->irq_freq, (unsigned long __user *)uarg);

              break;

 

#if 0

       caseRTC_EPOCH_SET:

#ifndef rtc_epoch

              /*

               * There were no RTC clocks before 1900.

               */

              if(arg < 1900) {

                     err= -EINVAL;

                     break;

              }

              rtc_epoch= arg;

              err= 0;

#endif

              break;

 

       caseRTC_EPOCH_READ:

              err= put_user(rtc_epoch, (unsigned long __user *)uarg);

              break;

#endif

       caseRTC_WKALM_SET:

              mutex_unlock(&rtc->ops_lock);

              if(copy_from_user(&alarm, uarg, sizeof(alarm)))

                     return-EFAULT;

 

              returnrtc_set_alarm(rtc, &alarm);

 

       caseRTC_WKALM_RD:

              mutex_unlock(&rtc->ops_lock);

              err= rtc_read_alarm(rtc, &alarm);

              if(err < 0)

                     returnerr;

 

              if(copy_to_user(uarg, &alarm, sizeof(alarm)))

                     err= -EFAULT;

              returnerr;

 

       default:

              /*Finally try the driver's ioctl interface */

              if(ops->ioctl) {

                     err= ops->ioctl(rtc->dev.parent, cmd, arg);

                     if(err == -ENOIOCTLCMD)

                            err= -ENOTTY;

              }else

                     err= -ENOTTY;

              break;

       }

 

done:

       mutex_unlock(&rtc->ops_lock);

       returnerr;

}

 

驱动介绍完了,下面介绍应用。

系统的默认配置已经添加了RTC的部分,因此不用再修改,而且当系统启动后,会读取RTC中的时钟,以更新同步系统时间。

 

系统启动后,从打印出的信息来看,RTC已加载:

s3c-rtc s3c2410-rtc: rtc disabled, re-enabling

s3c-rtc s3c2410-rtc: rtc core: registered s3c as rtc0

s3c-rtc s3c2410-rtc: warning: invalid RTC value so initializingit

 

并且还有下面一句:

s3c-rtcs3c2410-rtc: setting systemclock to 2000-01-01 00:00:00 UTC (946684800)

说明系统已进行了时钟同步。

 

系统启动后,我们可以通过命令来查看系统的时间:

[root@zhaocj/]#date

SatJan  1 00:01:04 UTC 2000

 

下面我们就写一段应用程序来查看和修改RTC时间:

/**************

***mydate.c***

**************/

 

#include<stdio.h>

#include<linux/rtc.h>

#include<sys/ioctl.h>

#include<sys/time.h>

#include<sys/types.h>

#include<fcntl.h>

#include<unistd.h>

#include<stdlib.h>

#include<errno.h>

 

intmain(int argc, char **argv)

{

       int fd, retval;

       struct rtc_time rtc_tm;

 

       fd = open("/dev/rtc0", O_RDONLY);

 

       if (fd ==  -1) {

              perror("/dev/rtc0");

              exit(errno);

       }

 

       printf("\n\tRTC Driver Test Example.\n");

 

       if(argc == 1)

         goto test_READ;

 

       if(argc !=7)

         {

           printf( "wrong!\n");

         return 1;

              }

       //为时间变量赋值

       //要把字符串转换成整型

//年和月比较特殊

       rtc_tm.tm_year =atoi(argv[1])-1900;

       rtc_tm.tm_mon=atoi(argv[2])-1;;

       rtc_tm.tm_mday=atoi(argv[3]);

 

 

       rtc_tm.tm_hour=atoi(argv[4]);

       rtc_tm.tm_min=atoi(argv[5]);

       rtc_tm.tm_sec=atoi(argv[6]);

 

       retval = ioctl(fd, RTC_SET_TIME, &rtc_tm);              //设置RTC时间

       if (retval == -1) {

              perror("RTC_RD_TIME ioctl");

              exit(errno);

       }

 

test_READ:

       retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);               //读取RTC时间

       if (retval == -1) {

              perror("RTC_RD_TIME ioctl");

              exit(errno);

       }

 

       printf("\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n",

              rtc_tm.tm_year + 1900, rtc_tm.tm_mon + 1,rtc_tm.tm_mday,

              rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);

 

done:

       printf("\n\t *** Test complete ***\n");

       close(fd);

       return 0;

}

 

编译后下载到temp目录下,运行:

[root@zhaocj/temp]#./mydate

 

       RTC Driver Test Example.

 

CurrentRTC date/time is 2000-1-1, 0:20:31.

 

        *** Test complete ***

如果mydate没有带参数,表示只读取RTC时间。

 

[root@zhaocj/temp]#./mydate  2013  7  12  11  29  30

 

       RTC Driver Test Example.

 

CurrentRTC date/time is 2013-7-12, 11:29:30.

 

        *** Test complete ***

如果mydate带有表示年、月、日、时、分、秒的参数,则表示设置RTC时间。

 

下面我们再一段应用RTC报警中断的应用程序:

/***************

***myalm.c***

***************/

 

#include<stdio.h>

#include<linux/rtc.h>

#include<sys/ioctl.h>

#include<sys/time.h>

#include<sys/types.h>

#include<fcntl.h>

#include<unistd.h>

#include<stdlib.h>

#include<errno.h>

 

intmain(int argc, char **argv)

{

       int fd, retval;

       unsigned long data;

       struct rtc_time rtc_tm;

 

       fd = open("/dev/rtc0",O_RDONLY);

 

       if (fd ==  -1) {

              perror("/dev/rtc0");

              exit(errno);

       }

 

       printf("\nt\tRTC Driver TestExample.\n");

 

       retval = ioctl(fd, RTC_RD_TIME,&rtc_tm);               //读取当前时间

       if (retval == -1) {

              perror("RTC_RD_TIMEioctl");

              exit(errno);

       }

 

       printf("\nCurrent RTC date/time is%d-%d-%d, %02d:%02d:%02d.\n",

              rtc_tm.tm_year + 1900,rtc_tm.tm_mon + 1,rtc_tm.tm_mday,

              rtc_tm.tm_hour, rtc_tm.tm_min,rtc_tm.tm_sec);

 

       rtc_tm.tm_sec += 5;            // 设置5秒后报警

       //时间溢出调整

       if (rtc_tm.tm_sec >= 60) {

              rtc_tm.tm_sec %= 60;

              rtc_tm.tm_min++;

       }

       if (rtc_tm.tm_min == 60) {

              rtc_tm.tm_min = 0;

              rtc_tm.tm_hour++;

       }

       if (rtc_tm.tm_hour == 24)

              rtc_tm.tm_hour= 0;

 

       retval = ioctl(fd, RTC_ALM_SET,&rtc_tm);                     //设置RTC报警时间

       if (retval == -1) {

              if (errno == ENOTTY) {

                     fprintf(stderr,"\n...AlarmIRQs not supported.\n");

              }

              perror("RTC_ALM_SETioctl");

              exit(errno);

       }

 

       retval = ioctl(fd, RTC_ALM_READ,&rtc_tm);           //读取报警时间

 

       if (retval == -1) {

              perror("RTC_ALM_READioctl");

              exit(errno);

       }

 

       fprintf(stderr, "Alarm time now setto %02d:%02d:%02d.\n",

              rtc_tm.tm_hour, rtc_tm.tm_min,rtc_tm.tm_sec);

 

       retval = ioctl(fd, RTC_AIE_ON, 0);      //使能RTC报警中断

 

       if (retval == -1) {

              perror("RTC_AIE_ONioctl");

              exit(errno);

       }

 

       fprintf(stderr, "Waiting 5 secondsfor alarm...");

/* This blocks until the alarm ring causes an interrupt */

       retval = read(fd, &data,sizeof(unsigned long));              //等待RTC报警中断的发生

 

       if (retval == -1) {

              perror("read");

              exit(errno);

       }

       fprintf(stderr, " okay. Alarmrang.\n");

 

       retval = ioctl(fd, RTC_AIE_OFF, 0);           //关闭RTC报警

       if (retval == -1) {

              perror("RTC_AIE_OFFioctl");

              exit(errno);

       }

 

       printf("\n\t\t *** Test complete ***\n");

       close(fd);

       return 0;

}

 

[root@zhaocj/temp]#./myalm

 

       RTC Driver Test Example.

 

CurrentRTC date/time is 2013-7-12, 12:48:23.

Alarmtime now set to 12:48:28.

Waiting5 seconds for alarm... okay. Alarm rang.

 

        *** Test complete ***

 

由于系统的RTC报警中断只能是24小时内,因此不能设置RTC报警中断的年、月、日。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值