Linux RTC驱动模型分析
RTC简介
RTC驱动框架
基本数据结构
struct rtc_device
{struct device dev;struct module *owner;int id; //代表是那个rtc设备char name[RTC_DEVICE_NAME_SIZE]; //代表rtc设备的名称const struct rtc_class_ops *ops; //rtc操作函数集,需要驱动实现struct mutex ops_lock; //操作函数集的互斥锁struct cdev char_dev; //代表rtc字符设备,因为rtc就是个字符设备unsigned long flags; //rtc的状态标志,例如RTC_DEV_BUSYunsigned long irq_data; //rtc中断数据spinlock_t irq_lock; //访问数据是要互斥,需要spin_lockwait_queue_head_t irq_queue; //数据查询中用到rtc队列struct fasync_struct *async_queue; //异步队列struct rtc_task *irq_task; //在中断中使用task传输数据spinlock_t irq_task_lock; //task传输互斥int irq_freq; //rtc的中断频率int max_user_freq; //rtc的最大中断频率struct timerqueue_head timerqueue; //定时器队列 struct rtc_timer aie_timer; //aie(alaram interrupt enable)定时器struct rtc_timer uie_rtctimer; //uie(update interrupt enable)定时器struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */ //pie(periodic interrupt enable)定时器int pie_enabled; //pie使能标志struct work_struct irqwork; /* Some hardware can't support UIE mode */int uie_unsupported; //uie使能标志#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL //RTC UIE emulation on dev interface配置项,目前没有开启struct work_struct uie_task;struct timer_list uie_timer;/* Those fields are protected by rtc->irq_lock */unsigned int oldsecs;unsigned int uie_irq_active:1;unsigned int stop_uie_polling:1;unsigned int uie_task_active:1;unsigned int uie_timer_active:1;
#endif
};
这个结构是rtc驱动的核心结构,当驱动程序使用rtc_device_register函数传递正确的参数,然后就返回struct rtc_deivce给驱动程序。而在这个结构中rtc_class_ops函数需要驱动程序实现。
struct rtc_class_ops {int (*open)(struct device *);void (*release)(struct device *);int (*ioctl)(struct device *, unsigned int, unsigned long);int (*read_time)(struct device *, struct rtc_time *);int (*set_time)(struct device *, struct rtc_time *);int (*read_alarm)(struct device *, struct rtc_wkalrm *);int (*set_alarm)(struct device *, struct rtc_wkalrm *);int (*proc)(struct device *, struct seq_file *);int (*set_mmss)(struct device *, unsigned long secs);int (*read_callback)(struct device *, int data);int (*alarm_irq_enable)(struct device *, unsigned int enabled);
};
这些函数中大部分需要驱动程序实现,比如open, read_time, set_time等。这些函数大多数都是和rtc芯片的操作有关。
rtc-dev.c代码分析
void __init rtc_dev_init(void)
{int err;err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");if (err < 0)pr_err("failed to allocate char dev region\n");
}
动态分配一个次设备号为0,相同设备的最大个数为16的字符设备。该函数会在rtc_init函数中被调用。
void rtc_dev_prepare(struct rtc_device *rtc)
{if (!rtc_devt)return;if (rtc->id >= RTC_DEV_MAX) { //合法性判断,如果id大于16个,说明rtc设备个数太多dev_dbg(&rtc->dev, "%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 //UIE模拟配置相关,不做过多介绍INIT_WORK(&rtc->uie_task, rtc_uie_task);setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
#endifcdev_init(&rtc->char_dev, &rtc_dev_fops); //字符设备初始化,以及文件操作函数集合初始化rtc->char_dev.owner = rtc->owner;
}
该函数主要是初始化字符设备,设置rtc相关的file operation函数集合。
void rtc_dev_add_device(struct rtc_device *rtc)
{if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n",rtc->name, MAJOR(rtc_devt), rtc->id);elsedev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", rtc->name,MAJOR(rtc_devt), rtc->id);
}
调用cdev_add函数将rtc字符设备加入到内核中。这样以来rtc字符设备已经加入到系统中,就等待应用程序的调用。应用程序操作之前还需要实现rtc_dev_fops:
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字符设备驱动对应的file operation操作函数集合。接下来一个一个分析。
static int rtc_dev_open(struct inode *inode, struct file *file)
{int err;struct rtc_device *rtc = container_of(inode->i_cdev,struct rtc_device, char_dev);const struct rtc_class_ops *ops = rtc->ops; //获得驱动的rtc opsif (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) //检测rtc是否现在在使用,如果没有使用即可open return -EBUSY;file->private_data = rtc; //将rtc放入到private_data变量中err = ops->open ? ops->open(rtc->dev.parent) : 0; //如果驱动实现open函数,就调用驱动的open,如果没有实现返回0if (err == 0) {spin_lock_irq(&rtc->irq_lock);rtc->irq_data = 0;spin_unlock_irq(&rtc->irq_lock);return 0;}/* something has gone wrong */clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); //离开的时候将rtc设备为不忙return err;
}
/*** test_and_set_bit_lock - Set a bit and return its old value, for lock* @nr: Bit to set* @addr: Address to count from** This operation is atomic and provides acquire barrier semantics.* It can be used to implement bit locks.*/
#define test_and_set_bit_lock(nr, addr) test_and_set_bit(nr, addr)
设置一个bit然后返回以前的值, 用于检测是设备是否在使用。
接下来分析read函数的执行过程。
static ssize_t rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{struct rtc_device *rtc = file->private_data; //从private_data域取出rtc数据,在open中设置的private_dataDECLARE_WAITQUEUE(wait, current); //声明一个等待队列waitunsigned long data;ssize_t ret;if (count != sizeof(unsigned int) && count < sizeof(unsigned long)) return -EINVAL;add_wait_queue(&rtc->irq_queue, &wait); //将等待队列加入到rtc的等待队列do {__set_current_state(TASK_INTERRUPTIBLE); //设置当前进程的状态为可中断类型spin_lock_irq(&rtc->irq_lock);data = rtc->irq_data; //读取irq_date的数据,在中断中有数据的时候会设置irq_date的值rtc->irq_data = 0;spin_unlock_irq(&rtc->irq_lock);if (data != 0) { //data不等于0,说明有数据,跳出while循环ret = 0;break;}if (file->f_flags & O_NONBLOCK) { //如果读取数据是非阻塞的方式,直接返回ret = -EAGAIN;break;}if (signal_pending(current)) { //收到信号中断,退出ret = -ERESTARTSYS;break;}schedule(); //调度出去,睡眠} while (1);set_current_state(TASK_RUNNING); //执行到这里说明是从上述3种情况break出来的,然后将进程状态设置为runningremove_wait_queue(&rtc->irq_queue, &wait); //从等待队列移除waitif (ret == 0) { //ret等于0,说明是rtc中断触发导致退出while循环/* Check for any data updates */if (rtc->ops->read_callback) //驱动程序是否实现read_callback, 一般驱动程序没有实现该回调函数data = rtc->ops->read_callback(rtc->dev.parent,data);if (sizeof(int) != sizeof(long) &&count == sizeof(unsigned int))ret = put_user(data, (unsigned int __user *)buf) ?: //返回出去给用户sizeof(unsigned int);elseret = put_user(data, (unsigned long __user *)buf) ?:sizeof(unsigned long);}return ret;
}
该函数一般可以用来判断是否有rtc中断发生,如果有read读就不会blocked。 而此read不是用来读取具体时间的函数。
接下来分析poll函数。
static unsigned int rtc_dev_poll(struct file *file, poll_table *wait)
{struct rtc_device *rtc = file->private_data;unsigned long data;poll_wait(file, &rtc->irq_queue, wait); //使用poll系统调用,一直等待有数据是否到来data = rtc->irq_data;return (data != 0) ? (POLLIN | POLLRDNORM) : 0; //返回结果(有数据可读|有普通数据可读)
}
接下来分析rtc的重点函数ioctl调用。
static long rtc_dev_ioctl(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;struct rtc_time tm;struct rtc_wkalrm alarm;void __user *uarg = (void __user *) arg; //用户传递的第三个参数err = mutex_lock_interruptible(&rtc->ops_lock); //互斥操作,可以中断if (err)return err;
//以下几个都是合法性检测,检测调用者是否有权限执行操作。switch (cmd) {case RTC_EPOCH_SET:case RTC_SET_TIME:if (!capable(CAP_SYS_TIME))err = -EACCES;break;case RTC_IRQP_SET:if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))err = -EACCES;break;case RTC_PIE_ON:if (rtc->irq_freq > rtc->max_user_freq &&!capable(CAP_SYS_RESOURCE))err = -EACCES;break;}if (err)goto done;switch (cmd) {case RTC_ALM_READ: //读取闹钟时间mutex_unlock(&rtc->ops_lock);err = rtc_read_alarm(rtc, &alarm); //读取闹钟的具体操作if (err < 0)return err;if (copy_to_user(uarg, &alarm.time, sizeof(tm)))err = -EFAULT;return err;case RTC_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 the alarm 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_ON call, 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 "periodic alarm" capabilities.* Not supported here.*/{unsigned long now, then;err = rtc_read_time(rtc, &tm);if (err < 0)return err;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)return err;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;}}return rtc_set_alarm(rtc, &alarm);case RTC_RD_TIME: //读取时间mutex_unlock(&rtc->ops_lock);err = rtc_read_time(rtc, &tm);if (err < 0)return err;if (copy_to_user(uarg, &tm, sizeof(tm)))err = -EFAULT;return err;case RTC_SET_TIME: //设置时间mutex_unlock(&rtc->ops_lock);if (copy_from_user(&tm, uarg, sizeof(tm)))return -EFAULT;return rtc_set_time(rtc, &tm);case RTC_PIE_ON: //Enable the periodic interrupterr = rtc_irq_set_state(rtc, NULL, 1);break;case RTC_PIE_OFF: //Disable the periodic interrupterr = rtc_irq_set_state(rtc, NULL, 0);break;case RTC_AIE_ON: //Enable the alarm interruptmutex_unlock(&rtc->ops_lock);return rtc_alarm_irq_enable(rtc, 1);case RTC_AIE_OFF: //Disable the alarm interruptmutex_unlock(&rtc->ops_lock);return rtc_alarm_irq_enable(rtc, 0);case RTC_UIE_ON: //Enable the interrupt on every clock updatemutex_unlock(&rtc->ops_lock);return rtc_update_irq_enable(rtc, 1);case RTC_UIE_OFF: //Disable the interrupt on every clock updatemutex_unlock(&rtc->ops_lock);return rtc_update_irq_enable(rtc, 0);case RTC_IRQP_SET: //Set IRQ rateerr = rtc_irq_set_freq(rtc, NULL, arg);break;case RTC_IRQP_READ: //Read IRQ rate err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);break;case RTC_WKALM_SET: //Set wakeup alarmmutex_unlock(&rtc->ops_lock);if (copy_from_user(&alarm, uarg, sizeof(alarm)))return -EFAULT;return rtc_set_alarm(rtc, &alarm);case RTC_WKALM_RD: //Get wakeup alarmmutex_unlock(&rtc->ops_lock);err = rtc_read_alarm(rtc, &alarm);if (err < 0)return err;if (copy_to_user(uarg, &alarm, sizeof(alarm)))err = -EFAULT;return err;default: //默认操作,如果驱动不实现上述操作,可以实现自己的命令,然后走这里分支。/* Finally try the driver's ioctl interface */if (ops->ioctl) {err = ops->ioctl(rtc->dev.parent, cmd, arg);if (err == -ENOIOCTLCMD)err = -ENOTTY;} elseerr = -ENOTTY;break;}
done:mutex_unlock(&rtc->ops_lock);return err;
}
以上就是全部ioctl的操作,大多数rtc的功能都在这个函数中的case当中被调用。
接下来是rtc的关闭函数。
static int rtc_dev_release(struct inode *inode, struct file *file)
{struct rtc_device *rtc = file->private_data;/* Keep ioctl until all drivers are converted */rtc_dev_ioctl(file, RTC_UIE_OFF, 0); //关闭rtc的uie中断rtc_update_irq_enable(rtc, 0); //disable rtc中断rtc_irq_set_state(rtc, NULL, 0);if (rtc->ops->release)rtc->ops->release(rtc->dev.parent); //调用驱动的release函数clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); //将rtc的状态设置为空闲,也就是不忙。return 0;
}
以上就是全部的rtc-dev.c的分析。
Linux RTC驱动模型分析相关推荐
- linux RTC 驱动模型分析
linux RTC 驱动模型分析 RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间.RTC因为是电池供电的,所以掉电后时间不丢失.Linux内核把RTC用作&quo ...
- Linux RTC驱动模型分析之rtc-sysfs.c【转】
转自:https://blog.csdn.net/longwang155069/article/details/52353408 版权声明:本文为博主原创文章,未经博主允许不得转载. https:// ...
- linux MISC 驱动模型分析
linux MISC 驱动模型分析 阅读led驱动程序的代码的时候,没有发现ldd3中提到的各种字符设备注册函数,而是发现了一个misc_register函数,这说明led设备是作为杂项设备出现在内核 ...
- LINUX设备驱动模型分析之三 驱动(DRIVER)接口分析
上一章我们分析了bus-driver-device模型中bus接口部分,本章我们将分析driver接口,在bus-driver-device模型中,driver接口是依附于bus上,而不像device ...
- linux platform 驱动模型分析
一. 概述 platform设备和驱动与linux设备模型密切相关.platform在linux设备模型中,其实就是一种虚拟总线没有对应的硬件结构.它的主要作用就是管理系统的外设资源,比如io ...
- linux i2c adapter 增加设备_LINUX设备驱动模型分析之四 设备模块相关(DEVICE)接口分析...
本系列前几篇文章链接如下: <LINUX设备驱动模型分析之一 总体概念说明> <LINUX设备驱动模型分析之二 总线(BUS)接口分析> <LINUX设备驱动模型分析之三 ...
- linux下camera驱动分析_LINUX设备驱动模型分析之三 驱动模块相关(DRIVER)接口分析...
本系列前几篇文章链接如下: <LINUX设备驱动模型分析之一 总体概念说明> <LINUX设备驱动模型分析之二 总线(BUS)接口分析> 上一章我们分析了bus-driver- ...
- linux 内核驱动模型,linux设备驱动模型架构分析 一
linux设备驱动模型架构分析 一 发布时间:2018-07-04 15:14, 浏览次数:584 , 标签: linux 概述 LDD3中说:"Linux内核需要一个对系统结构的一般性描述 ...
- char添加一个字符_LINUX字符设备驱动模型分析(起始篇)
在前面几个模块的介绍中,我们主要以vfs为起始,完成了sysfs.设备-总线-驱动模型.platform设备驱动模型.i2c设备驱动模型.spi设备驱动模型的分析.在对这些模块进行分析的时候,我们或多 ...
- 请教几个Linux设备驱动模型的问题
http://bbs.chinaunix.net/thread-3691002-1-1.html 1.Linux设备驱动模型是如何将bus.device和driver关联起来的?它们之间的数据结构关系 ...
最新文章
- 3.5.3 CSMA协议
- 404 Not Found: Requested route ('jerrylist.cfapps.eu10.hana.ondemand.com') does not exist
- mysql 行列转换 动态_mysql 行列动态转换的实现(列联表,交叉表)
- 演练 类的定义 java 1615134691
- 二叉树——二叉树问题(洛谷 P3884)
- 安卓学习笔记23:常用控件 - 网格视图与图像切换器
- 《深入理解 Spring Cloud 与微服务构建》第十五章 微服务监控 Spring Boot Admin
- 敏友的【敏捷个人】有感(6): 我的改变从执行力分享开始
- Get_HD_Serial() 获得磁盘驱动器序列号
- java分布性_java大型分布系统性能优化实战教程
- WPF自定义动画控件 风机
- 关于embedding的理解,2020-7-30
- java web play_玩转Java Web应用开发:Play框架
- 雷军在北京大学的演讲
- 技术总结--android篇(四)--工具类总结
- 训练赛1_E_Lawnmower
- 如何实现实时音视频聊天功能
- react 使用qs
- (七)集成学习中-投票法Voting
- windows访问共享文件夹 登录失败: 禁用当前的账户 解决方法