九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 4973|回复: 1
打印 上一主题 下一主题

x4412&ibox项目实战70-RTC驱动实验

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-22 15:07:14 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
linux内核的RTC驱动都保存在kernel/drivers/rtc目录下,相关源码如下:
  1. kernel/drivers/rtc/alarm.c
  2. kernel/drivers/rtc/alarm-dev.c
  3. kernel/drivers/rtc/class.c
  4. kernel/drivers/rtc/interface.c
  5. kernel/drivers/rtc/rtc-dev.c
  6. kernel/drivers/rtc/rtc-lib.c
  7. kernel/drivers/rtc/rtc-proc.c
  8. kernel/drivers/rtc/rtc-s3c.c
  9. kernel/drivers/rtc/rtc-sysfs.c
  10. kernel/drivers/rtc/rtc-core.h
  11. kernel/arch/arm/plat-samsung/dev-rtc.c
复制代码
       kernel/drivers/rtc下集成了linuxRTC驱动模型,当我们添加新的平台的RTC驱动时,只需要在此模型的基础上增加与平台相关的驱动即可。默认该目录下有大量平台相关的驱动源码可参考。针对三星平台的驱动源码为rtc-s3c.c
       kernel/drivers/rtc/Makefile文件部分内容如下:
  1. obj-$(CONFIG_RTC_LIB)            += rtc-lib.o
  2. obj-$(CONFIG_RTC_HCTOSYS)         += hctosys.o
  3. obj-$(CONFIG_RTC_CLASS)               += rtc-core.o
  4. rtc-core-y                      := class.o interface.o

  5. obj-$(CONFIG_RTC_INTF_ALARM) += alarm.o
  6. obj-$(CONFIG_RTC_INTF_ALARM_DEV) += alarm-dev.o
  7. rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
  8. rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
  9. rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o

  10. # Keep the list ordered.
  11. ……
  12. obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
  13. ……
复制代码
       从这里可以看出rtc驱动相关的源码文件。rtc-lib.c中封装了一些时钟转换的函数,如每月有多少天,每年有多少天,秒转化为时钟等,这些函数供整个RTC驱动模型调用,不限平台。在rtc-core.h中声明了一些必要的函数,供驱动调用。interface.c中封装了一些接口函数,如读取时钟,设置时钟,读取闹铃,设置闹铃等。alarm.calarm-dev.c文件封装了具体的闹铃操作方法。rtc-proc.c文件用于给rtc驱动增加proc文件系统的功能,rtc-sysfs.c文件用于给rtc驱动增加sysfs的功能,class.crtc-dev.crtc-s3c.c文件构成了RTC驱动的主体,它实现了RTC字符设备驱动,对外封装了IOCTL接口。如果把RTC驱动比作是一辆汽车,rtc-lib.crtc-core.hinterface.crtc-proc.crtc-sysfs.c就相当于汽车的壳子,而class.crtc-dev.c以及rtc-s3c.c就相当于汽车的发动机。下面我们就从class.c开始,贯穿整个RTC驱动。
       class.c的初始化函数rtc_init中,通过class_create函数注册了一个类,通过rtc_dev_init函数注册一个字符设备驱动,rtc_sysfs_init函数用于支持sysfs属性。
  1. static int __init rtc_init(void)
  2. {
  3.          rtc_class = class_create(THIS_MODULE, "rtc");//注册一个类
  4.          if (IS_ERR(rtc_class)) {
  5.                    printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
  6.                    return PTR_ERR(rtc_class);
  7.          }
  8.          rtc_class->suspend = rtc_suspend;
  9.          rtc_class->resume = rtc_resume;
  10.          rtc_dev_init();//给字符设备分配设备号
  11.          rtc_sysfs_init(rtc_class);//支持sysfs属性
  12.          return 0;
  13. }
复制代码
       rtc_dev_init函数原型如下:
  1. void __init rtc_dev_init(void)
  2. {
  3.          int err;

  4.          err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
  5.          if (err < 0)
  6.                    printk(KERN_ERR "%s: failed to allocate char dev region\n",
  7.                             __FILE__);
  8. }
复制代码
       它仅仅是调用alloc_chrdev_region函数,用于自动分配设备号,将将设备号保存到全局变量rtc_devt中。
       rtc_sysfs_init函数原型如下:
  1. void __init rtc_sysfs_init(struct class *rtc_class)
  2. {
  3.          rtc_class->dev_attrs = rtc_attrs;
  4. }
复制代码
       这里只是给注册的类rtc_class的成员dev_attrs赋值。到此,class.c的初始化函数也就完成了,程序中还有一个关键的返回rtc_device结构体的函数rtc_device_register,它将会被其他程序调用,字符设备驱动就是在这里注册的。
    在kernel/arch/arm/plat-samsung/dev-rtc.c中定义了一个平台设备s3c_device_rtc,同时为这个设备分配了一些资源:
  1. static struct resource s3c_rtc_resource[] = {
  2.          [0] = {
  3.                    .start  = S3C_PA_RTC,
  4.                    .end   = S3C_PA_RTC + 0xff,
  5.                    .flags = IORESOURCE_MEM,
  6.          },
  7.          [1] = {
  8.                    .start  = IRQ_RTC_ALARM,
  9.                    .end   = IRQ_RTC_ALARM,
  10.                    .flags = IORESOURCE_IRQ,
  11.          },
  12.          [2] = {
  13.                    .start  = IRQ_RTC_TIC,
  14.                    .end   = IRQ_RTC_TIC,
  15.                    .flags = IORESOURCE_IRQ
  16.          }
  17. };

  18. struct platform_device s3c_device_rtc = {
  19.          .name                   = "s3c64xx-rtc",
  20.          .id               = -1,
  21.          .num_resources   = ARRAY_SIZE(s3c_rtc_resource),
  22.          .resource    = s3c_rtc_resource,
  23. };
复制代码
       kernel/arch/arm/mach-exynos/mach-x4412.c中,在平台设备的结构体指针smdk4x12_devices中包含了s3c_device_rtc,通过platform_add_devices函数会将包含s3c_device_rtc在内的一长串平台设备添加进内核。
       rtc-s3c.c的初始化函数s3c_rtc_init中,通过platform_driver_register函数将名为s3c-rtc的平台驱动:
  1. static struct platform_device_id s3c_rtc_driver_ids[] = {
  2.          {
  3.                    .name                   = "s3c2410-rtc",
  4.                    .driver_data         = TYPE_S3C2410,
  5.          }, {
  6.                    .name                   = "s3c64xx-rtc",
  7.                    .driver_data         = TYPE_S3C64XX,
  8.          },
  9.          { }
  10. };
  11. MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids);
  12. static struct platform_driver s3c_rtc_driver = {
  13.          .probe                  = s3c_rtc_probe,
  14.          .remove               = __devexit_p(s3c_rtc_remove),
  15.          .suspend     = s3c_rtc_suspend,
  16.          .resume                = s3c_rtc_resume,
  17.          .id_table     = s3c_rtc_driver_ids,
  18.          .driver                  = {
  19.                    .name         = "s3c-rtc",
  20.                    .owner        = THIS_MODULE,
  21.          },
  22. };
复制代码
       很明显,平台驱动结构体s3c_rtc_driver中的驱动名称为s3c-rtc,而在平台设备结构体s3c_device_rtc中的设备名称却为s3c64xx-rtc,二者并不相同,那么探测函数s3c_rtc_probe是如何执行的呢?
       事实上,platform_deviceplatform_driver有两种匹配方式。在kernel/drivers/base/platform.c中,匹配函数原型如下:
  1. static int platform_match(struct device *dev, struct device_driver *drv)
  2. {
  3.          struct platform_device *pdev = to_platform_device(dev);
  4.          struct platform_driver *pdrv = to_platform_driver(drv);

  5.          /* Attempt an OF style match first */
  6.          if (of_driver_match_device(dev, drv))
  7.                    return 1;

  8.          /* Then try to match against the id table */
  9.          if (pdrv->id_table)
  10.                    return platform_match_id(pdrv->id_table, pdev) != NULL;

  11.          /* fall-back to driver name match */
  12.          return (strcmp(pdev->name, drv->name) == 0);
  13. }
复制代码
       该函数的作用就是将平台设备绑定到平台驱动,绑定成功后,平台驱动的probe函数将会执行。可以看到,程序首先判断平台驱动结构体是否存在id_table结构成员,如果存在则比较id_table中的成员名称,如果不存在,再比较platform_driver中的成员名称。
       回到rtc-s3c.c文件,由于在platform_driver中已经存在id_tableplatform_match函数会调用platform_match_id将平台设备的名称s3c64xx-rtc与结构体数组s3c_rtc_driver_ids中的成员名称比较,如果找到相同的名称,会将匹配上的platform_device_id保存到platform_device结构的id_entry中,在probe的时候就可以通过id_entry中的driver_data判断匹配的具体是哪一组ID
       在探测函数s3c_rtc_probe中,通过platform_get_irq从平台设备中读取中断号,通过platform_get_resource函数从平台设备中获取IO资源,request_mem_region函数用于给IO资源申请内存空间。得到IO资源信息后,通过ioremap函数将内存映像到虚拟地址,供后续寄存器操作。clk_get函数用于获取RTC时钟的CLK结构体,clk_enable函数用于使能RTC时钟,s3c_rtc_enable函数最终打开RTCRTC开始工作。到这里,就可以获取RTC时钟信息了。
       s3c_rtc_gettime函数用于读取时钟信息,并保存到rtc_tm结构体中。
紧接着调用rtc_device_register函数,该函数将完成rtc字符设备的注册,节点的生成。在rtc_device_register函数的开始定义了一个指向rtc_device结构体的指针rtc,并对其成员进行了初始化。在rtc_dev_prepare函数中,调用cdev_init函数初始化一个字符设备:
  1. void rtc_dev_prepare(struct rtc_device *rtc)
  2. {
  3.          if (!rtc_devt)
  4.                    return;

  5.          if (rtc->id >= RTC_DEV_MAX) {
  6.                    pr_debug("%s: too many RTC devices\n", rtc->name);
  7.                    return;
  8.          }

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

  10. #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
  11.          INIT_WORK(&rtc->uie_task, rtc_uie_task);
  12.          setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
  13. #endif

  14.          cdev_init(&rtc->char_dev, &rtc_dev_fops);
  15.          rtc->char_dev.owner = rtc->owner;
  16. }
复制代码
       rtc_device_register中,紧接着调用device_register函数,用于在/dev下创建设备节点。在class.c的初始化函数中,通过class_create函数注册了一个类,但是程序并没有调用device_create函数创建设备节点。在这里,device_register函数充当了device_create的功能。
       紧接着rtc_dev_add_device函数调用cdev_add函数完成字符设备的注册:
  1. void rtc_dev_add_device(struct rtc_device *rtc)
  2. {
  3.          if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))
  4.                    printk(KERN_WARNING "%s: failed to add char device %d:%d\n",
  5.                             rtc->name, MAJOR(rtc_devt), rtc->id);
  6.          else
  7.                    pr_debug("%s: dev (%d:%d)\n", rtc->name,
  8.                             MAJOR(rtc_devt), rtc->id);
  9. }
复制代码
       执行到这里,字符设备驱动已经注册成功,在/dev下会产生rtc0节点。rtc_sysfs_add_device函数用于支持sysfs,在/sys/devices/platform目录下将会产生关于rtc的相关目录和文件。rtc_proc_add_device函数用于支持proc,在/proc目录下将会产生关于rtc的相关文件。
       执行完rtc_device_register函数后,会返回rtc结构体,在探测函数s3c_rtc_probe中,通过platform_set_drvdata函数将前面返回的结构体rtc保存到pdev的结构成员中。request_irq函数用于申请RTC闹钟中断,probe函数到此结束。
       rtc-dev.c中,cdev_init函数指定了file_operations结构体rtc_dev_fops
  1. static const struct file_operations rtc_dev_fops = {
  2.          .owner                 = THIS_MODULE,
  3.          .llseek                  = no_llseek,
  4.          .read           = rtc_dev_read,
  5.          .poll            = rtc_dev_poll,
  6.          .unlocked_ioctl    = rtc_dev_ioctl,
  7.          .open          = rtc_dev_open,
  8.          .release       = rtc_dev_release,
  9.          .fasync                 = rtc_dev_fasync,
  10. };
复制代码
       这些成员函数构成了和应用交互数据的桥梁。

回复

使用道具 举报

沙发
发表于 2014-11-6 09:44:34 | 只看该作者
看着不错,顶一个
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-9-28 16:16 , Processed in 0.021326 second(s), 16 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表