Hasen Linux device Driver Development learning journey--clock

Source: Internet
Author: User

/** * Author:hasen * Reference: Linux device Driver Development Details * Introduction: Android Small rookie Linux *          device Driver Development Learning Journey * Theme: Clock * date:2014-11-15 * *
First, the kernel timer
1. Kernel Timer programming
The software sense of the timer is ultimately dependent on the hardware timer to be implemented, the kernel after the clock interrupt occurs to detect whether each timer expires,
The timer handler function after expiration is performed as a soft interrupt in the bottom half. Essentially, the clock interrupt handler evokes TIMER_SOFTIRQ
Soft interrupts, running all timers that expire on the current processor.
In Linux device driver programming, you can use a set of functions and data structures provided in the kernel to complete a timed trigger or to complete a cycle
Sexual affairs. This set of functions and data allows the driver engineer to take care of the kernel and hardware that the specific software timer corresponds to in most cases
Behavior.
The data structures and functions provided by the Linux kernel for manipulating timers are as follows:
(1) timer_list
In the Linux kernel, an instance of the Timer_list struct corresponds to a timer.
struct Timer_list {struct List_head entry;/* Timer list */unsigned long expires;/* Timer expiry time (jiffies) */void (*function) ( unsigned long);/* Timer handler function */unsigned Long data;/* Input timer handler function as parameter */struct timer_base_s base;}
The following defines a my_timer timer
struct Timer_lsit my_timer;
(2) Initialize timer
void Init_timer (struct timer_list *timer);
The Init_timer () function above initializes the next null for the Timer_list entry and assigns a value to the base pointer.
The Timer_initializer (_FUNCTION,_EXPIRES,_DATA) macro is used to assign a timer structure to function, expires, data,
Base these members, the definition of this macro is:
The Define_timer (_NAME,_FUNCTION,_ESPIRES,_DATA) macro is a "shortcut" that defines and initializes the timer member. This one
The macro is defined as follows:
In addition, the Setup_timer () function can also be used to initialize the timer and assign values to its members, with the following code:
static inline void Setup_timer (struct timer_list *timer,void (*function) (unsigned long), unsigned Long data) {     timer- >function = function;     Timer->data = data;     Init_timer (timer);}
(3) Add timer
void Add_timer (struct timer_list *timer);
The above function is used to register the kernel timer and connect the timer to the kernel dynamic timer list.
(4) Delete timer
int Del_timer (struct timer_lsit *timer);
The above functions are used to remove timers.
Del_timer_sync () is a synchronous version of Del_timer () that waits for it to be processed when a timer is deleted, so the call to the function does not
Can occur in the interrupt context.
(5) Modify the expire of the timer
int Mod_timer (struct timer_list *timer,unsigned long expires);
The above function is used to modify the timer expiration time, and the timer function will not be executed until the new incoming expires arrives.

Example: Kernel timer using templates

/*XXX Equipment Structure Body */struct xx_dev{    struct Cdev cdev;    ...    Timer_lsit xxx_timer;/* Device to use the timer *     /}/*xxx driver in the module function */xxx_func1 (...) {struct Xxx_dev *dev = filp->private_data, .../* Initialize timer */init_timer (&dev->xxx_timer);d ev->xxx_ Timer.function = &xxx_do_timer;d ev->xxx_timer.data = (unsigned long) dev;/* device struct body pointer as timer handler function parameter */dev->xxx_ Timer.expires = jiffies + delay;/* Add (Register) timer */add_timer (&dev->xxx_timer); A function */xxx_func2 (...) in the/*xxx driver. {.../* Delete timer */del_timer (&dev->xxx_timer); ...} /* Timer handler function */static void Xxx_do_timer (unsigned long arg) {struct Xxx_device *dev = (struct xxx_device*) (ARG) .../* Scheduler timer re-execution */dev->xxx_timer.expires = jiffies + delay; Add_timer (&dev->xxx_timer); ...}

The expiry time of the timer is often based on the current jiffies to add a delay, if it is Hz, it means a delay of 1 seconds.
Timer handler function, after doing the corresponding work, will often delay expires and add the timer to the core timer list again
So that the timer can be triggered again.
2, the kernel of the delayed work Delayed_work
Note that for periodic tasks, Linux also provides a set of packaged quick mechanisms, essentially leveraging the work queue and timer
Now, this set of mechanisms is the definition of delayed_work,delayed_work structure as follows:
struct delayed_work{    struct work_struct work;    struct timer_list timer;}; struct Work_struct {    atimic_long_t data; #define Work_struct_pending 0#define work_struct_flag_mask (3UL) #define Work_struct_wq_data_mask (~work_struct_flag_mask)    STRUCT list_head entry;    work_func_t func; #ifdef config_lockdep    struct lockdep_map lockdep_map; #endif};
We can dispatch a delayed_work with the following function to execute after the specified delay.
int schedule_delayed_work (struct delayed_work *work,unsigned long delay);
The work_func_t type member func () of the work member in the Delayed_work struct is executed when the specified delay arrives.
The work_func_t type is defined as:
typedef void (*work_func_t) (struct work_sturct *work);
The unit of the delay parameter is jiffies, so a common and usage is as follows:
Schedule_delayed_work (&work,msecs_to_jiffies (poll_interval));
The Msecs_to_jiffies () is used to convert milliseconds to jiffies.
If you want to perform a task periodically, you will typically call Schedule_delayed_work () again in the Delayed_work () function, ZHOUERFU
Began.
The following function is used to cancel the delayed_work:
int cancel_delayed_work (struct delayed_work *work); int Cancel_delayed_work_sync (struct delayed_work *work);
instance: Seconds character device
The following is a driver for the character device "second" (that is, "seconds"), which initializes a timer when it is opened and adds it to the
A list of kernel timers that output the current jiffies per second (for this reason, the new expires is modified each time in the timer handler function).
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h > #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/ cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #define Second_major 248/* Preset second main device number */static int second_major = secong_major;/*second device structure */struct second_dev{struct Cdev cdev;/* Cdev structure */atomic_t counter;/* Total number of seconds */struct timer_list s_timer; * * Device to use the timer */}struct Second_dev *SECOND_DEVP;/* device struct pointer */struct void second_timer_handle (unsigned long arg) {Mod_timer (&second_devp->s_timer,jiffies + Hz); Atomic_inc (&second_devp->counter);p rintk (kern_notice "Current jiffies is%d\n", jiffies);} /* File Open function */int second_open (struct inode *inode, struct file *filp) {/* Initialize timer */init_timer (&second_devp->s_timer) ; second_devp->s_timer.function = &second_timer_handle; second_devp->s_timer.expires = jiffies + Hz; Add_timer (&second_devp->s_timer);/* Add (Register) timer */atomic_set (&second_devp->count,0); Count clear 0return 0;} /* File release function */int second_release (struct inode *inode, struct file *filp) {Del_timer (&second_devp->s_timer); return 0 ;} /* Read function */static ssize_t second_read (struct file *filp, char __user *buf,size_t count,loff_t *ppos) {int counter; counter = at Omic_read (&second_devp->counter); if (Put_user (counter, (int *) BUF) Return-efault; else return sizeof (unsigned int);} /* File operation struct */static const struct File_operations second_fops = {. Owner = This_module,. Open = Second_open,. Release = Second _release,. Read = Second_read,}; * Initialize and register cdev*/static void Second_setup_cdev (struct Second_dev *dev,int index) {int err,d Evno = MKDEV (Second_major,index); Cdev_init (&dev->cdev,&second_fops);d ev->cdev.owner = THIS_MODULE; Err = Cdev_add (&dev->cdev,devno,1), if (err) PRINTK (Kern_notice, "Error%d adding led%d", err,index);} /* Device driver module load function */int second_init (void) {int ret;d ev_T devno = MKDEV (second_major,0);/* Request device number */if (second_major) ret = register_chrdev_region (devno,1, "second"); Else{ret = Alloc_chrdev_region (&devno,0,1, "second"); second_major = major (Devno);} if (Ret < 0) return ret;/* Dynamic request device structure Memory */SECOND_DEVP = kmalloc (sizeof (struct second_dev), gfp_kern); if (!SECOND_DEVP) { /* Application Failed */ret =-enomem; goto Fail_malloc;} memset (second_devp,0,sizeof (struct second_dev)); Second_setup_cdev (second_devp,0); fail_malloc:unregister_chrdev_ Region (devno,1); return ret;} /* Module unload function */void second_exit (void) {Cdev_del (&second_devp->cdev);/* Logout cdev*/kfree (SECOND_DEVP);/* release device structure BODY */ Unregister_chrdev_reigon (MKDEV (second_major,0), 1);} Module_author ("hasen<[email protected]>"); Module_license ("Dual BSD/GPL"); Module_param (Second_major,int,s_irugo); Module_init (Second_init); Module_exit ( Second_exit);
In the open () function of second, the timer is started, and the timer handler function is run again every second thereafter, in the second
In the release () function, the timer is removed.
The atomic variable counter in the Second_dev struct is used for the second count, each time in the timer handler function is Atomic_inc ()
The read () function that calls the 1,second of an atom returns this value to the user space.
The following is a second Test program second_test.c
#include ... main () {     int fd;     int counter = 0;     int old_counter = 0;          /* Open/dev/second Device file *     /fd= open ("/dev/second", o_rdonly);     if (FD! =-1) {     while (1) {     read (fd,&counter,sizeof (unsigned int));/* Reads the number of seconds currently experienced */     if (counter! = Old_ Counter) {printf ("seconds after Open/dev/second:%d\n", counter); old_counter = counter;     }     }     else{     printf ("Device open failure\n");}     }            
After running Second_test, the kernel will continuously output the current jiffies value, and the application will continuously output self-opening
The number of seconds since/dev/second.
Second, kernel delay
1. Short delay
The following 3 functions are available in the Linux kernel for nanosecond, microsecond, and millisecond latencies, respectively:
void Ndelay (unsigned long nsecs), void Udelay (unsigned long usecs), void Mdelay (unsigned long msecs);
The implementation of the above delay is essentially a busy wait, it is based on the CPU frequency of a certain number of cycles, the software to do such a delay:
void delay (unsigned int time) {    while (time--);}
The Ndelay (), Udelay (), and Mdelay () functions are implemented in such a way that the mechanism is similar. The kernel starts up Yes, it runs a delay test
The program (Delay Looop calibration) calculates the LPJ (loop per jiffy). For example, for LDD6410 boards, the kernel starts
Print:
Calibrating delay loop ... 530.84 bogomips (lpj=1327104)
Millisecond latency (and greater second-time latency) is relatively large, and in the kernel it is best not to use the Mdelay () function directly, which will have no
CPU resources, the kernel provides the following functions for latency over the millisecond level:
void msleep (unsigned int millisecs) unsigned long msleep_interruptible (unsigned int millisecs); void ssleep (unsigned int seconds);
The above function will make the process sleep parameter called it specified time, Msleep (), Ssleep () can not be interrupted, and
Msleep_interruptible () can be interrupted.
2, long delay
A straightforward way to delay in the kernel is to compare the current jiffies and Target jiffies (set to current Jiffies plus time
Interval of jiffies), until the next jiffies hits the target jiffies.
Example: Delay 100 jiffies before delay 2s with busy waiting
/* Delay of 100 jiffies*/unsigned long delay = jiffies + +, while (Time_before (Jiffies,delay));/* Delay 2s*/unsigned long delay = Jif Fies + 2*hz; while (Time_before (Jiffies,delay));
There is also a time_after () corresponding to Time_before (), which is defined in the kernel as (in fact, just passing in the future
A simple comparison between the jiffies and the jiffies being called):
#define Time_after (b) (Typecheck (unsigned long, a)) &&typecheck (unsigned long, b) && ((long)-(long) (a) <0) #define Time_before (A, B) time_after (b,a)
To prevent Timer_before () and Timer_after () comparisons from being optimized by the compiler optimizer for jiffies, the kernel defines it
is a volatile variable, which guarantees that it is re-read every time.
3, Sleep delay
Sleep delay is undoubtedly a better way than busy waiting, sleep delay between waiting time arrives process is asleep, CPU resource
Used by other processes. Schedule_timeout () can make the current task sleep specified after the jiffies is scheduled to execute again, Msleep () and
Msleep_interruptible () is essentially dependent on the schedule_timeout_uninterruptible () and the Schedule_timeout () that contain the
Implemented by Schedule_timeout_interruptible ().
void msleep (unsigned int msecs) {     unsigned long timeout = msecs_to_jiffies (msecs) +1;     while (timeout)     timeout = schedule_timeout_uninterruptible (timeout);}  unsigned long msleep_interruptible (unsigned int msecs) {unsigned long timeout = msecs_to_jiffies (msecs) + 1; while (timeout &&!signal_pending (current) timeout = schedule_timeout_interruptible (timeout); return Jiffies_to_msecs ( timeout);}
In fact, the principle of schedule_timeout () is to add a timer to the system that wakes up the parameter in the timer handler function.
The process that should be. The difference between the schedule_timeout_uninterruptible () and schedule_timeout_interruptible () functions is that the former call
Schedule_timeout () precedes a process state of task_uninterruptible, which has a process state of task_interruptible.
Signed Long __sched schedule_timeout_interruptible (signed long timeout) {      __set_current_state (task_interruptible ) ;     return Schedule_timeout (timeout);} Signed Long __sched schedule_timeout_uninterruptible (signed long timeout) {  __set_current_state (task_ uninterruptible); return Schedule_timeout (timeout);}      
In addition, the following two functions can add the current process to the wait queue, thus sleeping on the waiting queue. When the timeout occurs, the input
The process will be awakened (the latter can be interrupted before the timeout):
Summarize
Latency in the kernel can be busy waiting or sleep wait, in order to make full use of CPU resources, is the system has better throughput performance, in the
Latency is not very precise, and sleep waiting is usually recommended. While the Ndelay (), Udelay () busy wait mechanism is in the drive
is typically designed to accommodate short-time latency requirements on hardware.

Hasen Linux device Driver Development learning journey--clock

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.