1.How-to
request_threaded_irq()
{
action->handler = handler;
action->thread_fn = thread_fn;
->__setup_irq()
{
/*
* 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) {
struct task_struct *t;
t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
new->name);
adb shell ps -P -p
可以看到创建的 irq thread:
USER PID PPID VSIZE RSS PRIO NICE RTPRI SCHED PCY WCHAN PC NAME
root 57 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/102-msm_iom
root 58 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/102-msm_iom
root 59 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/102-msm_iom
root 60 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/79-msm_iomm
root 61 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/78-msm_iomm
root 62 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/78-msm_iomm
root 63 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/74-msm_iomm
root 64 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/75-msm_iomm
root 65 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/75-msm_iomm
root 66 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/273-msm_iom
root 67 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/273-msm_iom
root 68 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/273-msm_iom
root 69 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/97-msm_iomm
root 70 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/97-msm_iomm
root 71 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/97-msm_iomm
root 77 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/402-mba
root 78 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/530-wcnss
root 134 2 0 0 -51 0 50 1 fg c0100ac8 00000000 S irq/335-main_tt
2.什么时候执行
产生中断之后先执行中断对应的 hard handler, 之后根据hard handler中断服务程序是否返回 IRQ_WAKE_THREAD flag决定是否执行中断对应的thread_fn。
(下半部tasklet 也是在hard handler 中断服务程序中执行tasklet_schedule()触发的)
执行流程是下面这个样子的:
handle_irq_event_percpu()
->
trace_irq_handler_entry(irq, action);
res = action->handler(irq, action->dev_id);
trace_irq_handler_exit(irq, action, res);
->
switch (res) {
case IRQ_WAKE_THREAD:
/*
* Catch drivers which return WAKE_THREAD but
* did not set up a thread function
*/
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
}
irq_wake_thread(desc, action);
->static void irq_wake_thread(struct irq_desc *desc, struct irqaction *action)
{
wake_up_process(action->thread);
->
/*
* Interrupt handler thread
*/
static int irq_thread(void *data)
{
static const struct sched_param param = {
.sched_priority = MAX_USER_RT_PRIO/2,
};
sched_setscheduler(current, SCHED_FIFO, ¶m);//irq thread 优先级比ksoftirqd/N 高很多
->
ret = action->thread_fn(action->irq, action->dev_id);
3. 一个threaded irq 典型的例子
static irqreturn_t ads7846_hard_irq(int irq, void *handle)
{
struct ads7846 *ts = handle;
return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
}
static irqreturn_t ads7846_irq(int irq, void *handle)
{
struct ads7846 *ts = handle;
/* Start with a small delay before checking pendown state */
msleep(TS_POLL_DELAY);
while (!ts->stopped && get_pendown_state(ts)) {
/* pen is down, continue with the measurement */
ads7846_read_state(ts);
if (!ts->stopped)
ads7846_report_state(ts);
wait_event_timeout(ts->wait, ts->stopped,
msecs_to_jiffies(TS_POLL_PERIOD));
}
if (ts->pendown) {
struct input_dev *input = ts->input;
input_report_key(input, BTN_TOUCH, 0);
input_report_abs(input, ABS_PRESSURE, 0);
input_sync(input);
ts->pendown = false;
dev_vdbg(&ts->spi->dev, "UP\n");
}
return IRQ_HANDLED;
}
static int __devinit ads7846_probe(struct spi_device *spi)
{
err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq,
irq_flags, spi->dev.driver->name, ts);
这个驱动既申请了 hard handler(ads7846_hard_irq) 也申请了thread_fn(ads7846_irq)。