title: RTC子系统

tags: linux

date: 2019/1/2 17:15:27

toc: true

RTC子系统

引入

hctosys.c

查看下内核打印的错误信息如下,很明确指定了程序的入口了

drivers/rtc/hctosys.c: unable to open rtc device (rtc0)

程序流程如下:

// 这个也就是定义了段属性,内核在启动的时候会调用的
late_initcall(rtc_hctosys);
#define late_initcall(fn) __define_initcall("7",fn,7) static int __init rtc_hctosys(void)
{
int err;
struct rtc_time tm;
struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); if (rtc == NULL) {
printk("%s: unable to open rtc device (%s)\n",
__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
return -ENODEV;
}
....
}

interface.c

搜索下为什么打不开设备rtc_class_open,可以发现是寻找全局变量rtc_class总线的设备

struct rtc_device *rtc_class_open(char *name)
{
struct device *dev;
struct rtc_device *rtc = NULL; down(&rtc_class->sem);
list_for_each_entry(dev, &rtc_class->devices, node) {
....
}

可以看到在drivers\rtc\interface.c中包含了RTC的打开,设置事件设置闹钟等

class.c

搜索这个全局的链表,可以看到在drivers\rtc\class.c中注册的

struct class *rtc_class;
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
const struct rtc_class_ops *ops,
struct module *owner)
static int __init rtc_init(void)
subsys_initcall(rtc_init); #define subsys_initcall(fn) __define_initcall("4",fn,4) #define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __attribute_used__ \
__attribute__((__section__(".initcall" level ".init"))) = fn

subsys_initcall其实就是定义一个段属性,内核会主动调用的,查看该文件的入口

static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc");
if (IS_ERR(rtc_class)) {
printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
return PTR_ERR(rtc_class);
}
rtc_class->suspend = rtc_suspend;
rtc_class->resume = rtc_resume;
rtc_dev_init();
rtc_sysfs_init(rtc_class);
return 0;
}

小结

  • hctosys.c中的入口rtc_hctosys是使用__define_initcall("7",fn,7)

  • class.c的入口rtc_init是使用__define_initcall("4",fn,4)

  • 内核按照先后顺序调用,内核的链接脚本如下

      __initcall_start = .;
    *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
    __initcall_end = .;
  • 搜索这个链接脚本,可以看到

    extern initcall_t __initcall_start[], __initcall_end[];
    static void __init do_initcalls(void)
    {
    for (call = __initcall_start; call < __initcall_end; call++) {
    ...
    }
  • 也就是按找序号调用了,先执行rtc_init,再去打开,想想也是这样的

流程一览

框架分析

rtc_init

drivers\rtc\class.c这是内核入口初始化注册设备,rtc_init()->rtc_dev_init(),来注册字符设备

  • 创建类
  • 分配主次设备号
static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc"); rtc_class->suspend = rtc_suspend;
rtc_class->resume = rtc_resume;
rtc_dev_init();
#define RTC_DEV_MAX 16
alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
rtc_sysfs_init(rtc_class);
return 0;
}

rtc_device_register

搜索这个函数,是在drivers\rtc\rtc-s3c.c调用的

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

具体形式如下:

  • cdev_init > cdev_add 注册字符设备驱动
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
const struct rtc_class_ops *ops,
struct module *owner)
{
> 设置rtc结构体
rtc->id = id;
rtc->ops = ops;
rtc->owner = owner;
rtc->max_user_freq = 64;
rtc->dev.parent = dev;
rtc->dev.class = rtc_class; //这个是全局的,内核刚开始就注册了的在rtc_init
rtc->dev.release = rtc_device_release; //这里是总线bus 后面会有 device_add 会来匹配,如果没有对应的bus匹配
snprintf(rtc->dev.bus_id, BUS_ID_SIZE, "rtc%d", id);
//>dev->bus->probe
//drv->probe //没有dev->bus->probe执行 rtc_dev_prepare(rtc);
rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
cdev_init(&rtc->char_dev, &rtc_dev_fops);
rtc_dev_add_device(rtc);
cdev_add(&rtc->char_dev, rtc->dev.devt, 1)
// vfs 相关 sysfs 和 proc
rtc_sysfs_add_device
rtc_proc_add_device(rtc); }

s3c_rtc_probe

简述

  • 获得硬件资源,设置寄存器

  • 设置rtc_device结构体,包含了实际的硬件操作s3c_rtcops

    static const struct rtc_class_ops s3c_rtcops = {
    .open = s3c_rtc_open,
    .release = s3c_rtc_release,
    .ioctl = s3c_rtc_ioctl,
    .read_time = s3c_rtc_gettime,
    .set_time = s3c_rtc_settime,
    .read_alarm = s3c_rtc_getalarm,
    .set_alarm = s3c_rtc_setalarm,
    .proc = s3c_rtc_proc,
    };
  • 注册字符设备驱动,操作函数为rtc_dev_fops,通用接口,最终应该会使用具体的硬件操作接口

    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;
    rtc->ops = ops;
    rtc_dev_prepare(rtc);
    >cdev_init(&rtc->char_dev, &rtc_dev_fops); } //drivers\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,
    .ioctl = rtc_dev_ioctl,
    .open = rtc_dev_open,
    .release = rtc_dev_release,
    .fasync = rtc_dev_fasync,
    };

详细

s3c_rtc_probe会调用rtc_device_register来注册rtc设备,这是一个platform平台了,资源文件如下

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

搜索资源文件名找到相应设备文件arch\arm\plat-s3c24xx\devs.c 包含了寄存器地址和中断号

struct platform_device s3c_device_rtc = {
.name = "s3c2410-rtc",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
.resource = s3c_rtc_resource,
}; 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
}
};

具体的函数流程如下:

s3c_rtc_probe
//获得中断号 RQ_TICK节拍和RTC闹钟 ,寄存器等资源
s3c_rtc_tickno = platform_get_irq(pdev, 1);
s3c_rtc_alarmno = platform_get_irq(pdev, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//内存分配 寄存器映射
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); //使能RTC,设置寄存器
s3c_rtc_enable(pdev, 1);
//读取RTC寄存器
pr_debug("s3c2410_rtc: RTCCON=%02x\n",readb(s3c_rtc_base + S3C2410_RTCCON));
//设置频率,设置TICONT寄存器,使能节拍中断,设置节拍计数值
s3c_rtc_setfreq(s3c_rtc_freq); //注册驱动
rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);
> 设置rtc结构体
rtc->id = id;
rtc->ops = ops;
rtc->owner = owner;
rtc->max_user_freq = 64;
rtc->dev.parent = dev;
rtc->dev.class = rtc_class; //这个是全局的,内核刚开始就注册了的在rtc_init
rtc->dev.release = rtc_device_release; //初始化cdev结构体,绑定file_operations
rtc_dev_prepare(rtc);
//这个应该是唤醒队列了
>init_waitqueue_head(&rtc->irq_queue);
>cdev_init(&rtc->char_dev, &rtc_dev_fops);
// 添加到内核,这里好像是udev机制
device_register(&rtc->dev); //添加到驱动设备
rtc_dev_add_device(rtc);
> cdev_add
// /sysfs/文件
rtc_sysfs_add_device(rtc);
> device_create_file(&rtc->dev, &dev_attr_wakealarm);
//创建 /proc/下的一些东西
rtc_proc_add_device(rtc);
>create_proc_entry("driver/rtc", 0, NULL)
>ent->proc_fops = &rtc_proc_fops;

open

公用

我们注册驱动的时候是注册了rtc_dev_fops,它是一个公共的,具体如何找到实际硬件的open?

static const struct file_operations rtc_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = rtc_dev_read,
.poll = rtc_dev_poll,
.ioctl = rtc_dev_ioctl,
.open = rtc_dev_open,
.release = rtc_dev_release,
.fasync = rtc_dev_fasync,
}; static int rtc_dev_open(struct inode *inode, struct file *file)
{
int err;
//获取对应的rtc_device
struct rtc_device *rtc = container_of(inode->i_cdev,
struct rtc_device, char_dev);
//这里就能得到实际的ops了
const struct rtc_class_ops *ops = rtc->ops; //这里就调用实际的open
err = ops->open ? ops->open(rtc->dev.parent) : 0;
}

芯片级

申请了闹钟中断和tick中断

static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release = s3c_rtc_release,
.ioctl = s3c_rtc_ioctl,
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
.proc = s3c_rtc_proc,
}; static int s3c_rtc_open(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
int ret; ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev); if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
return ret;
} ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev); if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
goto tick_err;
} return ret; tick_err:
free_irq(s3c_rtc_alarmno, rtc_dev);
return ret;
}

ioctl

同样的,公共级别的ioctl也会调用到芯片级的

static int rtc_dev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
struct rtc_device *rtc = file->private_data;
const struct rtc_class_ops *ops = rtc->ops;
if (ops->ioctl) {
err = ops->ioctl(rtc->dev.parent, cmd, arg);
...
}

芯片级

这里也就是读写时间,操作寄存器了

static int s3c_rtc_ioctl(struct device *dev,
unsigned int cmd, unsigned long arg)
{
unsigned int ret = -ENOIOCTLCMD; switch (cmd) {
case RTC_AIE_OFF:
case RTC_AIE_ON:
s3c_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0);
ret = 0;
break; case RTC_PIE_OFF:
case RTC_PIE_ON:
tick_count = 0;
s3c_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0);
ret = 0;
break; case RTC_IRQP_READ:
ret = put_user(s3c_rtc_freq, (unsigned long __user *)arg);
break; case RTC_IRQP_SET:
/* check for power of 2 */ if ((arg & (arg-1)) != 0 || arg < 1) {
ret = -EINVAL;
goto exit;
} pr_debug("s3c2410_rtc: setting frequency %ld\n", arg); s3c_rtc_setfreq(arg);
ret = 0;
break; case RTC_UIE_ON:
case RTC_UIE_OFF:
ret = -EINVAL;
} exit:
return ret;
}

加入时钟

可以看到内核是有驱动的,只是没有注册平台设备文件 ,添加这个设备文件

ls /dev/rtc*

arch/arm/plat-s3c24xx/Common-smdk.c加入s3c_device_rtc这个结构即可

static struct platform_device __initdata *smdk_devs[] = {
&s3c_device_nand,
.....
&s3c_device_rtc,
....
};

测试

上电启动信息如下

s3c2410-rtc s3c2410-rtc: setting the system clock to 2165-10-04 10:44:26 (1882584970)

查看下设备

# ls /dev/rtc*
/dev/rtc0

date命令

使用date命令读时间

# 查看时间
# date
Tue Aug 28 04:17:30 UTC 2029
# date "+ %Y/%m/%d %H:%M:%S"
2029/08/28 04:18:06

使用date设置时间,格式是date 月日时分年.秒

# date 010314322019.30
Thu Jan 3 14:32:30 UTC 2019 # date "+ %Y/%m/%d %H:%M:%S"
2019/01/03 14:33:04
# date "+ %Y/%m/%d %H:%M:%S"
2019/01/03 14:33:10
# date "+ %Y/%m/%d %H:%M:%S"
2019/01/03 14:33:11

hwclock命令

  -r, --show          读取并打印硬件时钟(read hardware clock and print result )
-s, --hctosys 将硬件时钟同步到系统时钟(set the system time from the hardware clock )
-w, --systohc 将系统时钟同步到硬件时钟(set the hardware clock to the current system time )

使用如下

#读取硬件时钟
# hwclock -r
Wed Dec 31 23:59:59 1969 0.000000 seconds
# 同步,设置软时钟到硬件时钟
# hwclock -w
# hwclock -r
Thu Jan 3 14:35:49 2019 0.000000 seconds

最新文章

  1. NSTimer整理总结
  2. C#字符串的四舍五入
  3. 一次有趣的XSS漏洞挖掘分析(1)
  4. 自定义头文件 No such file or directory
  5. python3 写文件
  6. rop框架签名功能控制
  7. linux查看历史命令history
  8. erMaster插件
  9. linux 子系统折腾记 (三)
  10. 记一次Java调优案例分析
  11. 排序——选择排序(java描述)
  12. Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove &#39;readOnly&#39; marker from transaction definition.
  13. docker从私有镜像库pull/push镜像问题:Error response from daemon: Get https://xxxx.com/: x509: certificate signed by unknown authority
  14. 4-计算九位数以内各个位数字和为s的种类
  15. By.cssSelector定位元素一个不足发现
  16. java项目 远程debug
  17. Codeforces 160 D. Edges in MST
  18. Modsecurity原理分析--从防御方面谈WAF的绕过(一)
  19. Ubuntu14.04下使用PPA安装php5.6,php7
  20. 【Android开发】范例1-绘制Android的机器人

热门文章

  1. JavaScript(三)数据类型转换
  2. Windows server 2012 R2 部署WSUS补丁服务
  3. 安装mysql的踩坑之旅
  4. 基本数据对象(int,float,str)
  5. Django框架【form组件】
  6. supervisor管理php-fpm
  7. WPFの命中测试
  8. yum工作原理
  9. Sqlserver查询死锁及杀死死锁的方法
  10. 遍历CheckBox根据指定条件做筛选js