第10章:中断处理-3: Autodetecting the IRQ Number

In continuation of the previous text第10章:中断处理-2: The /proc Interface let's GO ahead.

Autodetecting the IRQ Number

One of the most challenging problems for a driver at initialization time can be how to determine which IRQ line is going to be used by the device. The driver needs the information in order to correctly install the handler. Even though a programmer could require the user to specify the interrupt number at load time, this is a bad practice, because most of the time the user doesn’t know the number, either because he didn’t configure the jumpers or because the device is jumperless. Most users want their hardware to “just work” and are not interested in issues like interrupt numbers. So autodetection of the interrupt number is a basic requirement for driver
usability.

驱动在初始化时面临的最具挑战性的问题之一,就是确定设备将要使用的中断线(IRQ 号)。驱动需要这一信息才能正确安装中断处理程序。尽管开发者可以要求用户在加载模块时指定中断号,但这是一种糟糕的做法 —— 因为大多数情况下,用户也不知道这个号码(可能是因为没有配置跳线,或者设备本身无跳线设计)。大多数用户希望硬件 “即插即用”,对中断号这类细节不感兴趣。因此,自动检测中断号是保证驱动可用性的基本要求。

Sometimes autodetection depends on the knowledge that some devices feature a default behavior that rarely, if ever, changes. In this case, the driver might assume that the default values apply. This is exactly how short behaves by default with the parallel port. The implementation is straightforward, as shown by short itself:

有时,自动检测依赖于设备的默认行为(这种行为很少改变)。在这种情况下,驱动可以假设默认值适用。short 模块对并行端口的处理正是如此,其实现非常直接:

if (short_irq < 0) /* not yet specified: force the default on */
    switch(short_base) {
        case 0x378: short_irq = 7; break;
        case 0x278: short_irq = 2; break;
        case 0x3bc: short_irq = 5; break;
    }
}

The code assigns the interrupt number according to the chosen base I/O address,
while allowing the user to override the default at load time with something like:

代码根据所选的 I/O 基地址分配中断号,同时允许用户在加载模块时通过以下方式覆盖默认值:

insmod ./short.ko irq=x

short_base defaults to 0x378, so short_irq defaults to 7.

short_base 的默认值为 0x378,因此 short_irq 的默认值为 7。

Some devices are more advanced in design and simply “announce” which interrupt they’re going to use. In this case, the driver retrieves the interrupt number by reading a status byte from one of the device’s I/O ports or PCI configuration space. When the target device is one that has the ability to tell the driver which interrupt it is going to use, autodetecting the IRQ number just means probing the device, with no additional work required to probe the interrupt. Most modern hardware works this way, fortunately; for example, the PCI standard solves the problem by requiring peripheral devices to declare what interrupt line(s) they are going to use. The PCI
standard is discussed in Chapter 12.

有些设备设计更先进,会直接 “宣告” 自己将要使用的中断。这种情况下,驱动只需从设备的某个 I/O 端口或 PCI 配置空间读取状态字节,即可获取中断号。对于这类能主动告知中断号的设备,自动检测只需探测设备本身,无需额外的中断探测工作。幸运的是,大多数现代硬件都采用这种方式 —— 例如,PCI 标准要求外设声明自己将要使用的中断线,这一标准将在第 12 章讨论。

Unfortunately, not every device is programmer friendly, and autodetection might require some probing. The technique is quite simple: the driver tells the device to generate interrupts and watches what happens. If everything goes well, only one interrupt line is activated.

Although probing is simple in theory, the actual implementation might be unclear. We look at two ways to perform the task: calling kernel-defined helper functions and implementing our own version.

遗憾的是,并非所有设备都如此 “友好”,自动检测可能需要主动探测。探测的原理很简单:驱动让设备生成中断,然后观察发生的情况。如果一切顺利,只会有一条中断线被触发。

尽管探测在理论上很简单,但实际实现可能并不直观。我们将介绍两种实现方式:调用内核定义的辅助函数,以及自行实现探测逻辑。

Kernel-assisted probing

The Linux kernel offers a low-level facility for probing the interrupt number. It works for only nonshared interrupts, but most hardware that is capable of working in a shared interrupt mode provides better ways of finding the configured interrupt number anyway. The facility consists of two functions, declared in <linux/interrupt.h> (which also describes the probing machinery):

Linux 内核提供了一套底层工具用于探测中断号,该工具仅适用于非共享中断。不过,大多数支持共享中断模式的硬件,本身就提供了更优的方式来确定已配置的中断号。这套工具包含两个函数,声明于 <linux/interrupt.h>(该头文件也描述了探测机制):

unsigned long probe_irq_on(void);

This function returns a bit mask of unassigned interrupts. The driver must preserve the returned bit mask, and pass it to probe_irq_off later. After this call, the driver should arrange for its device to generate at least one interrupt.

该函数返回未分配中断的位掩码。驱动必须保存返回的位掩码,并在后续传给 probe_irq_off。调用此函数后,驱动应安排其设备生成至少一次中断。

int probe_irq_off(unsigned long);

After the device has requested an interrupt, the driver calls this function, passing as its argument the bit mask previously returned by probe_irq_on. probe_irq_off returns the number of the interrupt that was issued after “probe_on.” If no interrupts occurred, 0 is returned (therefore, IRQ 0 can’t be probed for, but no custom device can use it on any of the supported architectures anyway). If more than one interrupt occurred (ambiguous detection), probe_irq_off returns a negative value.

在设备请求中断后,驱动调用此函数,并将之前 probe_irq_on 返回的位掩码作为参数传入。probe_irq_off 会返回在 “probe_on 之后” 触发的中断号。若未发生任何中断,返回 0(因此无法探测 IRQ 0,但在所有受支持的架构中,自定义设备均无法使用 IRQ 0);若发生多个中断(探测结果模糊),则返回负值。

The programmer should be careful to enable interrupts on the device after the call to probe_irq_on and to disable them before calling probe_irq_off. Additionally, you must remember to service the pending interrupt in your device after probe_irq_off. The short module demonstrates how to use such probing. If you load the module with probe=1, the following code is executed to detect your interrupt line, provided pins 9 and 10 of the parallel connector are bound together:

开发者需注意:应在调用 probe_irq_on 之后启用设备中断,并在调用 probe_irq_off 之前禁用中断。此外,必须记住在 probe_irq_off 之后处理设备中未完成的中断。short 模块演示了如何使用这种探测方式:若以 probe=1 加载模块,且并行端口连接器的 9 号与 10 号引脚已短接,将执行以下代码来探测中断线:

int count = 0;
do {
    unsigned long mask;
    mask = probe_irq_on( );
    outb_p(0x10,short_base+2); /* enable reporting */
    outb_p(0x00,short_base);
    /* clear the bit */
    outb_p(0xFF,short_base);
    /* set the bit: interrupt! */
    outb_p(0x00,short_base+2); /* disable reporting */
    udelay(5); /* give it some time */
    short_irq = probe_irq_off(mask);
    if (short_irq = = 0) { /* none of them? */
    printk(KERN_INFO "short: no irq reported by probe\n");
    short_irq = -1;
}
/*
* if more than one line has been activated, the result is
* negative. We should service the interrupt (no need for lpt port)
* and loop over again. Loop at most five times, then give up
*/
} while (short_irq < 0 && count++ < 5);
if (short_irq < 0)
    printk("short: probe failed %i times, giving up\n", count);

Note the use of udelay before calling probe_irq_off. Depending on the speed of your processor, you may have to wait for a brief period to give the interrupt time to actually be delivered.

注意在调用 probe_irq_off 前使用了 udelay。根据处理器速度,可能需要等待一小段时间,确保中断实际被送达。

Probing might be a lengthy task. While this is not true for short, probing a frame grabber, for example, requires a delay of at least 20 ms (which is ages for the processor), and other devices might take even longer. Therefore, it’s best to probe for the interrupt line only once, at module initialization, independently of whether you install the handler at device open (as you should) or within the initialization function (which is not recommended).

It’s interesting to note that on some platforms (PowerPC, M68k, most MIPS implementations, and both SPARC versions) probing is unnecessary, and, therefore, the previous functions are just empty placeholders, sometimes called “useless ISA nonsense.” On other platforms, probing is implemented only for ISA devices. Anyway, most architectures define the functions (even if they are empty) to ease porting existing device drivers.

探测可能是一项耗时的任务。尽管 short 模块的探测很快,但例如探测帧捕获器时,至少需要 20 毫秒的延迟(对处理器而言这是很长的时间),其他设备可能需要更长时间。因此,最好仅在模块初始化时执行一次中断线探测 —— 无论中断处理程序是在设备打开时安装(推荐做法),还是在初始化函数中安装(不推荐做法)。

有趣的是,在某些平台(PowerPC、M68k、大多数 MIPS 实现以及两种 SPARC 版本)上,探测是不必要的,因此上述函数只是空占位符,有时被称为 “无用的 ISA 冗余代码”。在其他平台上,探测仅针对 ISA 设备实现。无论如何,大多数架构都会定义这些函数(即使为空),以简化现有设备驱动的移植工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值