Linux RTC设备驱动

1. 在Linux2.6.29内核中,RTC是以平台设备的方式注册进内核的。

① RTC驱动定义于文件:drivers/rtc/rtc-s3c.c

static struct platform_driver s3c2410_rtc_driver = {
    .probe        = s3c_rtc_probe,
    .remove        = __devexit_p(s3c_rtc_remove),
    .suspend    = s3c_rtc_suspend,
    .resume        = s3c_rtc_resume,
    .driver        = {
        .name    = "s3c2410-rtc",
        .owner    = THIS_MODULE,
    },
};

② RTC平台资源定义于:arch/arm/plat-s3c24xx/devs.c

static struct resource s3c_rtc_resource[] = {
    [0] = {
        .start = S3C24XX_PA_RTC,
        .end   = S3C24XX_PA_RTC + 0xff,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_RTC,
        .end   = IRQ_RTC,
        .flags = IORESOURCE_IRQ,
    },
    [2] = {
        .start = IRQ_TICK,
        .end   = IRQ_TICK,
        .flags = IORESOURCE_IRQ
    }
};

struct platform_device s3c_device_rtc = {
    .name          = "s3c2410-rtc",
    .id          = -1,
    .num_resources      = ARRAY_SIZE(s3c_rtc_resource),
    .resource      = s3c_rtc_resource,
};

③ 当RTC设备驱动匹配到名字为“s3c2410-rtc”的设备时,会调用probe函数s3c_rtc_probe()

static int __devinit s3c_rtc_probe(struct platform_device *pdev)
{
    struct rtc_device *rtc;
    struct resource *res;
    int ret;/* find the IRQs */
    s3c_rtc_tickno = platform_get_irq(pdev, 1);
    s3c_rtc_alarmno = platform_get_irq(pdev, 0);
   /* get the memory region */
    s3c_rtc_mem = request_mem_region(res->start,
                     res->end-res->start+1,
                     pdev->name);

    s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);/* check to see if everything is setup correctly */
    
s3c_rtc_enable(pdev,
1); s3c_rtc_setfreq(&pdev->dev, 1); device_init_wakeup(&pdev->dev, 1); /* register RTC and exit */ rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE); rtc->max_user_freq = 128; platform_set_drvdata(pdev, rtc); return 0;

(1) s3c_rtc_probe()函数分析1: 获取平台资源(中断号和IO内存),然后记录中断号,进行IO内存映射。

(2) s3c_rtc_probe()函数分析2: 调用rtc_device_register()函数,分配RTC的设备描述结构rtc_device,并填充其成员,然后进行设备注册(注册字符设备,注册进proc系统,注册进sys系统)

(3) s3c_rtc_probe()函数分析3: 保存RTC设备描述结构到平台设备驱动的dev域。

注: RTC的设备描述结构rtc_device是device结构的扩展,描述一个具体的RTC设备。

⑤ rtc_device_register()函数分析:

struct rtc_device *rtc_device_register(const char *name, struct device *dev,
                    const struct rtc_class_ops *ops,
                    struct module *owner)
{
    struct rtc_device *rtc;
    int id, err;

    if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
        err = -ENOMEM;
        goto exit;
    }

    mutex_lock(&idr_lock);
    err = idr_get_new(&rtc_idr, NULL, &id);
    mutex_unlock(&idr_lock);

    if (err < 0)
        goto exit;

    id = id & MAX_ID_MASK;

    rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
    if (rtc == NULL) {
        err = -ENOMEM;
        goto exit_idr;
    }

    rtc->id = id;
    rtc->ops = ops;
    rtc->owner = owner;
    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);

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

    dev_info(dev, "rtc core: registered %s as %s\n",
            rtc->name, dev_name(&rtc->dev));

    return rtc;

}

(1) 为RTC设备描述结构(rtc_device)分配空间,并填充相关成员。

(2) 通过rtc_dev_prepare()函数填充RTC字符设备的主设备号和操作函数集rtc_dev_fops

void rtc_dev_prepare(struct rtc_device *rtc)
{
    rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);

    cdev_init(&rtc->char_dev, &rtc_dev_fops); rtc->char_dev.owner = rtc->owner; }

(3)通过rtc_dev_add_device()将RTC以字符设备注册进内核

void rtc_dev_add_device(struct rtc_device *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);
}

(4)RTC字符设备操作函数集rtc_dev_fops:用于实现访问RTC设备文件/dev/rtc0的操作方法,包括open、read、ioctl等方法。

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

(5)RTC设备操作函数集s3c_rtcops:用于实现操作RTC设备的基本方法也是rtc_device_register()函数中赋值给rtc(rtc_device结构)的ops成员的

static const struct rtc_class_ops s3c_rtcops = {
    .open        = s3c_rtc_open,
    .release    = s3c_rtc_release,
    .read_time    = s3c_rtc_gettime,
    .set_time    = s3c_rtc_settime,
    .read_alarm    = s3c_rtc_getalarm,
    .set_alarm    = s3c_rtc_setalarm,
    .irq_set_freq    = s3c_rtc_setfreq,
    .irq_set_state    = s3c_rtc_setpie,
    .proc            = s3c_rtc_proc,
};

注:s3c_rtcops属于RTC的设备描述结构rtc_device的成员,用于实现操作RTC的基本方法;而rtc_dev_fops属于cdev结构的成员,用于实现访问RTC设备文件/dev/rtc0的基本方法。rtc_dev_fops中的成员需要调用s3c_rtcops中的成员来实现其具体功能。

2. Linux2.6.29内核中,RTC平台驱动已经注册进内核,但RTC平台资源并未注册。我们添加RTC仅需要把RTC的平台资源结构体添加到平台资源初始化列表中即可。

(1)向smdk2440_devices[]数组中添加s3c_device_rtc

static struct platform_device *smdk2440_devices[] __initdata = {
    &s3c_device_usb,
    &s3c_device_lcd,
    &s3c_device_wdt,
    &s3c_device_i2c0,
    &s3c_device_iis,
    &smdk2440_device_eth,
    &s3c_device_rtc,
};

(2)重新配置内核,加入RTC的驱动:Device Drivers--->Real Time Clock-->Samsung S3C series Soc RTC

3. RTC测试

(1)操作设备文件/dev/rtc0

(2)通过proc查看:cat /proc/driver/rtc

(3)通过sys文件系统接口:cat /sys/class/trc/rtc0/date; cat /sys/class/trc/rtc0/time

#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>

static const char default_rtc[] = "/dev/rtc0";

int main(int argc, char** argv)
{
    int fd, retval;
    struct rtc_time rtc_tm;
    const char* rtc = default_rtc;
    
    switch(argc)
    {
        case 2:
            rtc = argv[1];
            break;
        case 1:
            break;
        default:
            fprintf(stderr, "usage: TestRtc [RtcDev]");
            return 1;
    }
    
    fd = open(rtc, O_RDONLY);
    if(fd == -1)
    {
        perror(rtc);
        exit(errno);
    }
    
    retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);
    if(retval == -1)
    {
        perror("RTC_RD_TIME ioctl");
        exit(errno);
    }
    
    printf("Current 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);
    
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wulei0630/p/10840516.html