由于I/O操作的不确定因素以及处理器和I/O设备之间速度的不匹配(I/O设备的速度要比处理器慢很多),设备往往通过某种硬件信号异步地唤醒起处理器的注意。这些硬件信号就是中断。每个中断设备都被分配了一个相关的标识符,被称为中断请求(IRQ)号。当处理器检测到某一IRQ号对应的中断产生时,它将停止现在的工作,并启动该IRQ所对应的中断服务例程(ISR)。中断处理函数在中断上下文执行。
模块在使用中断前要先请求一个中断通道(或者中断请求IRQ),然后在使用后释放该通道。
可以在kernel-4.9\include\linux\interrupt.h找到它们的定义:
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
/**
* request_threaded_irq - allocate an interrupt line
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQ occurs.
* Primary handler for threaded interrupts
* If NULL and thread_fn != NULL the default
* primary handler is installed
* @thread_fn: Function called from the irq handler thread
* If NULL, no irq thread is created
* @irqflags: Interrupt type flags
* @devname: An ascii name for the claiming device
* @dev_id: A cookie passed back to the handler function
*
* This call allocates interrupt resources and enables the
* interrupt line and IRQ handling. From the point this
* call is made your handler function may be invoked. Since
* your handler function must clear any interrupt the board
* raises, you must take care both to initialise your hardware
* and to set up the interrupt handler in the right order.
*
* If you want to set up a threaded irq handler for your device
* then you need to supply @handler and @thread_fn. @handler is
* still called in hard interrupt context and has to check
* whether the interrupt originates from the device. If yes it
* needs to disable the interrupt on the device and return
* IRQ_WAKE_THREAD which will wake up the handler thread and run
* @thread_fn. This split handler design is necessary to support
* shared interrupts.
*
* Dev_id must be globally unique. Normally the address of the
* device data structure is used as the cookie. Since the handler
* receives this value it makes sense to use it.
*
* If your interrupt is shared you must pass a non NULL dev_id
* as this is required when freeing the interrupt.
*
* Flags:
*
* IRQF_SHARED Interrupt is shared
* IRQF_TRIGGER_* Specify active edge(s) or level
*
*/
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
通常,reques_irq函数返回给请求函数的值为0时表示申请成功,为负值时表示错误码。函数返回-EBUSY表示已经有另一个驱动程序占用了你要请求的中断信号线。
参数irq是要请求的中断号;
handler是向系统注册的中断处理函数,是一个回调函数,中断发生时系统调用这个函数;
flags是中断处理相关的一个位掩码;
SA_INTERRUPT/IRQF_DISABLED:表明这是一个快速中断处理例程。快速中断处理例程运行在中断的禁用状态下。
SA_SHIRQ/IRQF_SHARED:表明中断可以在设备之间共享。
SA_SAMPLE_RANDOM/IRQF_SAMPLE_RANDOM:表明产生中断能对/dev/random设备和/dev/urandom设备使用的熵池有贡献。从这些设备读取,将会返回真正的随机数,从而有助于应用软件选择用于加密的安全秘钥。这些随机数是从一个熵池中得到的,各种随机事件都会对该熵池作出贡献,
name是会中断的名字,用来在/proc/interrupts中显示中断的拥有者;
dev是用于共享的中断信号线。它是唯一的标识符,在中断信号线空闲时可以使用它,驱动程序也可以使用它指向驱动程序自己的私有数据区(用来识别哪个设备产生中断)。在没有强制使用共享方式时,dev可以被设置为NULL,总之用它来指向设备的数据结构是一个比较好的思路。
free_irq的参数
/**
* free_irq - free an interrupt allocated with request_irq
* @irq: Interrupt line to free
* @dev_id: Device identity to free
*
* Remove an interrupt handler. The handler is removed and if the
* interrupt line is no longer in use by any driver it is disabled.
* On a shared IRQ the caller must ensure the interrupt is disabled
* on the card it drives before calling this function. The function
* does not return until any executing interrupts for this IRQ
* have completed.
*
* This function must not be called from interrupt context.
*/
void free_irq(unsigned int irq, void *dev_id)
中断处理例程可在驱动程序初始化时或设备第一次打开时安装。虽然在模块的初始化函数中安装中断处理例程看起来是个好主意,但实际上并非如此。因为中断信号线的数量是非常有限的,我们不想肆意浪费。计算机拥有的设备通常要比中断信号线多得多,如果一个模块在初始化时请求了IRQ,那么即使驱动程序只是占用它而从未使用,也将会阻止任意一个其他的驱动程序使用该中断。而在设备打开的时候申请中断,则可以共享这些有限的资源。
因此,调用request_irq的正确位置应该是在设备第一次打开、硬件被告知产生中断之前。调用free_irq的位置是最后一次关闭设备、硬件被告知不再中断处理器之后。这种技术的缺点是必须为每一个设备维护一个打开计数,这样我们才能知道什么时候可以禁用中断。