One, interrupt nesting
When the system is executing an interrupt handler function, a new interrupt is created, which is called interrupt nesting. When the interrupt is a slow interrupt, the new interrupt replaces the current interrupt, that is, the current interrupt is not completed, and the new terminal is not created when the interrupt is a fast interrupt. Both of these situations are things that we don't want to see, so we have the topic of today-breaking hierarchies.
Second, interrupt stratification
Interrupt layering is the separation of interrupt processing functions into the upper and lower two parts. The upper part is hardware-related, and the lower half is hardware-independent. Hardware-independent content we can separate it out of the interrupt processing function and give it to the kernel to handle when the system is idle, which shortens the time to interrupt processing, thus reducing the chance of interruption loss.
Layering mode:
1. Soft Interrupt
2, Tasklet
3. Work Queue
Third, Work queue processing mode
Mounts the work in the lower part of the interrupt to the work queue and calls the work when the kernel is idle. The Linux kernel uses struct workqueue_struct to describe a task queue, and a struct work_struct to describe a work item.
structworkqueue_struct {unsignedintFlags/*i:wq_* Flags*/Union {structCpu_workqueue_struct __PERCPU *pcpu; structCpu_workqueue_struct *Single ; unsignedLongv; } Cpu_wq; /*I:cwq ' s*/ structList_head list;/*w:list of all Workqueues*/ structMutex Flush_mutex;/*protects Wq Flushing*/ intWork_color;/*f:current Work Color*/ intFlush_color;/*f:current Flush Color*/atomic_t Nr_cwqs_to_flush;/*Flush in Progress*/ structWq_flusher *first_flusher;/*F:first flusher*/ structList_head Flusher_queue;/*F:flush Waiters*/ structList_head Flusher_overflow;/*F:flush Overflow list*/mayday_mask_t Mayday_mask; /*CPUs requesting Rescue*/ structWorker *rescuer;/*i:rescue worker*/ intsaved_max_active;/*w:saved cwq max_active*/ Const Char*name;/*i:workqueue name*/};
struct work_struct { atomic_long_t data; struct List_head entry; work_func_t func;};
1. Create a Work queue
Create_workqueue (name)
Parameter: Name is a const char * type, creating the name of the work queue
return value: struct workqueue_struct* type pointer
2. Create a job
Init_work (_work, _func)
Parameters:
Work: Create a good address for a working item variable
Func: The name of the member Func in the work item
No return value
3. Submit work
Attach the work item to the work queue.
int queue_work (struct workqueue_struct *wq, struct work_struct *work)
Parameters:
WQ: Address of the Work queue variable
Work: Address of a working item variable
In fact, the Linux kernel has created a default work queue for users to use, KEVENTD_WQ, so we don't have to work on creating work queues. The function that submits the work item to the default work queue is
int schedule_work (struct work_struct *work)
Parameters: addresses to submit work items
Work Queue application code:
#include <linux/module.h>#include<linux/init.h>structWork_struct *work1,*Work2;voidWork1_func () {PRINTK ("This is work1\n");}voidWork2_func () {PRINTK ("This is work2\n");}intQue_init () {/*Create a job*/Work1= Kmalloc (sizeof(structwork_struct), Gfp_kernel); Init_work (Work1, Work1_func); /*mount work to work queue*/schedule_work (WORK1); /*Create a job*/Work2= Kmalloc (sizeof(structwork_struct), Gfp_kernel); Init_work (WORK2, Work2_func); /*mount work to work queue*/schedule_work (WORK2); return 0;}voidQue_exit () {}module_license ("GPL"); Module_init (Que_init); Module_exit (que_exit) ;
Apply interrupt layering to the keyboard interrupt driver:
#include <linux/module.h>#include<linux/init.h>#include<linux/fs.h>#include<linux/miscdevice.h>#include<linux/interrupt.h>#include<linux/io.h>#defineGpgcon 0x56000060/*Defining work Queues*/structWork_struct *Work1;voidWork1_func () {PRINTK ("Press key down\n");}intQue_init () {/*Create a job*/Work1= Kmalloc (sizeof(structwork_struct), Gfp_kernel); Init_work (Work1, Work1_func);}/*Interrupt handler function*/irqreturn_t Key_int (intIrqvoid*dev_id) { /*1, detect whether the device has interrupted*/ /*2. Clear Interrupt generation Mark*/ /*3. Submit the lower part of the work*/schedule_work (WORK1); return 0;}voidKey_hw_init () {unsignedintdata; unsignedint*Gpio_config; Gpio_config= Ioremap (Gpgcon,4); Data=READW (gpio_config); Data&= ((3)| (3<<6)| (3<<Ten)| (3<< A)| (3<< -)| (3<< A));//~ (0B11);Data |= (2| (2<<6)| (2<<Ten)| (2<< A)| (2<< -)| (2<< A));//0B10;Writew (data,gpio_config);}intKey_open (structInode *node,structFile *Filp) { return 0; }structFile_operations Key_fops ={. Open=Key_open,//. Unlocked_ioctl = Key_ioctl,};structMiscdevice Key_miscdev ={. Minor= $,. Name="Key",. FoPs= &Key_fops,};Static intKey_init () {/*Registering your device*/Misc_register (&Key_miscdev); /*Hardware Initialization*/Key_hw_init (); /*Registration Interrupted*/Request_irq (irq_eint8,key_int,irqf_trigger_falling,"Key",0); REQUEST_IRQ (irq_eint11,key_int,irqf_trigger_falling,"Key",0); REQUEST_IRQ (irq_eint13,key_int,irqf_trigger_falling,"Key",0); REQUEST_IRQ (irq_eint14,key_int,irqf_trigger_falling,"Key",0); REQUEST_IRQ (irq_eint15,key_int,irqf_trigger_falling,"Key",0); REQUEST_IRQ (irq_eint19,key_int,irqf_trigger_falling,"Key",0); Que_init (); PRINTK ("Key.ko is ready\n"); return 0;}Static voidKey_exit () {/*Logout Device*/Misc_deregister (&Key_miscdev); /*Logoff interrupts*/Free_irq (Irq_eint8,0);} Module_license ("GPL"); Module_init (Key_init); Module_exit (key_exit) ;
Implementation features:
When a button is pressed, the corresponding information is printed in the string.
This code applies to the MINI2440 Development Board, different types of development Board IO port and interrupt number is different. If you have questions or suggestions, please note.
Linux Interrupt tiering Technology