RTC Introduction RTC (Real-time clock) abbreviation real-time clock, the main function is to remember when, produce alarm clock and so on. RTC has a backup battery, so even if the computer shuts down, it will not affect the RTC memory. The difference between RTC and system time (mainly by software simulation) is that the RTC will not lose the data after power-down and can still reset the current time to the computer at the next boot. The system time mainly relies on software simulation, after the power loss will be lost, need to re-simulation after the next computer restart. The RTC time is used every time the system is started, and will be written to the RTC at a later time when it is needed, and other times the acquisition time is available through the software. RTC can use periodic interrupts to generate alarms, or it can be used as the wake-up source of the system when the system is suspend. The Linux system provides two RTC interfaces, the/DEV/RTC is for PC machines, the other/dev/rtc0,/DEV/RTC1 supports all systems, and can refer to rtc.txt documentation. Linux designs a set of drive models for the new interface, and if the driver engineer wants to add a driver, simply write the chip-related code and register it in the RTC core layer. The RTC Driver Framework RTC involves the following code: DRIVER/RTC/CLASS.C: This file registers a class RTC with the Linux kernel driver model, and provides the registered/unregistered RTC interface for the underlying RTC driver. RTC-related PM operations are also implemented. DRIVER/RTC/RTC-DEV.C: The abstraction of various RTC devices into a single character device, together with a set of file manipulation functions. DRIVER/RTC/RTC-SYSFS.C: Users can easily and quickly operate the RTC device via SYSFS file system. DRIVER/RTC/RTC-PROC.C: can obtain information about RTC via proc file system, such as Rtc_time, Rtc_data, etc. DRIVER/RTC/INTERFACE.C: provides application and driver interface functions, mainly to provide the relevant calling interface for RTC. DRIVER/RTC/RTC-LIB.C: provides a conversion function between RTC and data and time DRIVER/RTC/HCTOSYS.C: Gets the value of the RTC for boot-up. Driver/RTC/RTC-XXX.C: A wide range of RTC drivers. The RTC Model diagram is as follows: The CLASS.C can be clearly seen to provide a registration interface for various different drivers. The same user can operate the device node/dev/rtc0, or through the SYSFS or proc file system eventually through the interface operation to the actual driver code. RTC-DEV.C is an abstraction for a wide variety of RTC drivers, so the next step is to analyze rtc-dev.c first.
Basic data structures need to understand some of the necessary data structures before parsing the code
1. struct RTC-DEVICE data structure
struct rtc_device{struct device dev;struct module *owner;int ID; The representative is that RTC device char name[rtc_device_name_size]; Represents the name of the RTC device const struct RTC_CLASS_OPS *ops; RTC operation function Set, need to drive implementation of struct mutex ops_lock; The mutual exclusion lock struct Cdev char_dev of the operation function set; Represents the RTC character device, because RTC is a character device unsigned long flags; The status flag of the RTC, for example rtc_dev_busyunsigned long irq_data; RTC Interrupt Data spinlock_t Irq_lock; Access to data is mutually exclusive, need spin_lockwait_queue_head_t irq_queue; The RTC queue struct Fasync_struct *async_queue is used in data query; Asynchronous queue struct Rtc_task *irq_task; Using task to transmit data in interrupts spinlock_t Irq_task_lock; Task transport mutex int irq_freq; RTC Interrupt frequency int max_user_freq; RTC's maximum interrupt frequency sTruct Timerqueue_head Timerqueue; Timer queue struct Rtc_timer aie_timer; AIE (Alaram interrupt enable) timer struct Rtc_timer uie_rtctimer; Uie (update interrupt enable) timer struct Hrtimer pie_timer; /* Sub Second exp, so needs Hrtimer *///pie (periodic interrupt enable) timer int pie_enabled; PIE Enable flag struct work_struct irqwork; /* Some hardware can ' t support UIE mode */int uie_unsupported; UIE Enable flag #ifdef Config_rtc_intf_dev_uie_emul//RTC UIE emulation on DEV interface configuration item, not currently open Struc T work_struct uie_task;struct timer_list uie_timer;/* Those fields is protected by rtc->irq_lock */unsigned int oldsec s;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};
This structure is the core structure of the RTC driver, and when the driver uses the Rtc_device_register function to pass the correct parameters, it returns the struct rtc_deivce to the driver. In this structure, the Rtc_class_ops function requires a driver implementation.
2. struct RTC_CLASS_OPS data structure
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 (*p ROC) (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);};
Most of these functions require driver implementations, such as Open, Read_time, Set_time, and so on. Most of these functions are related to the operation of the RTC chip. RTC-DEV.C Code Analysis RTC-DEV.C is an abstraction of a variety of RTC devices, implements some common functionality, and then registers this abstract RTC device as a character device.
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");}
A character device that dynamically assigns a secondary device number of 0 and a maximum number of 16 for the same device. The function is called in the Rtc_init function. Output parameter device number RTC_DEVT, consisting of the main device number and the secondary device number.
void Rtc_dev_prepare (struct rtc_device *rtc) {if (!rtc_devt) return;if (rtc->id >= rtc_dev_max) { //legality judgment, If the ID is greater than 16, the number of RTC devices is too many 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 analog configuration related, do not do too much introduction 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); Character device initialization, and the set of file manipulation functions Initialize Rtc->char_dev.owner = Rtc->owner;}
This function primarily initializes the character device and sets the RTC-related file Operation function collection.
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);}
Call the Cdev_add function to join the RTC character device to the kernel. Since the RTC character device has been added to the system, it waits for the application to be called. You also need to implement rtc_dev_fops before application operations:
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, };
The above is the RTC character device driver corresponding to the file operation operation function set. The next one is an analysis. When the application opens the/DEV/RTC device, it goes to the open function collection.
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; Get driven RTC Opsif (Test_and_set_bit_lock (rtc_dev_busy, &rtc->flags) //Detect if RTC is now in use, open if not used Return-ebusy;file->private_data = RTC; Put the RTC into the private_data variable err = ops->open? Ops->open (rtc->dev.parent): 0; If the driver implements the open function, it calls the drive open, if no implementation returns 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); Leave the RTC device as not busy return err;}
The above operation is the RTC open operation, how simple, how familiar routines.
/** * Test_and_set_bit_lock-set a bit and return its old value, for lock * @nr: bit to set * @addr: Address to Count fro M * * 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)
Set a bit and then return to the previous value to detect if the device is in use.
Next, the execution of the read function is analyzed.
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; Remove the RTC data from the Private_data domain, set the private_datadeclare_waitqueue (wait, current) in open; Declares a wait queue waitunsigned long data;ssize_t ret;if (count! = sizeof (unsigned int) && count < sizeof (unsigned long) ) Return-einval;add_wait_queue (&rtc->irq_queue, &wait); Add the wait queue to the RTC's wait queue do {__set_current_state (task_interruptible); Sets the current process's status to interruptible type SPIN_LOCK_IRQ (&rtc->irq_lock);d ata = rtc->irq_data; Read the irq_date data, set the value of Irq_date when there is data in the interrupt Rtc->irq_data = 0;SPIN_UNLOCK_IRQ (&rtc->irq_lock); if (data! = 0) { Data is not equal to 0, which indicates that there is information, jump out while loop ret = 0;break;} if (File->f_flags & O_nonblock) {//If read data is non-blocking way, return ret =-eagain;break;} if (signal_pending (current)) { Received signal interruption, exit ret =-erestartsys;break;} Schedule (); Dispatch out, sleep} while (1); set_current_state (task_running); Execution to this point is a break out of the above 3 cases, and then set the process state to Runningremove_wait_queue (&rtc->irq_queue, &wait); Remove Waitif from the wait queue (ret = = 0) {//ret equals 0, which means that the RTC interrupt trigger causes Exit while loop/* Check for an Y data Updates */if (rtc->ops->read_callback)///driver is implemented Read_callback, the generic driver does not implement the callback function dat A = Rtc->ops->read_callback (rtc->dev.parent, data); if (sizeof (int)! = sizeof (long) && count = = S izeof (unsigned int)) ret = Put_user (data, (unsigned int __user *) buf)?://Return to user sizeof (unsigned int); Elseret = Put_use R (data, (unsigned long __user *) buf)?: sizeof (unsigned long);} return ret;}
This function can generally be used to determine if there is an RTC interrupt, and if read reads it, it will not blocked. This read is not a function to read a specific time.
Next, analyze the poll function.
static unsigned int rtc_dev_poll (struct file *file, poll_table *wait) {struct Rtc_device *RTC = File->private_data;unsi gned long data;poll_wait (file, &rtc->irq_queue, wait); Using the poll system call, wait for the data to come on, is it rtc->irq_data;return (data! = 0)? (Pollin | Pollrdnorm): 0; Return results (with data readable | With normal data readable)}
Next, the key function of the RTC is analyzed by an IOCTL call.
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; The third parameter passed by the user is Err = mutex_lock_interruptible (&rtc->ops_lock); The mutex operation can be interrupted if (err) return err;
The following are legality detections that detect whether the caller has permission to perform the operation. 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://Read alarm time Mutex_unlock (& Amp;rtc->ops_lock); err = Rtc_read_alarm (RTC, &alarm); Read the specific action of the alarm if (Err < 0) return Err;if (Copy_to_user (Uarg, &alarm.time, sizeof)) Err =-efault;return Err;case RT C_alm_set://Set alarm time Mutex_unlock (&rtc->ops_lock); if (Copy_from_u Ser (&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 is up-to-hours in the future. * Rather than expecting every RTC to implement "don ' t Care" * for day/month/year fields, just force the alarm to has * th E Right values for those fields. * * Rtc_wkalm_set should be used instead. Does it * eliminate the need for a separate rtc_aie_on call, it * doesn ' t has the "alarm 23:59:59 in the" Race. * * Note:some Legacy code may has 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 + $ *, &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://Read 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://Set time Mutex_unlock (&rtc-> ; ops_lock); if (Copy_from_user (&tm, Uarg, sizeof)) 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 Interruptmute X_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 RT C_UIE_ON://enable the interrupt on every clock Updatemutex_unlock (&r Tc->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); Bre Ak;case rtc_irqp_read://read IRQ rate err = Put_user (rtc->irq_fre Q, (unsigned long __user *) uarg); Break;case rtc_wkalm_set://set wakeu P 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 =-E Fault;return err;default://default action, if the driver does not implement the above operation, you can implement your own command, and then go Branch here. /* Finally Try the driver ' s IOCTL interface */if (OPS->IOCTL) {err = Ops->ioctl (rtc->dev.parent, CMD, arg); if (er r = =-enoioctlcmd) err =-enotty;} Elseerr =-enotty;break;} Done:mutex_unlock (&rtc->ops_lock); return err;}
These are all IOCTL operations, and most RTC functions are called in the case of this function.
Next is the RTC shutdown function.
static int rtc_dev_release (struct inode *inode, struct file *file) {struct Rtc_device *RTC = file->private_data;/* Keep IOCTL until all drivers is converted */rtc_dev_ioctl (file, Rtc_uie_off, 0); Turn off RTC's Uie Interrupt rtc_update_irq_enable (RTC, 0); Disable RTC Interrupt rtc_irq_set_state (RTC, NULL, 0); if (rtc->ops->release) rtc->ops->release (rtc-> dev.parent); Call the driver's release function Clear_bit_unlock (Rtc_dev_busy, &rtc->flags); Set the status of the RTC to idle, that is, not busy. return 0;}
The above is the analysis of all rtc-dev.c.
Linux RTC Driver Model Analysis