Linux内核活动之中断API
- 1. `__tasklet_hi_schedule( )`
- 2. `__tasklet_schedule( )`
- 3. `disable_irq( )`
- 4. `disable_irq_nosync()`
- 5. `disable_irq_wake()`
- 6. `irq_set_irq_wake()`
- 7. `enable_irq()`
- 8. `enable_irq_wake()`
- 9. `free_irq()`
- 10. `irq_set_chip()`
- 11. `irq_set_chip_data()`
- 12. `irq_set_irq_type()`
- 13. `remove_irq()`
- 14. `request_irq()`
- 15. `request_threaded_irq( )`
- 16. `setup_irq()`
- 17. `tasklet_disable()`
- 18. `tasklet_disable_nosync()`
- 19. `tasklet_enable()`
- 20. `tasklet_hi_schedule()`
- 21. `tasklet_init()`
- 22. `tasklet_kill()`
- 23. `tasklet_schedule()`
- 24. `tasklet_trylock()`
- 25. `tasklet_unlock()`
- refer to
1. __tasklet_hi_schedule( )
kernel/softirq.c
void __tasklet_hi_schedule(struct tasklet_struct *t)
{
__tasklet_schedule_common(t, &tasklet_hi_vec,
HI_SOFTIRQ);
}
EXPORT_SYMBOL(__tasklet_hi_schedule);
static void __tasklet_schedule_common(struct tasklet_struct *t,
struct tasklet_head __percpu *headp,
unsigned int softirq_nr)
{
struct tasklet_head *head;
unsigned long flags;
local_irq_save(flags);
head = this_cpu_ptr(headp);
t->next = NULL;
*head->tail = t;
head->tail = &(t->next);
raise_softirq_irqoff(softirq_nr);
local_irq_restore(flags);
}
-
函数功能描述:
函数__tasklet_hi_schedule( )
的主要作用是将参数t代表的软中断的描述符添加到向量tasklet_hi_vec
的尾部,等待获得CPU资源,被调度执行。tasklet_hi_vec
代表高优先级的软中断描述符链表。通过此函数添加的软中断具有较高的优先级,会先被调度处理。 -
输入参数说明:
函数输入参数是struct tasklet_struct
结构体类型的指针,保存软中断的描述符信息,其定义见文件include/linux/interrupt.h
,struct tasklet_struct { struct tasklet_struct *next; unsigned long state; atomic_t count; void (*func)(unsigned long); unsigned long data; };
- 字段
next
指向链表的下一个元素。 - 字段
state
定义了当前软中断的状态,是一个32位的无符号长整数,内核系统中只使用了bit[1]
和bit[0]
两个状态位。其中,bit[1]=1表示当前tasklet
正在执行,它仅对SMP系统有意义,其作用就是为了防止多个CPU同时执行一个tasklet
的情形出现;bit[0]=1
表示当前tasklet
已经被调度,等待获得CPU资源执行。对这两个状态位的宏定义如下所示(interrupt.h):enum { TASKLET_STATE_SCHED, /* 软中断被调度,但未执行*/ TASKLET_STATE_RUN /*软中断正在执行 */ };
- 字段
count
是一个原子计数器,代表当前tasklet
的引用计数值。只有当count
等于0
时,tasklet
对应的中断处理函数才能被执行,也即此时tasklet
是被使能的;如果count
非零,则这个tasklet
是被禁止的。 - 字段
func
是一个函数指针,代表中断的处理函数。 - 字段
data
代表中断处理函数执行时的参数,即字段func
代表函数执行时的参数,是一个32位的无符号整数,其具体含义可由func
字段指向的函数自行解释,比如将其解释成一个指向某个用户自定义数据结构的地址值等。
- 字段
2. __tasklet_schedule( )
kernel/softirq.c
void __tasklet_schedule(struct tasklet_struct *t)
{
__tasklet_schedule_common(t, &tasklet_vec,
TASKLET_SOFTIRQ);
}
- 函数功能描述:
函数__tasklet_schedule( )
用于将一个tasklet_struct
结构体代表的软中断添加到tasklet_vec
队列的尾部,并等待获取CPU资源,被调度执行。tasklet_vec
是一个保存软中断的链表,与链表tasklet_hi_vec
中保存的软中断相比,其保存的软中断优先级较低。 - 输入参数说明:
此函数的输入参数是struct tasklet_struct
结构体类型的指针,代表将添加入内核系统的软中断,保存软中断的信息。
3. disable_irq( )
kernel/irq/manage.c
/**
* disable_irq - disable an irq and wait for completion
* @irq: Interrupt to disable
*
* Disable the selected interrupt line. Enables and Disables are
* nested.
* This function waits for any pending IRQ handlers for this interrupt
* to complete before returning. If you use this function while
* holding a resource the IRQ handler may need you will deadlock.
*
* This function may be called - with care - from IRQ context.
*/
void disable_irq(unsigned int irq)
{
if (!__disable_irq_nosync(irq))
synchronize_irq(irq);
}
EXPORT_SYMBOL(disable_irq);
/**
* disable_irq_nosync - disable an irq without waiting
* @irq: Interrupt to disable
*
* Disable the selected interrupt line. Disables and Enables are
* nested.
* Unlike disable_irq(), this function does not ensure existing
* instances of the IRQ handler have completed before returning.
*
* This function may be called from IRQ context.
*/
void disable_irq_nosync(unsigned int irq)
{
__disable_irq_nosync(irq);
}
EXPORT_SYMBOL(disable_irq_nosync);
static int __disable_irq_nosync(unsigned int irq)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
if (!desc)
return -EINVAL;
__disable_irq(desc);
irq_put_desc_busunlock(desc, flags);
return 0;
}
void __disable_irq(struct irq_desc *desc)
{
if (!desc->depth++)
irq_disable(desc);
}
- 函数功能描述:
函数disable_irq( )
在实现过程中先后调用了函数disable_irq_nosync( )
和函数synchronize_irq( )
,首先调用函数disable_irq_nosync( )
完成增加中断所处的深度和改变中断的状态,然后调用函数synchronize_irq( )
使处理器处于监测中断号所对应的中断状态,当有中断发生时处理器会调用中断处理函数处理此中断。 - 输入参数说明:
此函数的参数是int
型变量,代表操作中断对应的中断号,与数组irq_desc
中元素的下标相对应,结构体变量irq_desc
的定义参见文件include/linux/irqdesc.h
,根据它查找对应设备的中断服务例程,其取值范围是0~NR_IRQS-1
,其中NR_IRQS
的值是16640
。/** * struct irq_desc - interrupt descriptor * @irq_common_data: per irq and chip data passed down to chip functions * @kstat_irqs: irq stats per cpu * @handle_irq: highlevel irq-events handler * @preflow_handler: handler called before the flow handler (currently used by sparc) * @action: the irq action chain * @status: status information * @core_internal_state__do_not_mess_with_it: core internal status information * @depth: disable-depth, for nested irq_disable() calls * @wake_depth: enable depth, for multiple irq_set_irq_wake() callers * @irq_count: stats field to detect stalled irqs * @last_unhandled: aging timer for unhandled count * @irqs_unhandled: stats field for spurious unhandled interrupts * @threads_handled: stats field for deferred spurious detection of threaded handlers * @threads_handled_last: comparator field for deferred spurious detection of theraded handlers * @lock: locking for SMP * @affinity_hint: hint to user space for preferred irq affinity * @affinity_notify: context for notification of affinity changes * @pending_mask: pending rebalanced interrupts * @threads_oneshot: bitfield to handle shared oneshot threads * @threads_active: number of irqaction threads currently running * @wait_for_threads: wait queue for sync_irq to wait for threaded handlers * @nr_actions: number of installed actions on this descriptor * @no_suspend_depth: number of irqactions on a irq descriptor with * IRQF_NO_SUSPEND set * @force_resume_depth: number of irqactions on a irq descriptor with * IRQF_FORCE_RESUME set * @rcu: rcu head for delayed free * @kobj: kobject used to represent this struct in sysfs * @request_mutex: mutex to protect request/free before locking desc->lock * @dir: /proc/irq/ procfs entry * @debugfs_file: dentry for the debugfs file * @name: flow handler name for /proc/interrupts output */ struct irq_desc { struct irq_common_data irq_common_data; struct irq_data irq_data; unsigned int __percpu *kstat_irqs; irq_flow_handler_t handle_irq; #ifdef CONFIG_IRQ_PREFLOW_FASTEOI irq_preflow_handler_t preflow_handler; #endif struct irqaction *action; /* IRQ action list */ unsigned int status_use_accessors; unsigned int core_internal_state__do_not_mess_with_it; unsigned int depth; /* nested irq disables */ unsigned int wake_depth; /* nested wake enables */ unsigned int tot_count; unsigned int irq_count; /* For detecting broken IRQs */ unsigned long last_unhandled; /* Aging timer for unhandled count */ unsigned int irqs_unhandled; atomic_t threads_handled; int threads_handled_last; raw_spinlock_t lock; struct cpumask *percpu_enabled; const struct cpumask *percpu_affinity; #ifdef CONFIG_SMP const struct cpumask *affinity_hint; struct irq_affinity_notify *affinity_notify; #ifdef CONFIG_GENERIC_PENDING_IRQ cpumask_var_t pending_mask; #endif #endif unsigned long threads_oneshot; atomic_t threads_active; wait_queue_head_t wait_for_threads; #ifdef CONFIG_PM_SLEEP unsigned int nr_actions; unsigned int no_suspend_depth; unsigned int cond_suspend_depth; unsigned int force_resume_depth; #endif #ifdef CONFIG_PROC_FS struct proc_dir_entry *dir; #endif #ifdef CONFIG_GENERIC_IRQ_DEBUGFS struct dentry *debugfs_file; const char *dev_name; #endif #ifdef CONFIG_SPARSE_IRQ struct rcu_head rcu; struct kobject kobj; #endif struct mutex request_mutex; int parent_irq; struct module *owner; const char *name; } ____cacheline_internodealigned_in_smp;
- 注意:
需要与函数enable_irq( )
配对使用。
4. disable_irq_nosync()
函数如上
- 函数功能描述:
函数disable_irq_nosync( )
在实现过程中调用了函数__disable_irq( )
,完成增加中断所处的深度和改变中断的状态,一般在中断处理之前调用它保证中断的安全和处理函数的正常运行。 - 输入参数说明:
此函数的参数是int
型变量,代表操作中断对应的中断号,与数组irq_desc
中元素的下标相对应,根据它查找对应设备的中断服务例程,其取值范围是0~NR_IRQS-1
,其中NR_IRQS
的值是16640
。
5. disable_irq_wake()
include/linux/interrupt.h
static inline int disable_irq_wake(unsigned int irq)
{
return irq_set_irq_wake(irq, 0);
}
-
功能描述:
disable_irq_wake( )
在实现过程中调用了函数irq_set_irq_wake( )
,在调用时disable_irq_wake( )
传递的第二个参数是0
。disable_irq_wake( )
使中断处于不可唤醒的中断状态,减少其唤醒深度wake_depth
的值。 -
输入参数说明:
此函数的参数是int型变量,代表操作中断对应的中断号,与数组irq_desc
中元素的下标相对应,根据它查找对应设备的中断服务例程,其取值范围是0~NR_IRQS-1
,其中NR_IRQS
的值是16640
。 -
返回参数说明:
此函数的返回值是整数,代表执行的结果,可能的取值是-6
、0
。返回-6
说明当前中断号对应的irq_desc[]
数组中元素的chip
字段set_wake
字段值为NULL
,此时函数的相应的功能无法实现,即函数既不改变中断唤醒的深度也不改变当前中断的状态。对于函数disable_irq_wake( )
返回0
有两种情况:- 当前中断的唤醒深度是
0
,其函数作用没有实现,作用与返回-6
相同; - 当前的唤醒深度是
1
,则其作用全部实现,即函数既改变了中断唤醒的深度,也改变了当前中断的状态。
- 当前中断的唤醒深度是
6. irq_set_irq_wake()
kernel/irq/manage.c
/**
* irq_set_irq_wake - control irq power management wakeup
* @irq: interrupt to control
* @on: enable/disable power management wakeup
*
* Enable/disable power management wakeup mode, which is
* disabled by default. Enables and disables must match,
* just as they match for non-wakeup mode support.
*
* Wakeup mode lets this IRQ wake the system from sleep
* states like "suspend to RAM".
*/
int irq_set_irq_wake(unsigned int irq, unsigned int on)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
int ret = 0;
if (!desc)
return -EINVAL;
/* wakeup-capable irqs can be shared between drivers that
* don't need to have the same sleep mode behaviors.
*/
if (on) {
if (desc->wake_depth++ == 0) {
ret = set_irq_wake_real(irq, on);
if (ret)
desc->wake_depth = 0;
else
irqd_set(&desc->irq_data, IRQD_WAKEUP_STATE);
}
} else {
if (desc->wake_depth == 0) {
WARN(1, "Unbalanced IRQ %d wake disable\n", irq);
} else if (--desc->wake_depth == 0) {
ret = set_irq_wake_real(irq, on);
if (ret)
desc->wake_depth = 1;
else
irqd_clear(&desc->irq_data, IRQD_WAKEUP_STATE);
}
}
irq_put_desc_busunlock(desc, flags);
return ret;
}
EXPORT_SYMBOL(irq_set_irq_wake);
- 函数功能描述:
函数irq_set_irq_wake( )
用于改变中断的状态及中断的唤醒深度,其对中断状态及中断唤醒深度的影响根据参数on
不同会有不同的结果。如果on
的值为0
,函数将使中断处于睡眠状态,不能被唤醒,减少中断唤醒深度wake_depth
的值;如果on
的值为非0
,函数将中断从睡眠状态唤醒,使中断处于唤醒状态,增加其唤醒深度wake_depth
的值。 - 输入参数说明:
此函数的参数irq
是int
型变量,代表操作中断对应的中断号,与数组irq_desc
中元素的下标相对应,根据它查找对应设备的中断服务例程,其取值范围是0~NR_IRQS-1
,其中NR_IRQS
的值是16640
。
参数on
是用来选择执行的程序序列,可取任意的整数,一般取1
、0
。 - 返回参数说明:
此函数的返回值是整数,代表执行的结果,可能的取值是-6
、0
。返回-6
说明当前中断号对应的irq_desc[]
数组中元素的chip字段的set_wake
字段的值为NULL
,此时函数的相应的功能无法实现,即函数既不改变中断唤醒的深度也不改变当前中断的状态。如果传递的第二个参数on
的值为0
,函数的返回结果是0
,有两种情况:- 当前中断的唤醒深度是
0
,其函数功能没有实现,作用与返回-6
相同; - 当前的唤醒深度是
1
,则其作用全部实现,即函数既减少中断的唤醒深度,也改变了当前中断的状态,使中断处于不可唤醒状态;如果传递的第二个参数on
的值为1
,函数返回结果是0
,说明函数既增加了中断的唤醒深度,也改变了当前中断的状态,使中断处于唤醒状态。
- 当前中断的唤醒深度是
7. enable_irq()
kernel/irq/manage.c
/**
* enable_irq - enable handling of an irq
* @irq: Interrupt to enable
*
* Undoes the effect of one call to disable_irq(). If this
* matches the last disable, processing of interrupts on this
* IRQ line is re-enabled.
*
* This function may be called from IRQ context only when
* desc->irq_data.chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
*/
void enable_irq(unsigned int irq)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
if (!desc)
return;
if (WARN(!desc->irq_data.chip,
KERN_ERR "enable_irq before setup/request_irq: irq %u\n", irq))
goto out;
__enable_irq(desc);
out:
irq_put_desc_busunlock(desc, flags);
}
EXPORT_SYMBOL(enable_irq);
void __enable_irq(struct irq_desc *desc)
{
switch (desc->depth) {
case 0:
err_out:
WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n",
irq_desc_get_irq(desc));
break;
case 1: {
if (desc->istate & IRQS_SUSPENDED)
goto err_out;
/* Prevent probing on this irq: */
irq_settings_set_noprobe(desc);
/*
* Call irq_startup() not irq_enable() here because the
* interrupt might be marked NOAUTOEN. So irq_startup()
* needs to be invoked when it gets enabled the first
* time. If it was already started up, then irq_startup()
* will invoke irq_enable() under the hood.
*/
irq_startup(desc, IRQ_RESEND, IRQ_START_FORCE);
break;
}
default:
desc->depth--;
}
}
- 函数功能描述:
函数enable_irq( )
在实现过程中调用了函数__enable_irq( )
,根据中断所处的深度和状态的不同,会有不同的执行结果,一般用于改变中断的状态,使中断处于唤醒状态,触发中断处理函数的执行及减少中断所处的深度,即改变字段depth
的值。 - 输入参数说明:
此函数的参数是int
型变量,代表操作中断对应的中断号,与数组irq_desc
中元素的下标相对应,根据它查找对应设备的中断服务例程,其取值范围是0~NR_IRQS-1
,其中NR_IRQS
的值是16640
。
8. enable_irq_wake()
include/linux/interrupt.h
static inline int enable_irq_wake(unsigned int irq)
{
return irq_set_irq_wake(irq, 1);
}
/**
* irq_set_irq_wake - control irq power management wakeup
* @irq: interrupt to control
* @on: enable/disable power management wakeup
*
* Enable/disable power management wakeup mode, which is
* disabled by default. Enables and disables must match,
* just as they match for non-wakeup mode support.
*
* Wakeup mode lets this IRQ wake the system from sleep
* states like "suspend to RAM".
*/
int irq_set_irq_wake(unsigned int irq, unsigned int on)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
int ret = 0;
if (!desc)
return -EINVAL;
/* wakeup-capable irqs can be shared between drivers that
* don't need to have the same sleep mode behaviors.
*/
if (on) {
if (desc->wake_depth++ == 0) {
ret = set_irq_wake_real(irq, on);
if (ret)
desc->wake_depth = 0;
else
irqd_set(&desc->irq_data, IRQD_WAKEUP_STATE);
}
} else {
if (desc->wake_depth == 0) {
WARN(1, "Unbalanced IRQ %d wake disable\n", irq);
} else if (--desc->wake_depth == 0) {
ret = set_irq_wake_real(irq, on);
if (ret)
desc->wake_depth = 1;
else
irqd_clear(&desc->irq_data, IRQD_WAKEUP_STATE);
}
}
irq_put_desc_busunlock(desc, flags);
return ret;
}
EXPORT_SYMBOL(irq_set_irq_wake);
- 函数功能描述:
函数enable_irq_wake( )
在实现过程中调用了函数irq_set_irq_wake( )
,在调用时传递的第二个参数是1
。enable_irq_wake( )
使中断能够从睡眠状态唤醒,增加其唤醒深度wake_depth
的值,并且改变当前的状态,使其处于唤醒状态。 - 输入参数说明:
此函数的参数是int
型变量,代表操作中断对应的中断号,与数组irq_desc
中元素的下标相对应,根据它查找对应设备的中断服务例程,其取值范围是0~NR_IRQS-1
,其中NR_IRQS
的值是16640
。 - 返回参数说明:
此函数的返回值是整数,代表执行的结果,可能的取值为-6
、0
。返回-6
说明当前中断号对应的irq_desc[]
数组中元素的chip
字段的set_wake
字段值为NULL
,此时函数的相应功能无法实现,即函数既不改变中断唤醒的深度也不改变当前中断的状态。对于函数enable_irq_wake( )
返回0
说明其功能实现,即既改变唤醒的深度也改变了当前中断的状态。
9. free_irq()
kernel/irq/manage.c
/**
* 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.
*
* Returns the devname argument passed to request_irq.
*/
const void *free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irqaction *action;
const char *devname;
if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return NULL;
#ifdef CONFIG_SMP
if (WARN_ON(desc->affinity_notify))
desc->affinity_notify = NULL;
#endif
action = __free_irq(desc, dev_id);
if (!action)
return NULL;
devname = action->name;
kfree(action);
return devname;
}
EXPORT_SYMBOL(free_irq);
/*
* Internal function to unregister an irqaction - used to free
* regular and special interrupts that are part of the architecture.
*/
static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
{
unsigned irq = desc->irq_data.irq;
struct irqaction *action, **action_ptr;
unsigned long flags;
WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
mutex_lock(&desc->request_mutex);
chip_bus_lock(desc);
raw_spin_lock_irqsave(&desc->lock, flags);
/*
* There can be multiple actions per IRQ descriptor, find the right
* one based on the dev_id:
*/
action_ptr = &desc->action;
for (;;) {
action = *action_ptr;
if (!action) {
WARN(1, "Trying to free already-free IRQ %d\n", irq);
raw_spin_unlock_irqrestore(&desc->lock, flags);
chip_bus_sync_unlock(desc);
mutex_unlock(&desc->request_mutex);
return NULL;
}
if (action->dev_id == dev_id)
break;
action_ptr = &action->next;
}
/* Found it - now remove it from the list of entries: */
*action_ptr = action->next;
irq_pm_remove_action(desc, action);
/* If this was the last handler, shut down the IRQ line: */
if (!desc->action) {
irq_settings_clr_disable_unlazy(desc);
/* Only shutdown. Deactivate after synchronize_hardirq() */
irq_shutdown(desc);
}
#ifdef CONFIG_SMP
/* make sure affinity_hint is cleaned up */
if (WARN_ON_ONCE(desc->affinity_hint))
desc->affinity_hint = NULL;
#endif
raw_spin_unlock_irqrestore(&desc->lock, flags);
/*
* Drop bus_lock here so the changes which were done in the chip
* callbacks above are synced out to the irq chips which hang
* behind a slow bus (I2C, SPI) before calling synchronize_hardirq().
*
* Aside of that the bus_lock can also be taken from the threaded
* handler in irq_finalize_oneshot() which results in a deadlock
* because kthread_stop() would wait forever for the thread to
* complete, which is blocked on the bus lock.
*
* The still held desc->request_mutex() protects against a
* concurrent request_irq() of this irq so the release of resources
* and timing data is properly serialized.
*/
chip_bus_sync_unlock(desc);
unregister_handler_proc(irq, action);
/*
* Make sure it's not being used on another CPU and if the chip
* supports it also make sure that there is no (not yet serviced)
* interrupt in flight at the hardware level.
*/
__synchronize_hardirq(desc, true);
#ifdef CONFIG_DEBUG_SHIRQ
/*
* It's a shared IRQ -- the driver ought to be prepared for an IRQ
* event to happen even now it's being freed, so let's make sure that
* is so by doing an extra call to the handler ....
*
* ( We do this after actually deregistering it, to make sure that a
* 'real' IRQ doesn't run in parallel with our fake. )
*/
if (action->flags & IRQF_SHARED) {
local_irq_save(flags);
action->handler(irq, dev_id);
local_irq_restore(flags);
}
#endif
/*
* The action has already been removed above, but the thread writes
* its oneshot mask bit when it completes. Though request_mutex is
* held across this which prevents __setup_irq() from handing out
* the same bit to a newly requested action.
*/
if (action->thread) {
kthread_stop(action->thread);
put_task_struct(action->thread);
if (action->secondary && action->secondary->thread) {
kthread_stop(action->secondary->thread);
put_task_struct(action->secondary->thread);
}
}
/* Last action releases resources */
if (!desc->action) {
/*
* Reaquire bus lock as irq_release_resources() might
* require it to deallocate resources over the slow bus.
*/
chip_bus_lock(desc);
/*
* There is no interrupt on the fly anymore. Deactivate it
* completely.
*/
raw_spin_lock_irqsave(&desc->lock, flags);
irq_domain_deactivate_irq(&desc->irq_data);
raw_spin_unlock_irqrestore(&desc->lock, flags);
irq_release_resources(desc);
chip_bus_sync_unlock(desc);
irq_remove_timings(desc);
}
mutex_unlock(&desc->request_mutex);
irq_chip_pm_put(&desc->irq_data);
module_put(desc->owner);
kfree(action->secondary);
return action;
}
- 函数功能描述:
此函数用于卸载IRQ链表中与输入参数相对应的irqaction
描述符,并释放其所占用的内存空间。
功能实现过程:首先调用函数__free_irq( )
,函数__free_irq( )
根据参数irq
找到数组irq_desc
中对应的元素desc
,如果不存在则返回NULL
,如果存在则根据dev
找到对应的irqaction
标识符,如果不存在对应的irqaction
标识符则返回NULL,如果存在则进行一定的操作,最后返回该irqaction
标识符。然后函数free_irq( )
调用函数kfree( )
释放该标识符所占用的空间。 - 输入参数说明:
- 参数
irq
是unsigned int
型变量,代表操作中断对应的中断号,与数组irq_desc
中元素的下标相对应,根据它查找对应设备的中断服务例程,其取值范围是0~NR_IRQS-1
,其中NR_IRQS
的值是16640
。 - 参数
void *dev
是对应的设备描述符,可能的取值是系统内所有已经存在的并且挂载在IRQ
链表中对应的设备,当设备不真实存在时可取NULL
。
- 参数
10. irq_set_chip()
kernel/irq/chip.c
/**
* irq_set_chip - set the irq chip for an irq
* @irq: irq number
* @chip: pointer to irq chip description structure
*/
int irq_set_chip(unsigned int irq, struct irq_chip *chip)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
if (!desc)
return -EINVAL;
if (!chip)
chip = &no_irq_chip;
desc->irq_data.chip = chip;
irq_put_desc_unlock(desc, flags);
/*
* For !CONFIG_SPARSE_IRQ make the irq show up in
* allocated_irqs.
*/
irq_mark_irq(irq);
return 0;
}
EXPORT_SYMBOL(irq_set_chip);
-
函数功能描述:
此函数是为irq_desc
数组中对应下标为irq
的元素设定irq_chip
的值,如果传入的参数chip
为NULL
,则使用系统定义好的no_irq_chip
为它赋值:如果传入的参数chip
不为NULL
,则用传入的参数赋值。在赋值之前函数会调用函数irq_chip_set_defaults( )
对传入的参数chip
进行相应的设置处理,处理完之后把参数chip
赋值给irq_desc
数组中的变量的irq_chip
。 -
输入参数说明:
- 参数
irq
是设备对应的中断编号,对应数组irq_desc
中元素的下标,此数组的大小为16640
。 - 参数
chip
是一个struct irq_chip
型的结构体变量,是对应的硬件中断描述符的irq_chip
字段的值,定义见文件include/linux/irq.h
如下:/** * struct irq_chip - hardware interrupt chip descriptor * * @parent_device: pointer to parent device for irqchip * @name: name for /proc/interrupts * @irq_startup: start up the interrupt (defaults to ->enable if NULL) * @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL) * @irq_enable: enable the interrupt (defaults to chip->unmask if NULL) * @irq_disable: disable the interrupt * @irq_ack: start of a new interrupt * @irq_mask: mask an interrupt source * @irq_mask_ack: ack and mask an interrupt source * @irq_unmask: unmask an interrupt source * @irq_eoi: end of interrupt * @irq_set_affinity: Set the CPU affinity on SMP machines. If the force * argument is true, it tells the driver to * unconditionally apply the affinity setting. Sanity * checks against the supplied affinity mask are not * required. This is used for CPU hotplug where the * target CPU is not yet set in the cpu_online_mask. * @irq_retrigger: resend an IRQ to the CPU * @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ * @irq_set_wake: enable/disable power-management wake-on of an IRQ * @irq_bus_lock: function to lock access to slow bus (i2c) chips * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips * @irq_cpu_online: configure an interrupt source for a secondary CPU * @irq_cpu_offline: un-configure an interrupt source for a secondary CPU * @irq_suspend: function called from core code on suspend once per * chip, when one or more interrupts are installed * @irq_resume: function called from core code on resume once per chip, * when one ore more interrupts are installed * @irq_pm_shutdown: function called from core code on shutdown once per chip * @irq_calc_mask: Optional function to set irq_data.mask for special cases * @irq_print_chip: optional to print special chip info in show_interrupts * @irq_request_resources: optional to request resources before calling * any other callback related to this irq * @irq_release_resources: optional to release resources acquired with * irq_request_resources * @irq_compose_msi_msg: optional to compose message content for MSI * @irq_write_msi_msg: optional to write message content for MSI * @irq_get_irqchip_state: return the internal state of an interrupt * @irq_set_irqchip_state: set the internal state of a interrupt * @irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine * @ipi_send_single: send a single IPI to destination cpus * @ipi_send_mask: send an IPI to destination cpus in cpumask * @flags: chip specific flags */ struct irq_chip { struct device *parent_device; const char *name; unsigned int (*irq_startup)(struct irq_data *data); void (*irq_shutdown)(struct irq_data *data); void (*irq_enable)(struct irq_data *data); void (*irq_disable)(struct irq_data *data); void (*irq_ack)(struct irq_data *data); void (*irq_mask)(struct irq_data *data); void (*irq_mask_ack)(struct irq_data *data); void (*irq_unmask)(struct irq_data *data); void (*irq_eoi)(struct irq_data *data); int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force); int (*irq_retrigger)(struct irq_data *data); int (*irq_set_type)(struct irq_data *data, unsigned int flow_type); int (*irq_set_wake)(struct irq_data *data, unsigned int on); void (*irq_bus_lock)(struct irq_data *data); void (*irq_bus_sync_unlock)(struct irq_data *data); void (*irq_cpu_online)(struct irq_data *data); void (*irq_cpu_offline)(struct irq_data *data); void (*irq_suspend)(struct irq_data *data); void (*irq_resume)(struct irq_data *data); void (*irq_pm_shutdown)(struct irq_data *data); void (*irq_calc_mask)(struct irq_data *data); void (*irq_print_chip)(struct irq_data *data, struct seq_file *p); int (*irq_request_resources)(struct irq_data *data); void (*irq_release_resources)(struct irq_data *data); void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg); void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg); int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state); int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state); int (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info); void (*ipi_send_single)(struct irq_data *data, unsigned int cpu); void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest); unsigned long flags; };
- 如果传入的参数
chip
为NULL
,则系统用no_irq_chip
进行初始化,no_irq_chip
的定义见文件kernel/irq/dummychip.c
,如下:
/* * Generic no controller implementation */ struct irq_chip no_irq_chip = { .name = "none", .irq_startup = noop_ret, .irq_shutdown = noop, .irq_enable = noop, .irq_disable = noop, .irq_ack = ack_bad, .flags = IRQCHIP_SKIP_SET_WAKE, };
- 如果传入的参数
- 参数
-
返回参数说明:
此函数的返回结果是int
型变量,可能的取值是0
、-22
,如果返回0
说明设置字段chip
的值成功,如果返回-22
说明设置字段chip
的值失败,设置失败的原因是与irq
对应的数组中的元素不存在。
11. irq_set_chip_data()
kernel/irq/chip.c
/**
* irq_set_chip_data - set irq chip data for an irq
* @irq: Interrupt number
* @data: Pointer to chip specific data
*
* Set the hardware irq chip data for an irq
*/
int irq_set_chip_data(unsigned int irq, void *data)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
if (!desc)
return -EINVAL;
desc->irq_data.chip_data = data;
irq_put_desc_unlock(desc, flags);
return 0;
}
EXPORT_SYMBOL(irq_set_chip_data);
-
函数功能描述:
此函数是为irq_desc
(结构体变量irq_desc
的定义参见文件include/linux/irqdesc.h
如上)数组中对应下标为irq
的元素设定字段chip_data
的值,chip_data
是一个void
类型的指针,那么其类型就是不确定的了,一般此字段代表提供给字段chip
中的函数的数据,供字段chip
中函数共享,即提供一个共享数据区。 -
输入参数说明:
- 参数
irq
是设备对应的中断号,对应数组irq_desc
中元素的下标,此数组的大小为16640
。 - 参数
data
对应的是一个函数,其目的是为irq_desc
数组中元素的chip
字段中的函数提供一个私有的数据区,以实现chip
字段中函数的共享执行。
- 参数
-
返回参数说明:
此函数的返回结果是int
型的变量,可能的取值是0
、-22
,如果返回0
说明设置字段chip_data
成功,如果返回-22
说明设置字段chip_data
失败,可能的原因有两个:- 参数
irq
超过数组irq_desc
的范围,数组越界; - 与参数
irq
对应的irq_desc
数组中的元素的chip
字段为NULL
。
- 参数
12. irq_set_irq_type()
kernel/irq/chip.c
/**
* irq_set_type - set the irq trigger type for an irq
* @irq: irq number
* @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h
*/
int irq_set_irq_type(unsigned int irq, unsigned int type)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
int ret = 0;
if (!desc)
return -EINVAL;
ret = __irq_set_trigger(desc, type);
irq_put_desc_busunlock(desc, flags);
return ret;
}
EXPORT_SYMBOL(irq_set_irq_type);
-
函数功能描述:
此函数用于设置中断处理函数触发的类型,被操作的中断描述符保存在数组irq_desc
中,对应的下标为参数irq
的值,设置的中断触发类型为参数type
所代表的类型。 -
输入参数说明:
- 参数
unsigned int irq
是对应的中断号,与数组irq_desc
的下标相对应,数组的大小为16640
。 - 参数
unsigned int type
是系统定义的中断触发类型,定义见文件include/linux/irq.h
,可能的取值定义如下:IRQ_TYPE_NONE 0x00000000
系统默认的没有明确指明类型的触发模式IRQ_TYPE_EDGE_RISING 0x00000001
上升沿触发IRQ_TYPE_EDGE_FALLING 0x00000002
下降沿触发IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)
上升沿下降沿两种触发都行IRQ_TYPE_LEVEL_HIGH 0x00000004
高电平触发IRQ_TYPE_LEVEL_LOW 0x00000008
低电平触发IRQ_TYPE_SENSE_MASK 0x0000000f
以上任何一种条件IRQ_TYPE_DEFAULT IRQ_TYPE_SENSE_MASK
以上任何一种条件IRQ_TYPE_PROBE 0x00000010
在进程中查询
- 参数
-
返回参数说明:
函数的返回结果是int
型变量,返回0
或者负数,如果返回结果是负数,说明设置中断触发类型失败;如果返回结果是0
,有四种情况:- 设置中断触发类型成功;
- 传入的中断触发类型参数是
IRQ_TYPE_NONE
,如果传入此参数,函数不会更改中断的触发类型; - 数组中与
irq
对应的元素的chip
字段的值为NULL
; - 数组中与
irq
对应的元素的chip
字段的值不为NULL
,但chip
字段的set_type
字段的值为NULL
。
13. remove_irq()
kernel/irq/manage.c
/**
* remove_irq - free an interrupt
* @irq: Interrupt line to free
* @act: irqaction for the interrupt
*
* Used to remove interrupts statically setup by the early boot process.
*/
void remove_irq(unsigned int irq, struct irqaction *act)
{
struct irq_desc *desc = irq_to_desc(irq);
if (desc && !WARN_ON(irq_settings_is_per_cpu_devid(desc)))
__free_irq(desc, act->dev_id);
}
EXPORT_SYMBOL_GPL(remove_irq);
-
函数功能描述:
此函数用于卸载IRQ
链表中的与输入参数相对应的irqaction
描述符。功能实现过程:函数通过调用函数
__free_irq( )
实现其功能,传给__free_irq( )
的参数是irq
与act->dec_id
,函数__free_irq( )
根据参数irq
找到数组irq_desc
中对应的元素desc
,如果不存在则返回NULL
;如果存在则根据dev_id
找到对应的irqaction
标识符,如果不存在则返回NULL
,如果存在则进行一定的操作,将其从IRQ
链表中删除,最后返回该irqaction
标识符。 -
输入参数说明:
- 参数
unsigned int irq
是对应的中断号,相应的取值是0~16640
,系统已用的是0~31
,其中编号IRQ9
、IRQ10
、IRQ15
系统保留,32~16640
是用于用户定义的中断。 - 参数act是与系统对应的一个
irqaction
标识符,是一个struct irqaction
类型的结构体指针,其定义见文件include/linux/interrupt.h
,如下:/** * struct irqaction - per interrupt action descriptor * @handler: interrupt handler function * @name: name of the device * @dev_id: cookie to identify the device * @percpu_dev_id: cookie to identify the device * @next: pointer to the next irqaction for shared interrupts * @irq: interrupt number * @flags: flags (see IRQF_* above) * @thread_fn: interrupt handler function for threaded interrupts * @thread: thread pointer for threaded interrupts * @secondary: pointer to secondary irqaction (force threading) * @thread_flags: flags related to @thread * @thread_mask: bitmask for keeping track of @thread activity * @dir: pointer to the proc/irq/NN/name entry */ struct irqaction { irq_handler_t handler; void *dev_id; void __percpu *percpu_dev_id; struct irqaction *next; irq_handler_t thread_fn; struct task_struct *thread; struct irqaction *secondary; unsigned int irq; unsigned int flags; unsigned long thread_flags; unsigned long thread_mask; const char *name; struct proc_dir_entry *dir; } ____cacheline_internodealigned_in_smp; extern irqreturn_t no_action(int cpl, void *dev_id);
- 字段
handler
是一个函数指针,代表中断处理函数,此函数的返回值类型是irq_handler_t
类型的变量,可能的取值是IRQ_NONE
、IRQ_HANDLED
、IRQ_WAKE_THREAD
。 - 字段
dev_id
保存与此中断标识符相对应的设备标识符,用于识别设备。 - 字段
percpu_dev_id
为设备识别符,用于识别设备。 - 字段
next
指向中断向量链表中的下一个中断标识符。 - 字段
thread_fn
是一个函数指针,指向中断线程处理函数,返回值类型与字段handler
的返回值类型相同。 - 字段
thread
是一个任务描述符指针,指向与此中断线程对应的线程。 - 字段
irq
对应此中断标识符对应的中断号。 - 字段
flags
是用来标识中断的类型,其定义见文件include/linux/interrupt.h
,可能的取值为:#define IRQF_DISABLED 0x00000020 //中断使能 #define IRQF_SHARED 0x00000080 //设备共享 #define IRQF_PROBE_SHARED 0x00000100 //错序共享中断 #define __IRQF_TIMER 0x00000200 //时钟中断 #define IRQF_PERCPU 0x00000400 //CPU中断 #define IRQF_NOBALANCING 0x00000800 //中断平衡使能 #define IRQF_IRQPOLL 0x00001000 //中断轮询检测,用于设备共享的中断 #define IRQF_ONESHOT 0x00002000 //将中断保持不可用状态,直到中断处理函数结束 #define IRQF_NO_SUSPEND 0x00004000 //挂起期间不让中断保持不可用状态 // 强制中断处于重新开始状态即使设置了IRQF_NO_SUSPEND状态 #define IRQF_FORCE_RESUME 0x00008000 #define IRQF_NO_THREAD 0x00010000 //不可中断线程状态 #define IRQF_EARLY_RESUME 0x00020000 //提前恢复IRQ而不是在设备恢复期间 #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) // 复合定义
- 字段
thread_f lags
是一个线程标志,标示一个线程所处的状态。 - 字段
thread_mask
是对应的CPU掩码,表示此中断所在的CPU编号。 - 字段
name
是与此中断标识符相对应的设备名。 - 字段
dir
是一个目录入口指针,指向在文件夹/proc/irq
中与此中断标识符所对应的中断号相对应的文件夹。
- 字段
- 参数
14. request_irq()
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_irq( )
在实现过程中调用了函数request_threaded_irq( )
,实现动态地申请注册一个中断。函数request_threaded_irq( )
首先对传入的参数进行安全检查,根据传入的irq
号获得数组irq_desc
中以irq
为下标的元素,然后动态地创建一个irqaction
描述符,根据传入的参数初始化新生成的irqaction
描述符,最后调用函数__setup_irq( )
把该描述符加入IRQ链表中,完成中断的动态申请及注册。 - 输入参数说明:
函数输入参数与函数request_threaded_irq( )
基本相同 - 返回参数说明:
如果返回值是0
则说明申请成功,如果申请不成功,则返回的值非零,一般为负数,可能的取值-16
、-38
,例如如果返回值是-16
,则说明申请的中断号在内核中已被占用。
15. request_threaded_irq( )
kernel/irq/manage.c
/**
* 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)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
if (irq == IRQ_NOTCONNECTED)
return -ENOTCONN;
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*
* Also IRQF_COND_SUSPEND only makes sense for shared interrupts and
* it cannot be set along with IRQF_NO_SUSPEND.
*/
if (((irqflags & IRQF_SHARED) && !dev_id) ||
(!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||
((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))
return -EINVAL;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
if (!handler) {
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
retval = irq_chip_pm_get(&desc->irq_data);
if (retval < 0) {
kfree(action);
return retval;
}
retval = __setup_irq(irq, desc, action);
if (retval) {
irq_chip_pm_put(&desc->irq_data);
kfree(action->secondary);
kfree(action);
}
#ifdef CONFIG_DEBUG_SHIRQ_FIXME
if (!retval && (irqflags & IRQF_SHARED)) {
/*
* It's a shared IRQ -- the driver ought to be prepared for it
* to happen immediately, so let's make sure....
* We disable the irq to make sure that a 'real' IRQ doesn't
* run in parallel with our fake.
*/
unsigned long flags;
disable_irq(irq);
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
enable_irq(irq);
}
#endif
return retval;
}
EXPORT_SYMBOL(request_threaded_irq);
-
函数功能描述:
函数request_threaded_irq( )
首先对传入的参数进行正确性检查,根据传入的irq
号获得数组irq_desc
中以irq
为下标的元素,然后动态地创建一个irqaction
描述符,根据传入的参数初始化新生成的irqaction
描述符,最后调用函数__setup_irq( )
把该描述符加入到IRQ链表中,完成中断的动态申请及注册。 -
输入参数说明:
- 参数
irq
是对应的中断号,相应的取值是0~16640
,系统已用的是0~31
,其中编号IRQ9
、IRQ10
、IRQ15
系统保留,32~16640
是用于用户定义的中断。 - 参数
handler
是对应的中断处理函数,返回值类型是irq_handler_t
,其定义见文件include/linux/irqreturn.h
。类型定义为typedef enum irqreturn irqreturn_t
,可能的值是:enum irqreturn { /*枚举类型*/ IRQ_NONE = (0<<0), //中断不是此设备发出 IRQ_HANDLED = (1<<0), //中断被此设备处理 IRQ_WAKE_THREAD = (1<<1), //中断处理函数需要唤醒中断处理线程 };
- 参数
thread_fn
是对应的中断线程处理函数,如果中断处理函数的返回值是IRQ_WAKE_THREAD
,则此时注册的中断线程处理函数将被调用,此函数是对中断处理函数的补充。 - 参数
flags
是用来标识中断的类型,其定义见文件include/linux/interrupt.h
,可能的取值为:#define IRQF_DISABLED 0x00000020 //中断使能 #define IRQF_SHARED 0x00000080 //设备共享 #define IRQF_PROBE_SHARED 0x00000100 //错序共享中断 #define __IRQF_TIMER 0x00000200 //时钟中断 #define IRQF_PERCPU 0x00000400 //CPU中断 #define IRQF_NOBALANCING 0x00000800 //中断平衡使能 #define IRQF_IRQPOLL 0x00001000 //中断轮询检测,用于设备共享的中断 #define IRQF_ONESHOT 0x00002000 //将中断保持不可用状态,直到中断处理函数结束 #define IRQF_NO_SUSPEND 0x00004000 //挂起期间不让中断保持不可用状态 // 强制中断处于重新开始状态即使设置了IRQF_NO_SUSPEND状态 #define IRQF_FORCE_RESUME 0x00008000 #define IRQF_NO_THREAD 0x00010000 //不可中断线程状态 #define IRQF_EARLY_RESUME 0x00020000 //提前恢复IRQ而不是在设备恢复期间 #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) // 复合定义
- 参数
name
是申请的中断对应的设备名称。 - 参数
dev
是对应的设备描述符指针,如果flags
的取值是IRQF_SHARED
,则dev
必须是真实存在的设备;如果是IRQF_DISABLED
,则dev
可以赋值为NULL
。
- 参数
-
返回参数说明:
如果返回值是0
则说明申请成功,如果申请不成功,则返回的值非零,一般为负数,可能的取值为-16
、-38
。例如,如果返回值是-16
,则说明申请的中断号在内核中已被占用。
16. setup_irq()
kernel/irq/manage.c
/**
* setup_irq - setup an interrupt
* @irq: Interrupt line to setup
* @act: irqaction for the interrupt
*
* Used to statically setup interrupts in the early boot process.
*/
int setup_irq(unsigned int irq, struct irqaction *act)
{
int retval;
struct irq_desc *desc = irq_to_desc(irq);
if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
retval = irq_chip_pm_get(&desc->irq_data);
if (retval < 0)
return retval;
retval = __setup_irq(irq, desc, act);
if (retval)
irq_chip_pm_put(&desc->irq_data);
return retval;
}
EXPORT_SYMBOL_GPL(setup_irq);
-
函数功能描述:
函数首先根据参数irq
找到数组irq_desc
中对应的元素,然后调用函数__setup_irq( )
把该描述符加入到IRQ
链表中。函数__setup_irq( )
首先对申请的中断进行正确性检查,如果满足相应的条件,则允许将新申请的中断加入IRQ链表,申请成功,返回0
;否则不允许,申请失败,返回一个非0常数,一般是负数。 -
输入参数说明:
-
参数
irq
是对应的中断号,相应的取值是0~16640
,系统已用的是0~31
,其中编号IRQ9
、IRQ10
、IRQ15
系统保留,32~16640
是用于用户定义的中断。 -
参数
new
是一个struct irqaction
类型的指针,与系统中的某一irqaction
标识符相对应,保存中断的相关信息,其具体定义及详细解释参考函数remove_irq( )
分析文档的输入参数说明部分。
-
-
返回参数说明:
此函数的返回值是int
型变量,可能的取值一般为0
或负数,如果返回0
,说明动态申请中断成功;如果返回值是负数,则表示动态申请中断失败,如返回值是-16
,则说明申请中断的中断号已经被占用。
17. tasklet_disable()
include/linux/interrupt.h
static inline void tasklet_disable(struct tasklet_struct *t)
{
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
smp_mb();
}
-
函数功能描述:
函数tasklet_disable( )
调用tasklet_disable_nosync( )
和tasklet_unlock_wait( )
函数,完成增加软中断描述符的count
字段的值,使软中断处于睡眠状态,不能响应对应的中断。 -
输入参数说明:
此函数的输入参数是struct tasklet_struct
结构体类型的指针变量,代表软中断的描述符信息,其定义及详细解释参考函数__tasklet_hi_schedule( )
分析文档的输入参数说明部分。
18. tasklet_disable_nosync()
include/linux/interrupt.h
static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{
atomic_inc(&t->count);
smp_mb__after_atomic();
}
- 函数功能描述:
函数tasklet_disable_nosync( )
通过增加taslet_struct
结构体变量中的count
字段的值,使此结构体描述的软中断不能被调度执行,使其处于睡眠状态。 - 输入参数说明:
此函数的输入参数是struct tasklet_struct
结构体类型的变量,保存一个软中断的描述符信息,其定义及详细解释参看函数__tasklet_hi_schedule( )
分析文档的输入参数说明部分。
19. tasklet_enable()
include/linux/interrupt.h
static inline void tasklet_enable(struct tasklet_struct *t)
{
smp_mb__before_atomic();
atomic_dec(&t->count);
}
-
函数功能描述:
函数tasklet_enable( )
用于减小结构体tasklet_struct
中字段count
的值,当此字段的值等于0
时,相应的软中断被重新使能,对应的中断处理函数能够被CPU调度执行,处理相应的中断。 -
输入参数说明:
此函数的输入参数是struct tasklet_struct
结构体类型的指针变量,代表软中断的描述符信息,其定义及详细解释参考函数__tasklet_hi_schedule( )
分析文档的输入参数说明部分。
20. tasklet_hi_schedule()
include/linux/interrupt.h
static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_hi_schedule(t);
}
-
函数功能描述:
tasklet_hi_schedule( )
函数是一个内联函数,其调用了函数__tasklet_hi_schedule( )
,完成检查当前软中断所处的状态,并改变其状态,使其处于被调度状态,然后将此tasklet
对应的软中断描述符加入到向量链表tasklet_hi_vec
的尾部,等待获得CPU资源,被调度执行。此函数在执行之前,有一个条件判断,只有传递的参数表示的软中断描述符没有被调度时,才能将其加入tasklet_hi_vec
中断向量链表。函数功能实现过程:首先通过检查
tasklet_struct
的state
的字段bit[0]
的值,并把其值设置为1
,返回其原值的“非”,如果其原值为0
,则返回1
,函数将调用函数__tasklet_hi_schedule( )
,把tasklet
对应的软中断描述符加入到向量tasklet_hi_vec
的尾部;如果其原值为1
,则返回0
,说明此tasklet
对应的代码断已经被添加到向量链表中,无法再次添加,函数将不进行任何操作而返回。通过此函数添加的软中断具有较高的优先级,会被优先响应处理。
-
输入参数说明:
此函数的输入参数是struct tasklet_struct
结构体类型的变量,保存一个软中断的描述符信息,其定义及详细解释参看函数__tasklet_hi_schedule( )
分析文档的输入参数说明部分。
21. tasklet_init()
kernel/softirq.c
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count, 0);
t->func = func;
t->data = data;
}
EXPORT_SYMBOL(tasklet_init);
- 函数功能描述:
函数tasklet_init( )
用于初始化一个struct tasklet_struct
结构体类型的变量,将其state
字段及count
字段的值都显示清零,用函数的第二个参数func
为结构体变量的func
字段赋值,用函数的第三个参数data为结构体变量的data字段赋值,完成对软中断描述符一些基本字段的初始化工作。 - 输入参数说明:
- 此函数的第一个参数是
struct tasklet_struct
结构体类型的变量,对应一个软中断,保存软中断的描述符信息,其定义及详细解释参考函数__tasklet_hi_schedule( )
分析文档的输入参数说明部分。 - 此函数的第二个参数是一个函数指针,代表软中断处理函数,用于给
tasklet_struct
结构体类型变量的func
字段赋值。 - 此函数的第三个参数代表传给软中断处理函数的参数,用于给
tasklet_struct
结构体类型变量的data
字段赋值。
- 此函数的第一个参数是
22. tasklet_kill()
kernel/softirq.c
void tasklet_kill(struct tasklet_struct *t)
{
if (in_interrupt())
pr_notice("Attempt to kill tasklet from interrupt\n");
while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
do {
yield();
} while (test_bit(TASKLET_STATE_SCHED, &t->state));
}
tasklet_unlock_wait(t);
clear_bit(TASKLET_STATE_SCHED, &t->state);
}
EXPORT_SYMBOL(tasklet_kill);
-
函数功能描述:
此函数用于阻塞当前线程,等待中断处理函数的执行完毕。此函数通过循环检测中断字段state
的值,判断中断处理函数的执行情况,当中断处理函数执行完毕之后,循环结束,然后将字段state
清零,函数返回。 -
输入参数说明:
此函数的输入参数是struct tasklet_struct
结构体类型的指针变量,代表软中断的描述符信息,其定义及详细解释参考函数__tasklet_hi_schedule( )
分析文档的输入参数说明部分。
23. tasklet_schedule()
include/linux/interrupt.h
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
-
函数功能描述:
此函数是一个内联函数,调用了函数__tasklet_schedule( )
。函数首先进行软中断状态的检查,如果当前中断没有被加入中断等待队列中,即没有被调度,则函数tasklet_schedule( )
更改中断的状态值,设置state字段的值为1
,即说明此中断被调度,加入中断等待队列中,然后调用函数__tasklet_schedule( )
将中断加入tasklet_vec
中断向量链表的尾部,等待获得CPU资源并被调度执行。与tasklet_hi_vec
相比,此中断向量链表是一个普通的中断向量链表,中断的优先级较低。 -
输入参数说明:
此函数的输入参数是struct tasklet_struct
结构体类型的指针变量,代表软中断的描述符信息,其定义及详细解释参考函数__tasklet_hi_schedule( )
分析文档的输入参数说明部分。
24. tasklet_trylock()
include/linux/interrupt.h
#ifdef CONFIG_SMP
static inline int tasklet_trylock(struct tasklet_struct *t)
{
return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
}
#else
#define tasklet_trylock(t) 1
#endif
-
函数功能描述:
函数tasklet_trylock( )
在实现过程中调用了函数test_and_set_bit( )
,此函数完成将参数tasklet_struct
结构体类型的变量的state
成员中的bit[1]
设置成1
,同时返回原bit[1]
的“非”。如果state
字段bit[1]
原值为1
(表示此中断已在另一个CPU上执行),则函数tasklet_trylock( )
返回0
,也就表示此中断不可在此CPU上调度执行;如果state
字段bit[1]
原值为0
,那么函数tasklet_trylock( )
将返回1
,表示此中断可在此CPU上调度执行。 -
输入参数说明:
此函数的输入参数是struct tasklet_struct
结构体类型的指针变量,代表软中断的描述符信息,其定义及详细解释参考函数__tasklet_hi_schedule( )
分析文档的输入参数说明部分。 -
返回参数说明:
函数tasklet_trylock ( )
的返回值是int
型的变量,可能的取值是0
和1
,与tasklet_struct
结构体变量中的state字段的bit[1]
的“非”相对应,如果bit[1]=1
,则函数返回0
,如果bit[1]=0
,则函数返回1
;返回0
表示此中断不可在此CPU上执行,返回1表示此中断可在此CPU上执行。
25. tasklet_unlock()
include/linux/interrupt.h
static inline void tasklet_unlock(struct tasklet_struct *t)
{
smp_mb__before_atomic();
clear_bit(TASKLET_STATE_RUN, &(t)->state);
}
-
函数功能描述:
函数tasklet_unlock( )
在实现中调用了函数clear_bit( )
,此函数用于对tasklet_struct
结构体变量中的state
字段的bit[1]
清零,即使中断处于可被CPU调度执行的状态。 -
输入参数说明:
此函数的输入参数是struct tasklet_struct
结构体类型的指针变量,代表软中断的描述符信息,其定义及详细解释参考函数__tasklet_hi_schedule( )
分析文档的输入参数说明部分。