Linux内核活动之中断API

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( )传递的第二个参数是0disable_irq_wake( )使中断处于不可唤醒的中断状态,减少其唤醒深度wake_depth的值。

  • 输入参数说明:
    此函数的参数是int型变量,代表操作中断对应的中断号,与数组irq_desc中元素的下标相对应,根据它查找对应设备的中断服务例程,其取值范围是0~NR_IRQS-1,其中NR_IRQS的值是16640

  • 返回参数说明:
    此函数的返回值是整数,代表执行的结果,可能的取值是-60。返回-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的值。
  • 输入参数说明:
    此函数的参数irqint型变量,代表操作中断对应的中断号,与数组irq_desc中元素的下标相对应,根据它查找对应设备的中断服务例程,其取值范围是0~NR_IRQS-1,其中NR_IRQS的值是16640
    参数on是用来选择执行的程序序列,可取任意的整数,一般取10
  • 返回参数说明:
    此函数的返回值是整数,代表执行的结果,可能的取值是-60。返回-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( ),在调用时传递的第二个参数是1enable_irq_wake( )使中断能够从睡眠状态唤醒,增加其唤醒深度wake_depth的值,并且改变当前的状态,使其处于唤醒状态。
  • 输入参数说明:
    此函数的参数是int型变量,代表操作中断对应的中断号,与数组irq_desc中元素的下标相对应,根据它查找对应设备的中断服务例程,其取值范围是0~NR_IRQS-1,其中NR_IRQS的值是16640
  • 返回参数说明:
    此函数的返回值是整数,代表执行的结果,可能的取值为-60。返回-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( )释放该标识符所占用的空间。
  • 输入参数说明:
    • 参数irqunsigned 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的值,如果传入的参数chipNULL,则使用系统定义好的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;
      };
      
      • 如果传入的参数chipNULL,则系统用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( )的参数是irqact->dec_id,函数__free_irq( )根据参数irq找到数组irq_desc中对应的元素desc,如果不存在则返回NULL;如果存在则根据dev_id找到对应的irqaction标识符,如果不存在则返回NULL,如果存在则进行一定的操作,将其从IRQ链表中删除,最后返回该irqaction标识符。

  • 输入参数说明:

    • 参数unsigned int irq是对应的中断号,相应的取值是0~16640,系统已用的是0~31,其中编号IRQ9IRQ10IRQ15系统保留,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_NONEIRQ_HANDLEDIRQ_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,其中编号IRQ9IRQ10IRQ15系统保留,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,其中编号IRQ9IRQ10IRQ15系统保留,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_structstate的字段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型的变量,可能的取值是01,与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( )分析文档的输入参数说明部分。

refer to

Linux内核中断机制API

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值