"I exhort you to say that you will not forget what you told me I also all cherish for us to remember is the day will never be yellow when the time is always very short look forward to the time is always very long years of the brook picked up how many shiny lines if you want to miss me to look at the sky that twinkling stars have I look for your eyes Light "Thank you, ever come ~
Interrupts and timers we are familiar with the problem, we are in the study of bare metal development, this is almost a difficult point, but also the necessary module information for each program, then in Linux, how can we achieve delay, count, and interrupt it?
First, interruption
1. Overview
The so-called interruption refers to the CPU in the process of executing the program, there are some emergencies to be processed, the CPU must suspend the execution of the current execution of the program, to deal with emergencies, after processing the CPU and return to the original program location and continue to execute, according to the source of interruption, interrupts are divided into internal interrupts and external interrupts, Soft interrupt instructions such as internal interrupts, interrupts can also be divided into masked interrupts and can not be shielded interrupt. Linux interrupt processing is divided into the top half and the bottom half, the top half to complete as few of the more urgent functions, often simply complete the "registration interrupted" work, is to hang the bottom half of the processing program to the bottom half of the device processing queue, interrupt processing mechanism such as:
2. Interrupt Programming
2.1 Application and release interruption
(1) Apply for IRQ
int REQUEST_IRQ (unsigned int IRQ, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
IRQ is the interrupt number to be applied, handler is the interrupt processing function to register to the system, Irq_flags is the attribute of interrupt processing, can specify the way of triggering the interrupt, and in the way of processing, the irqf_disabled indicates that the interrupt handler is a fast handler. When the fast handler is invoked, all interrupts are masked, irqf_shared, which means that multiple devices share interrupts (interrupt handlers). DEV_ID is used when sharing is interrupted, and is generally set to the structure of the device or null.
(2) Release IRQ
void Free_irq (unsigned int irq, void *dev_id); The parameter definition is the same as REQUEST_IRQ ()
2.2. Enable Shield interrupt
(1) Shielding (3)
void Disable_irq (int IRQ);
void Disable_irq_nosync (ing IRQ);//return immediately
void Enable_irq (int IRQ);
The difference between void Disable_irq_nosync (int IRQ) and void Disable_irq (int irg) is that the former returns immediately, while the latter waits for the current interrupt to finish processing.
(2) Block all interrupts
#define LOCAL_IRQ_SAVE (Flags)//block this CPU all
void local_irq_disable (void)//shield This CPU all interrupts
The former retains the interrupt state in the flags (flags is unsigned long type).
(3) Resume interrupt
#define LOCAL_IRQ_RESTORE (Flags)
void local_irq_enable (void);
The method that starts with local is scoped to this CPU.
2.3 Bottom half mechanism--implementation mechanism mainly has tasklet, work queue and soft interrupt
(1) Tasklet
void My_tasklet_func (unsigned long);D Eclare_tasklet (My_tasklet, My_tasklet_func, data);/* Defines a tasklet structure my_tasklet, */tasklet_schedule (&my_tasklet) associated with the My_tasklet_func (data) function;/* Enables the system to dispatch Tasklet registered functions when appropriate */
(2) Work queue
struct work_struct my_wq;void my_wq_func (unsigned long), Init_work (&my_wq, (Void (*) (void *)) my_wq_func, NULL) ;/* Initializes the work queue and binds it to the handler function */schedule_work (&MY_WQ); /* Schedule Work Queue execution */
(3) Soft interrupts (with the usual soft interrupts (software instruction-induced interrupts), such as Arm's SWI are completely different concepts)
In the Linux kernel, a soft interrupt is characterized by a softirq_action structure that contains the soft interrupt handler function pointer and the parameters passed to the function. Use the OPEN_SOFTIRQ () function to register a soft interrupt corresponding to the handler function, while the RAISE_SOFTIRQ () function can trigger a soft interrupt. Soft interrupts and Tasklet run with the soft interrupt context, which is still part of the atomic context, while the work queue runs with the process context. Therefore, soft interrupts and tasklet processing functions cannot sleep, while sleep is allowed in the Work queue processing function. Local_bh_disable () and local_bh_enable () are functions in the kernel that are used to suppress and enable soft interrupts and tasklet the bottom half of the mechanism.
2.4 Interrupt Sharing
Multiple devices sharing a single interrupt line is widespread in the hardware system, multiple devices sharing interrupts should use the IRQF_SHARED flag when requesting an outage, and a device with the IRQF_SHARED flag to request an interruption is successful if the interruption is not applied or the interruption is applied, However, the device before which the interrupt was requested was interrupted with the irqf_shared flag, although the kernel module could access the global address as the last parameter of the REQUEST_IRQ (..., void *dev_id), but the social structure was clearly the best parameter to pass in.
At the mid-end, all interrupt handlers that share this interrupt are traversed, and in the top half of the interrupt handler, the device's interrupt should be judged against the incoming dev_id parameter according to the information in the hardware register.
Shared Interrupt Module
irqreturn_t xxx_interrupt (int irq,void *dev_id,struct pt_regs *regs) {... int status = Read_int_status ();//learned interrupt source if (!is_ Myint (Dev_id,status))//To determine whether the device return irq_none;//is not the device interrupt immediately return//Is this device interrupted for processing ... return irq_handled;//return irq_ Handled description interrupt has been processed} ...
Second, timer/clock
1. Overview
The software sense Timer finally relies on the hardware timer, the kernel detects a timer release expires after the clock interrupt occurs, and the timer processing function after expiration is executed as the bottom half of the soft interrupt. In driver programming, a set of functions and data structures can be used to complete timer triggering or some periodic tasks.
(1) An instance of a timer_list struct corresponds to a timer, which is defined as follows:
struct Timer_list {struct List_head entry,/* Timer list */unsigned long expires,/* Timer expiry time */void (*function) (unsigned long),/* Timer processing function */unsigned long data,/* as a parameter is passed into the timer handler function */struct timer_base_s *base,...};
For example, define a timer named My_timer:
struct Timer_list my_timer;
(2) Initialize timer
void Init_timer (struct timer_list *timer); Timer_initializer (_function, _expires, _data) Define_timer (_name, _function, _expires, _data) Setup_timer ();
(3) Add timer
void Add_timer (struct timer_list *timer);
(4) Delete timer
int Del_timer (struct timer_list *timer);
(5) Modify the expire of the timer
int Mod_timer (struct timer_list *timer, unsigned long expires);
(6) For periodic tasks, the Linux kernel also provides a delayed_work mechanism that is essentially implemented with work queues and timers.
6.1, Kernel delay
The following 3 functions are available in the Linux kernel for nanosecond, subtle, and millisecond latencies, respectively.
void Ndelay (unsigned long nsecs), void Udelay (unsigned long usecs), void Mdelay (unsigned long msecs);
The principle of the above-mentioned delay is essentially busy waiting, millisecond delay compared CPU consumption resources, for milliseconds or more delay, the kernel provides the following functions
void msleep (unsigned int millisecs) unsigned long msleep_interruptible (unsigned int millisecs); void ssleep (unsigned int seconds);
The above function will make the process called it, the sleep parameter specified time, unsigned long msleep_interruptible () can be interrupted by the signal, the other two No
6.2, Sleep delay
Sleep delay between waiting time arrives the process is asleep, schedule_timeout () can make the current task sleep specified after jiffies is dispatched again, Msleep () and msleep_interruptible () The implementation of the Schedule_timeout () essentially schedule_timeout () is to add a timer to the system that wakes up the parameters corresponding to the process in the timer handler, which combines sleep_on () and __set_current_ Functions such as state (task_interruptible).
2. Kernel Timer use template
Device structure body struct xxx_dev{struct cdev cdev;...struct timer_list xxx_timer;//define timer}//Drive in a function Xxx_funcl (...) {struct Xxx_dev *dev = filp->private_data;...//initialization timer init_timer (&dev->xxx_time);d ev->xxx_ Timer.function = &xxx_do_timer;//Defines timer handler function Dev->xxx_timer.data = (unsigned long) dev;//device struct pointer as timer processing parameter dev- >xxx_timer.expires = jiffies + delay;//define expiry time Add_timer (&dev->xxx_timer);//register Timer ...} A function Xxx_func2 (...) in the driver. {...//delete interrupt Del_timer (&dev->xxx_timer); ...} Timer handler function static void Xxx_do_timer (unsigned long arg) {struct Xxx_dev *dev = filp->private_data;...dev->xxx_ Timer.expires = jiffies + delay;//Reset Timing time Add_timer (&dev->xxx_timer); ...} Hz means delay 1s
3, instance-the second character device second_drv.c, it is opened when the initialization of the timer added to the core timer list, output the current jiffes per second, the code is as follows:
#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> #include <linux/ slab.h> #define SECOND_MAJOR 248static int second_major = second_major;struct Second_dev {struct Cdev cdev; atomic_t counter; struct timer_list s_timer;}; struct Second_dev *second_devp;static void second_timer_handle (unsigned long arg) {Mod_timer (&second_devp->s_t Imer, jiffies + HZ); Atomic_inc (&second_devp->counter); PRINTK (kern_notice "Current jiffies is%ld\n", jiffies);} int Second_open (struct inode *inode, struct file *filp) {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); Atomic_set (&second_devp->counter, 0); return 0;} int second_release (struct inode *inode, struct file *filp) {Del_timer (&second_devp->s_timer); return 0;} Static ssize_t second_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos) {int counter; Counter = Atomic_read (&second_devp->counter); if (Put_user (counter, (int *) BUF)) Return-efault; else return sizeof (unsigned int);} static const struct File_operations second_fops = {. Owner = This_module,. Open = Second_open,. Release = Second_ Release,. Read = second_read,};static void Second_setup_cdev (struct second_dev *dev, int index) {int err, Devno = M Kdev (second_major, index); Cdev_init (&dev->cdev, &second_fops); Dev->cdev.owner = This_module; Err = Cdev_add (&dev->cdev, Devno, 1); if (err) PRINTK (kern_notice "Error%d adding Cdev%d", err, index);} int Second_init (void) {int reT dev_t Devno = MKDEV (second_major, 0); if (second_major) ret = Register_chrdev_region (Devno, 1, "second"); else {return alloc_chrdev_region (&devno, 0, 1, "second"); Second_major = Major (Devno); } if (Ret < 0) return ret; SECOND_DEVP = kmalloc (sizeof (struct second_dev), gfp_kernel); if (!SECOND_DEVP) {ret =-enomem; Goto Fail_malloc; } memset (SECOND_DEVP, 0, sizeof (struct second_dev)); Second_setup_cdev (SECOND_DEVP, 0); Return 0;fail_malloc:unregister_chrdev_region (Devno, 1); return ret;} void Second_exit (void) {Cdev_del (&second_devp->cdev); Kfree (SECOND_DEVP); Unregister_chrdev_region (MKDEV (second_major, 0), 1);} Module_author ("Ljia-----Ljia"); 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 after that, the timer handler function is run again every second, and after the release () function is removed, compiled, loaded and created "/dev/second" Device file node, the following program opens, Second_ Test will continuously read the number of seconds that have passed since the "/dev/second" device file.
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include < fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h>int main (void) { int FD; int counter = 0; int old_counter = 0; FD = open ("/dev/second", o_rdonly); if (FD! =-1) { while (1) { read (fd, &counter, sizeof (unsigned int)) ; if (counter! = Old_counter) { printf ("Seconds after Open/dev/second:%d\n", counter); Old_counter = Counter;}} } else { printf ("Device open failure\n"); } return 0;}
After running second_test, the value of jiffes is continuously output, as follows
Current Jiffes is 17216Current Jiffes is 17316Current Jiffes is 17416Current Jiffes is 17516Current Jiffes is 17616Current Jiffes is 17716Current Jiffes is 17816Current Jiffes is 17916Current Jiffes is 17016Current Jiffes is 17116Current Jiffes is 17216Current Jiffes is 17316
And the application will continue to output from the open "/dev/second" as follows:
Seconds after Open/dev/second:1seconds after open/dev/second:2seconds after open/dev/second:3seconds after open/dev/second:4seconds after open/dev/second:5seconds after open/dev/second:6seconds after open/dev/second:7seconds after open/dev/second:8seconds after open/dev/second:9seconds after open/dev/second:Ten
Iii. Summary
Linux interrupt processing is divided into two halves, the above is very clear, here the emphasis below, in order to make full use of CPU resources, in the case of delayed use is not very accurate, sleep waiting is recommended. For the above examples, we need you to knock out the operation of Linux, and compile, see the results of the output to fully understand ~
All rights reserved, reprint please specify reprint address: http://www.cnblogs.com/lihuidashen/p/4462220.html
~linux device-driven interrupts and timers in layman's