内核中有很多申请中断的函数,例如devm_request_threaded_irq和request_irq,它们都是对进行了封装。
request_threaded_irq函数的原型:
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);
request_threaded_irq函数的参数解释如下:
unsigned int irq
: 这是要注册中断处理程序的 IRQ 线的编号。irq_handler_t handler
: 这是主处理程序,它将被内核调用以处理中断。irq_handler_t thread_fn
: 这是线程处理程序,它将在主处理程序被调用后由内核调度器在单独的线程中执行。这允许中断处理与其余的进程/线程并行运行。unsigned long irqflags
: 一组标志,用于配置中断处理的行为。例如,IRQF_SHARED
?意味着多个设备可以共享同一个 IRQ 线,而?IRQF_ONESHOT
?表示该 IRQ 只能触发一次。const char *devname
: 一个字符串,用于标识与该 IRQ 关联的设备。这主要用于调试目的。void *dev_id
: 一个指向数据的指针,该数据将被传递给主处理程序和线程处理程序。这对于需要区分多个共享同一 IRQ 线的设备的情况很有用。request_threaded_irq源码如下:
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) ||//如果设置了共享中断,dev_id不能为空
?? ? ? ?(!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||//共享中断和IRQF_COND_SUSPEND不能同时设置
?? ? ? ?((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))// IRQF_NO_SUSPEND和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)))//判断一个中断描述符是否需要传递 per-cpu 的 device ID。
?? ??? ?return -EINVAL;
?? ?if (!handler) {//判断中断处理函数是否为空
?? ??? ?if (!thread_fn)//中断处理函数如果为空,判断中断线程化处理函数是否为空
?? ??? ??? ?return -EINVAL;
?? ??? ?handler = irq_default_primary_handler;//如果中断线程化,且中断处理函数为空,则走默认的中断处理函数
?? ?}
?? ?action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);//分配一个struct irqaction空间
?? ?if (!action)
?? ??? ?return -ENOMEM;
??? //action赋值
????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);//Enable power for an IRQ chip
?? ?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;
}
?
从以上可以看出重点在__setup_irq的设置,其源码如下:
static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
?? ?struct irqaction *old, **old_ptr;
?? ?unsigned long flags, thread_mask = 0;
?? ?int ret, nested, shared = 0;
?? ?if (!desc)
?? ??? ?return -EINVAL;
?? ?if (desc->irq_data.chip == &no_irq_chip)
?? ??? ?return -ENOSYS;
?? ?if (!try_module_get(desc->owner))
?? ??? ?return -ENODEV;
?? ?new->irq = irq;
?? ?/*
?? ? * If the trigger type is not specified by the caller,
?? ? * then use the default for this interrupt.
?? ? */
?? ?if (!(new->flags & IRQF_TRIGGER_MASK))//判断中断触发类型是否在IRQF_TRIGGER_MASK,如不在,则使用默认的设置
?? ??? ?new->flags |= irqd_get_trigger_type(&desc->irq_data);
?? ?/*
?? ? * Check whether the interrupt nests into another interrupt
?? ? * thread.
?? ? */
?? ?nested = irq_settings_is_nested_thread(desc);//判断是否嵌套线程
?? ?if (nested) {
?? ??? ?if (!new->thread_fn) {
?? ??? ??? ?ret = -EINVAL;
?? ??? ??? ?goto out_mput;
?? ??? ?}
?? ??? ?/*
?? ??? ? * Replace the primary handler which was provided from
?? ??? ? * the driver for non nested interrupt handling by the
?? ??? ? * dummy function which warns when called.
?? ??? ? */
?? ??? ?new->handler = irq_nested_primary_handler;
?? ?} else {
?? ??? ?if (irq_settings_can_thread(desc)) {//判断中断设置是否线程化
?? ??? ??? ?ret = irq_setup_forced_threading(new);
?? ??? ??? ?if (ret)
?? ??? ??? ??? ?goto out_mput;
?? ??? ?}
?? ?}
?? ?/*
?? ? * Create a handler thread when a thread function is supplied
?? ? * and the interrupt does not nest into another interrupt
?? ? * thread.
?? ? */
?? ?if (new->thread_fn && !nested) {//如果线程化且没有嵌套,则走这里
?? ??? ?ret = setup_irq_thread(new, irq, false);//设置中断线程
?? ??? ?if (ret)
?? ??? ??? ?goto out_mput;
?? ??? ?if (new->secondary) {
?? ??? ??? ?ret = setup_irq_thread(new->secondary, irq, true);//设置中断线程
?? ??? ??? ?if (ret)
?? ??? ??? ??? ?goto out_thread;
?? ??? ?}
?? ?}
?? ?/*
?? ? * Drivers are often written to work w/o knowledge about the
?? ? * underlying irq chip implementation, so a request for a
?? ? * threaded irq without a primary hard irq context handler
?? ? * requires the ONESHOT flag to be set. Some irq chips like
?? ? * MSI based interrupts are per se one shot safe. Check the
?? ? * chip flags, so we can avoid the unmask dance at the end of
?? ? * the threaded handler for those.
?? ? */
?? ?if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)//中断描述符如果支持IRQCHIP_ONESHOT_SAFE,则需要清除IRQF_ONESHOT,IRQCHIP_ONESHOT_SAFE 是一个中断控制器的特定标志,用于指定该控制器是否支持硬件级别的 oneshot 中断。
?? ??? ?new->flags &= ~IRQF_ONESHOT;
?? ?/*
?? ? * Protects against a concurrent __free_irq() call which might wait
?? ? * for synchronize_irq() to complete without holding the optional
?? ? * chip bus lock and desc->lock.
?? ? */
?? ?mutex_lock(&desc->request_mutex);
?? ?/*
?? ? * Acquire bus lock as the irq_request_resources() callback below
?? ? * might rely on the serialization or the magic power management
?? ? * functions which are abusing the irq_bus_lock() callback,
?? ? */
?? ?chip_bus_lock(desc);
?? ?/* First installed action requests resources. */
?? ?if (!desc->action) {//判断中断描述符中的struct irqaction成员是否为空
?? ??? ?ret = irq_request_resources(desc);//用于分配和配置中断相关的资源
?? ??? ?if (ret) {
?? ??? ??? ?pr_err("Failed to request resources for %s (irq %d) on irqchip %s\n",
?? ??? ??? ? ? ? ? new->name, irq, desc->irq_data.chip->name);
?? ??? ??? ?goto out_bus_unlock;
?? ??? ?}
?? ?}
?? ?/*
?? ? * The following block of code has to be executed atomically
?? ? * protected against a concurrent interrupt and any of the other
?? ? * management calls which are not serialized via
?? ? * desc->request_mutex or the optional bus lock.
?? ? */
?? ?raw_spin_lock_irqsave(&desc->lock, flags);
?? ?old_ptr = &desc->action;
?? ?old = *old_ptr;
?? ?if (old) {
?? ??? ?/*
?? ??? ? * Can't share interrupts unless both agree to and are
?? ??? ? * the same type (level, edge, polarity). So both flag
?? ??? ? * fields must have IRQF_SHARED set and the bits which
?? ??? ? * set the trigger type must match. Also all must
?? ??? ? * agree on ONESHOT.
?? ??? ? */
?? ??? ?unsigned int oldtype;
?? ??? ?/*
?? ??? ? * If nobody did set the configuration before, inherit
?? ??? ? * the one provided by the requester.
?? ??? ? */
?? ??? ?if (irqd_trigger_type_was_set(&desc->irq_data)) {//中断触发类型如果被设置了
?? ??? ??? ?oldtype = irqd_get_trigger_type(&desc->irq_data);
?? ??? ?} else {//中断触发类型如果没有被设置
?? ??? ??? ?oldtype = new->flags & IRQF_TRIGGER_MASK;
?? ??? ??? ?irqd_set_trigger_type(&desc->irq_data, oldtype);//设置中断触发类型
?? ??? ?}
?? ??? ?if (!((old->flags & new->flags) & IRQF_SHARED) ||
?? ??? ? ? ?(oldtype != (new->flags & IRQF_TRIGGER_MASK)) ||
?? ??? ? ? ?((old->flags ^ new->flags) & IRQF_ONESHOT))
?? ??? ??? ?goto mismatch;
?? ??? ?/* All handlers must agree on per-cpuness */
?? ??? ?if ((old->flags & IRQF_PERCPU) !=
?? ??? ? ? ?(new->flags & IRQF_PERCPU))//IRQF_PERCPU用于指定中断是否与每个 CPU 关联。
?? ??? ??? ?goto mismatch;
?? ??? ?/* add new interrupt at end of irq queue */
?? ??? ?do {
?? ??? ??? ?/*
?? ??? ??? ? * Or all existing action->thread_mask bits,
?? ??? ??? ? * so we can find the next zero bit for this
?? ??? ??? ? * new action.
?? ??? ??? ? */
?? ??? ??? ?thread_mask |= old->thread_mask;
?? ??? ??? ?old_ptr = &old->next;
?? ??? ??? ?old = *old_ptr;
?? ??? ?} while (old);
?? ??? ?shared = 1;
?? ?}
?? ?/*
?? ? * Setup the thread mask for this irqaction for ONESHOT. For
?? ? * !ONESHOT irqs the thread mask is 0 so we can avoid a
?? ? * conditional in irq_wake_thread().
?? ? */
?? ?if (new->flags & IRQF_ONESHOT) {//是否设置了中断处理程序只被触发一次后立即禁用
?? ??? ?/*
?? ??? ? * Unlikely to have 32 resp 64 irqs sharing one line,
?? ??? ? * but who knows.
?? ??? ? */
?? ??? ?if (thread_mask == ~0UL) {
?? ??? ??? ?ret = -EBUSY;
?? ??? ??? ?goto out_unlock;
?? ??? ?}
?? ??? ?/*
?? ??? ? * The thread_mask for the action is or'ed to
?? ??? ? * desc->thread_active to indicate that the
?? ??? ? * IRQF_ONESHOT thread handler has been woken, but not
?? ??? ? * yet finished. The bit is cleared when a thread
?? ??? ? * completes. When all threads of a shared interrupt
?? ??? ? * line have completed desc->threads_active becomes
?? ??? ? * zero and the interrupt line is unmasked. See
?? ??? ? * handle.c:irq_wake_thread() for further information.
?? ??? ? *
?? ??? ? * If no thread is woken by primary (hard irq context)
?? ??? ? * interrupt handlers, then desc->threads_active is
?? ??? ? * also checked for zero to unmask the irq line in the
?? ??? ? * affected hard irq flow handlers
?? ??? ? * (handle_[fasteoi|level]_irq).
?? ??? ? *
?? ??? ? * The new action gets the first zero bit of
?? ??? ? * thread_mask assigned. See the loop above which or's
?? ??? ? * all existing action->thread_mask bits.
?? ??? ? */
?? ??? ?new->thread_mask = 1 << ffz(thread_mask);
?? ?} else if (new->handler == irq_default_primary_handler &&
?? ??? ? ? !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {
?? ??? ?/*
?? ??? ? * The interrupt was requested with handler = NULL, so
?? ??? ? * we use the default primary handler for it. But it
?? ??? ? * does not have the oneshot flag set. In combination
?? ??? ? * with level interrupts this is deadly, because the
?? ??? ? * default primary handler just wakes the thread, then
?? ??? ? * the irq lines is reenabled, but the device still
?? ??? ? * has the level irq asserted. Rinse and repeat....
?? ??? ? *
?? ??? ? * While this works for edge type interrupts, we play
?? ??? ? * it safe and reject unconditionally because we can't
?? ??? ? * say for sure which type this interrupt really
?? ??? ? * has. The type flags are unreliable as the
?? ??? ? * underlying chip implementation can override them.
?? ??? ? */
?? ??? ?pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",
?? ??? ? ? ? ? irq);
?? ??? ?ret = -EINVAL;
?? ??? ?goto out_unlock;
?? ?}
?? ?if (!shared) {//如果没有设置共享中断
?? ??? ?init_waitqueue_head(&desc->wait_for_threads);//初始化等待队列
?? ??? ?/* Setup the type (level, edge polarity) if configured: */
?? ??? ?if (new->flags & IRQF_TRIGGER_MASK) {
?? ??? ??? ?ret = __irq_set_trigger(desc,
?? ??? ??? ??? ??? ??? ?new->flags & IRQF_TRIGGER_MASK);//设置中断触发类型
?? ??? ??? ?if (ret)
?? ??? ??? ??? ?goto out_unlock;
?? ??? ?}
?? ??? ?desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \
?? ??? ??? ??? ? ?IRQS_ONESHOT | IRQS_WAITING);//IRQS_AUTODETECT用于自动检测设备的中断请求,IRQS_SPURIOUS_DISABLED 用于禁用中断控制器中的虚假中断处理,IRQS_WAITING用于表示中断正在等待处理。
?? ??? ?irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);//IRQD_IRQ_INPROGRESS用于指示当前中断处理程序是否正在执行。
?? ??? ?if (new->flags & IRQF_PERCPU) {
?? ??? ??? ?irqd_set(&desc->irq_data, IRQD_PER_CPU);
?? ??? ??? ?irq_settings_set_per_cpu(desc);
?? ??? ?}
?? ??? ?if (new->flags & IRQF_ONESHOT)//是否设置了中断处理程序只被触发一次后立即禁用
?? ??? ??? ?desc->istate |= IRQS_ONESHOT;
?? ??? ?/* Exclude IRQ from balancing if requested */
?? ??? ?if (new->flags & IRQF_NOBALANCING) {//中断处理程序不进行负载平衡
?? ??? ??? ?irq_settings_set_no_balancing(desc);
?? ??? ??? ?irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
?? ??? ?}
?? ??? ?if (irq_settings_can_autoenable(desc)) {//判断是否使能了自动使能
?? ??? ??? ?irq_startup(desc, IRQ_RESEND, IRQ_START_COND);
?? ??? ?} else {
?? ??? ??? ?/*
?? ??? ??? ? * Shared interrupts do not go well with disabling
?? ??? ??? ? * auto enable. The sharing interrupt might request
?? ??? ??? ? * it while it's still disabled and then wait for
?? ??? ??? ? * interrupts forever.
?? ??? ??? ? */
?? ??? ??? ?WARN_ON_ONCE(new->flags & IRQF_SHARED);//警告共享中断关闭了自动使能
?? ??? ??? ?/* Undo nested disables: */
?? ??? ??? ?desc->depth = 1;
?? ??? ?}
?? ?} else if (new->flags & IRQF_TRIGGER_MASK) {//中断触发类型正常
?? ??? ?unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK;
?? ??? ?unsigned int omsk = irqd_get_trigger_type(&desc->irq_data);
?? ??? ?if (nmsk != omsk)
?? ??? ??? ?/* hope the handler works with current ?trigger mode */
?? ??? ??? ?pr_warn("irq %d uses trigger mode %u; requested %u\n",
?? ??? ??? ??? ?irq, omsk, nmsk);
?? ?}
?? ?*old_ptr = new;//将新的action给中断描述符
?? ?irq_pm_install_action(desc, new);//上电中断
?? ?/* Reset broken irq detection when installing new handler */
?? ?desc->irq_count = 0;
?? ?desc->irqs_unhandled = 0;
?? ?/*
?? ? * Check whether we disabled the irq via the spurious handler
?? ? * before. Reenable it and give it another chance.
?? ? */
?? ?if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {
?? ??? ?desc->istate &= ~IRQS_SPURIOUS_DISABLED;
?? ??? ?__enable_irq(desc);//使能中断
?? ?}
?? ?raw_spin_unlock_irqrestore(&desc->lock, flags);
?? ?chip_bus_sync_unlock(desc);
?? ?mutex_unlock(&desc->request_mutex);
?? ?irq_setup_timings(desc, new);//设置中断控制器的时序参数。
?? ?/*
?? ? * Strictly no need to wake it up, but hung_task complains
?? ? * when no hard interrupt wakes the thread up.
?? ? */
?? ?if (new->thread)
?? ??? ?wake_up_process(new->thread);
?? ?if (new->secondary)
?? ??? ?wake_up_process(new->secondary->thread);
?? ?register_irq_proc(irq, desc);//注册到proc文件系统中
?? ?irq_add_debugfs_entry(irq, desc);//添加到debugfs文件系统中
?? ?new->dir = NULL;
?? ?register_handler_proc(irq, new);//注册到proc文件系统中
?? ?return 0;
mismatch://错误处理
?? ?if (!(new->flags & IRQF_PROBE_SHARED)) {
?? ??? ?pr_err("Flags mismatch irq %d. %08x (%s) vs. %08x (%s)\n",
?? ??? ? ? ? ? irq, new->flags, new->name, old->flags, old->name);
#ifdef CONFIG_DEBUG_SHIRQ
?? ??? ?dump_stack();
#endif
?? ?}
?? ?ret = -EBUSY;
out_unlock:
?? ?raw_spin_unlock_irqrestore(&desc->lock, flags);
?? ?if (!desc->action)
?? ??? ?irq_release_resources(desc);
out_bus_unlock:
?? ?chip_bus_sync_unlock(desc);
?? ?mutex_unlock(&desc->request_mutex);
out_thread:
?? ?if (new->thread) {
?? ??? ?struct task_struct *t = new->thread;
?? ??? ?new->thread = NULL;
?? ??? ?kthread_stop(t);
?? ??? ?put_task_struct(t);
?? ?}
?? ?if (new->secondary && new->secondary->thread) {
?? ??? ?struct task_struct *t = new->secondary->thread;
?? ??? ?new->secondary->thread = NULL;
?? ??? ?kthread_stop(t);
?? ??? ?put_task_struct(t);
?? ?}
out_mput:
?? ?module_put(desc->owner);
?? ?return ret;
}