Brief Introduction to interrupt handling for Linux Device Drivers

Source: Internet
Author: User

Brief Introduction to interrupt handling for Linux Device Drivers

The first step related to interrupt handling in Linux device drivers is to apply for and release IRQ APIs: request_irq () and free_irq ().

The prototype of request_irq () is:

 

Int request_irq (unsigned int IRQ, void (* Handler) (int irq, void * dev_id, struct pt_regs * regs), unsigned long irqflags, const char * devname, void * dev_id );

IRQ is the hardware interrupt number to be applied;

Handler is the interrupt processing function registered to the system. It is a callback function. When an interrupt occurs, the system calls this function and the dev_id parameter is passed;

Irqflags is the attribute of Interrupt Processing. If sa_interrupt is set, it indicates that the interrupt processing program is a fast processing program. When the fast processing program is called, all interrupts are blocked, and the slow processing program is not blocked. If sa_shirq is set, multiple devices are interrupted. dev_id is used when sharing is interrupted. It is generally set to the device structure of the device itself or null.

Free_irq () is prototype:

Void free_irq (unsigned int IRQ, void * dev_id );

In addition, an important concept that is closely related to Linux interrupt is that Linux interrupt is divided into two parts: top half (tophalf) and bottom half (bottom half ). The upper half function is "registration interruption". When an interruption occurs, it reads and writes the relevant hardware and then mounts the lower half of the Interrupt Routine to the execution queue of the device. Therefore, the execution speed in the upper half is very fast, and more interrupt requests can be served. However, only "registration interruption" is far from enough, because the interruption event may be complicated. Therefore, Linux introduces a lower half to complete the vast majority of the mission of Interrupt events. The biggest difference between the lower half and the upper half is that the lower half can be interrupted, while the upper half cannot be interrupted. The lower half has almost done all the things of the interrupt handler and can be interrupted by new interruptions! In the lower half, it is not very urgent and usually time-consuming. Therefore, the system schedules the running time on its own and does not execute it in the context of the interrupted service.

In Linux, the lower half of the implementation mechanism mainly includes tasklet and work queue.

Tasklet is based on Linux softirq. It is quite simple to use. We only need to define tasklet and its processing functions and associate them:

Void my_tasklet_func (unsigned long); // defines a processing function:

Declare_tasklet (my_tasklet, my_tasklet_func, data); // defines a tasklet structure my_tasklet, which is associated with the my_tasklet_func (data) function.

Then, when tasklet needs to be scheduled, a simple API can be referenced to enable the system to schedule and run as appropriate:

Tasklet_schedule (& my_tasklet );

In addition, Linux provides other APIs for controlling tasklet scheduling and running:

 

Struct (name, function, data); // similar to declare_tasklet, but waits for the tasklet to enable tasklet_enable (struct tasklet_struct *); // enables tasklet tasklet_disble (struct tasklet_struct *); // disable tasklet tasklet_init (struct tasklet_struct *, void (* func) (unsigned long), unsigned long); // similar to declare_tasklet () tasklet_kill (struct tasklet_struct *); // clear the scheduling bit of the specified tasklet, that is, the tasklet cannot be scheduled.

Let's first look at a tasklet running instance. This instance has no practical significance, just for demonstration. Its function is to schedule a tasklet once globalvar is written, and the function outputs "tasklet is executing ":

 

# Include <Linux/interrupt. h>... // Define and bind the tasklet function void test_tasklet_action (unsigned long T); declare_tasklet (test_tasklet, test_tasklet_action, 0); void test_tasklet_action (unsigned long T) {printk ("tasklet is executing/N ");}... Ssize_t globalvar_write (struct file * filp, const char * Buf, size_t Len, loff_t * off ){... If (copy_from_user (& global_var, Buf, sizeof (INT) {return-efault;} // schedule tasklet to execute tasklet_schedule (& test_tasklet); Return sizeof (INT );}

As interruptions are closely related to real hardware, it is meaningless to talk about interruptions without hardware. Let's give a simple example. This example is from an embedded system instance of Samsung S3C2410. Let's look at the interrupt-related part of the real-time clock driver:

 

Static struct fasync_struct * comment; static int _ init rtc_init (void) {misc_register (& rtc_dev); create_proc_read_entry ("Driver/RTC", 0, 0, rtc_read_proc, null ); # If rtc_irq if (rtc_has_irq = 0) goto no_irq2; init_timer (& rtc_irq_timer); iterator = timer; spin_lock_irq (& rtc_lock);/* initialize periodic freq. to CMOS reset default, which is 1024Hz */cmos_write (CMOS _ Read (rtc_freq_select) & 0xf0) | 0x06), rtc_freq_select); spin_unlock_irq (& rtc_lock); rtc_freq = 1024; no_irq2: # endif printk (kern_info "real time clock driver v" rtc_version "/N"); Return 0;} static void _ exit rtc_exit (void) {remove_proc_entry ("Driver/RTC", null); misc_deregister (& rtc_dev); release_region (rtc_port (0), rtc_io_extent); If (partition) free_irq (rtc_irq, null );} static void rtc_interr Upt (int irq, void * dev_id, struct pt_regs * regs) {/** can be an alarm interrupt, update complete interrupt, * or a periodic interrupt. we store the status in the * low byte and the number of interrupts initialized ed since * The Last read in the remainder of rtc_irq_data. */spin_lock (& rtc_lock); rtc_irq_data + = 0x100; rtc_irq_data & = ~ 0xff; rtc_irq_data | = (cmos_read (rtc_intr_flags) & 0xf0); If (rtc_status & rtc_timer_on) mod_timer (& timer, jiffies + Hz/rtc_freq + 2 * Hz/100 ); spin_unlock (& rtc_lock);/* now do the rest of the actions */wake_up_interruptible (& rtc_wait); kill_fasync (& rtc_async_queue, sigio, poll_in );} static int rtc_fasync (int fd, struct file * filp, int on) {return fasync_helper (FD, filp, on, & rtc_async _ Queue);} static void rtc_dropped_irq (unsigned long data) {unsigned long freq; spin_lock_irq (& rtc_lock);/* just in case someone disabled the timer from behind our back... */If (rtc_status & rtc_timer_on) mod_timer (& rtc_irq_timer, jiffies + Hz/rtc_freq + 2 * Hz/100); rtc_irq_data + = (rtc_freq/Hz) <8 ); rtc_irq_data & = ~ 0xff; rtc_irq_data | = (cmos_read (rtc_intr_flags) & 0xf0);/* restart */freq = rtc_freq; spin_unlock_irq (& rtc_lock); printk (kern_warning "RTC: lost some interrupts at % ldhz. /n ", freq);/* now we have new data */wake_up_interruptible (& rtc_wait); kill_fasync (& rtc_async_queue, sigio, poll_in );}

After the RTC interrupt occurs, an asynchronous signal is triggered. Therefore, the driver provides support for the asynchronous signal in section 6th. Not every interrupt requires a lower half. If it is not complicated to handle, there may be only one upper half. In this example, the RTC driver is like this.

 

This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/gothicane/archive/2007/08/12/1739562.aspx

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.