Linux Device Drivers 2nd Edition (779877), страница 62
Текст из файла (страница 62)
We’ll see a practicaluse for dev_id in “Implementing a Handler,” later in this chapter.The bits that can be set in flags are as follows:SA_INTERRUPTWhen set, this indicates a ‘‘fast’’ interrupt handler. Fast handlers are executedwith interrupts disabled (the topic is covered in deeper detail later in thischapter, in “Fast and Slow Handlers”).25422 June 2001 16:39http://openlib.org.uaInstalling an Interrupt HandlerSA_SHIRQThis bit signals that the interrupt can be shared between devices. The conceptof sharing is outlined in “Interrupt Sharing,” later in this chapter.SA_SAMPLE_RANDOMThis bit indicates that the generated interrupts can contribute to the entropypool used by /dev/random and /dev/urandom. These devices return truly random numbers when read and are designed to help application softwarechoose secure keys for encryption. Such random numbers are extracted froman entropy pool that is contributed by various random events.
If your devicegenerates interrupts at truly random times, you should set this flag. If, on theother hand, your interrupts will be predictable (for example, vertical blankingof a frame grabber), the flag is not worth setting—it wouldn’t contribute tosystem entropy anyway. Devices that could be influenced by attackers shouldnot set this flag; for example, network drivers can be subjected to predictablepacket timing from outside and should not contribute to the entropy pool. Seethe comments in drivers/char/random.c for more information.The interrupt handler can be installed either at driver initialization or when thedevice is first opened. Although installing the interrupt handler from within themodule’s initialization function might sound like a good idea, it actually isn’t.Because the number of interrupt lines is limited, you don’t want to waste them.You can easily end up with more devices in your computer than there are interrupts.
If a module requests an IRQ at initialization, it prevents any other driverfrom using the interrupt, even if the device holding it is never used. Requestingthe interrupt at device open, on the other hand, allows some sharing of resources.It is possible, for example, to run a frame grabber on the same interrupt as amodem, as long as you don’t use the two devices at the same time. It is quitecommon for users to load the module for a special device at system boot, even ifthe device is rarely used.
A data acquisition gadget might use the same interrupt asthe second serial port. While it’s not too hard to avoid connecting to your Internetservice provider (ISP) during data acquisition, being forced to unload a module inorder to use the modem is really unpleasant.The correct place to call request_irq is when the device is first opened, befor e thehardware is instructed to generate interrupts. The place to call fr ee_irq is the lasttime the device is closed, after the hardware is told not to interrupt the processorany more. The disadvantage of this technique is that you need to keep a perdevice open count. Using the module count isn’t enough if you control two ormore devices from the same module.This discussion notwithstanding, short requests its interrupt line at load time.
Thiswas done so that you can run the test programs without having to run an extraprocess to keep the device open. short, therefore, requests the interrupt fromwithin its initialization function (short_init) instead of doing it in short_open, as areal device driver would.25522 June 2001 16:39http://openlib.org.uaChapter 9: Interrupt HandlingThe interrupt requested by the following code is short_irq.
The actual assignment of the variable (i.e., determining which IRQ to use) is shown later, since it isnot relevant to the current discussion. short_base is the base I/O address of theparallel interface being used; register 2 of the interface is written to enable interrupt reporting.if (short_irq >= 0) {result = request_irq(short_irq, short_interrupt,SA_INTERRUPT, "short", NULL);if (result) {printk(KERN_INFO "short: can’t get assigned irq %i\n",short_irq);short_irq = -1;}else { /* actually enable it -- assume this *is* a parallel port */outb(0x10,short_base+2);}}The code shows that the handler being installed is a fast handler (SA_INTERRUPT), does not support interrupt sharing (SA_SHIRQ is missing), and doesn’tcontribute to system entropy (SA_SAMPLE_RANDOM is missing too).
The outb callthen enables interrupt reporting for the parallel port.The /proc InterfaceWhenever a hardware interrupt reaches the processor, an internal counter is incremented, providing a way to check whether the device is working as expected.Reported interrupts are shown in /pr oc/interrupts. The following snapshot wastaken after several days of uptime on a two-processor Pentium system:0:1:2:5:9:10:12:13:15:NMI:LOC:ERR:CPU0345843232244070563675105659108890911175966969520392695137170CPU134936135226473056366660565269884276017345206952039269513716IO-APIC-edgeIO-APIC-edgeXT-PICIO-APIC-levelIO-APIC-levelIO-APIC-levelIO-APIC-edgeXT-PICIO-APIC-edgetimerkeyboardcascadeeth0acpiaic7xxxPS/2 Mousefpuide1The first column is the IRQ number. You can see from the IRQs that are missingthat the file shows only interrupts corresponding to installed handlers. For example, the first serial port (which uses interrupt number 4) is not shown, indicating25622 June 2001 16:39http://openlib.org.uaInstalling an Interrupt Handlerthat the modem isn’t being used.
In fact, even if the modem had been used earlierbut wasn’t in use at the time of the snapshot, it would not show up in the file; theserial ports are well behaved and release their interrupt handlers when the deviceis closed.The /pr oc/interrupts display shows how many interrupts have been delivered toeach CPU on the system. As you can see from the output, the Linux kernel tries todivide interrupt traffic evenly across the processors, with some success. The finalcolumns give information on the programmable interrupt controller that handlesthe interrupt (and which a driver writer need not worry about), and the name(s) ofthe device(s) that have registered handlers for the interrupt (as specified in thedev_name argument to request_irq).The /pr oc tree contains another interrupt-related file, /pr oc/stat; sometimes you’llfind one file more useful and sometimes you’ll prefer the other.
/pr oc/stat recordsseveral low-level statistics about system activity, including (but not limited to) thenumber of interrupts received since system boot. Each line of stat begins with atext string that is the key to the line; the intr mark is what we are looking for.The following (truncated and line-broken) snapshot was taken shortly after theprevious one:intr 884865 695557 4527 0 3109 4907 112759 3 0 0 0 113140 17747 1 0 34941 0 0 0 0 0 0 0The first number is the total of all interrupts, while each of the others represents asingle IRQ line, starting with interrupt 0. This snapshot shows that interrupt number 4 has been used 4907 times, even though no handler is curr ently installed.
Ifthe driver you’re testing acquires and releases the interrupt at each open and closecycle, you may find /pr oc/stat more useful than /pr oc/interrupts.Another difference between the two files is that interrupts is not architecturedependent, whereas stat is: the number of fields depends on the hardware underlying the kernel.
The number of available interrupts varies from as few as 15 onthe SPARC to as many as 256 on the IA-64 and a few other systems. It’s interestingto note that the number of interrupts defined on the x86 is currently 224, not 16 asyou may expect; this, as explained in include/asm-i386/irq.h, depends on Linuxusing the architectural limit instead of an implementation-specific limit (like the 16interrupt sources of the old-fashioned PC interrupt controller).The following is a snapshot of /pr oc/interrupts taken on an IA-64 system.
As youcan see, besides different hardware routing of common interrupt sources, there’sno platform dependency here.27:40:43:47:64:CPU017050913267223CPU134141069601466IO-SAPIC-levelSAPICIO-SAPIC-levelIO-SAPIC-levelIO-SAPIC-edgeqla1280perfmoneth0usb-uhciide025722 June 2001 16:39http://openlib.org.uaChapter 9: Interrupt Handling80:89:239:254:NMI:ERR:4056063416757500205606052528150IO-SAPIC-edgeIO-SAPIC-edgeSAPICSAPICkeyboardPS/2 MousetimerIPIAutodetecting the IRQ NumberOne of the most compelling problems for a driver at initialization time can be howto determine which IRQ line is going to be used by the device.
The driver needsthe 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 abad practice because most of the time the user doesn’t know the number, eitherbecause he didn’t configure the jumpers or because the device is jumperless.Autodetection of the interrupt number is a basic requirement for driver usability.Sometimes autodetection depends on the knowledge that some devices feature adefault behavior that rarely, if ever, changes.
In this case, the driver might assumethat the default values apply. This is exactly how short behaves by default with theparallel port. The implementation is straightforward, as shown by short itself:if (short_irq < 0) /* not yetswitch(short_base) {case 0x378: short_irq =case 0x278: short_irq =case 0x3bc: short_irq =}specified: force the default on */7; break;2; break;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 likeinsmod ./short.o short_irq=x.short_base defaults to 0x378, so short_irq defaults to 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 byreading a status byte from one of the device’s I/O ports or PCI configurationspace. When the target device is one that has the ability to tell the driver whichinterrupt it is going to use, autodetecting the IRQ number just means probing thedevice, with no additional work required to probe the interrupt.It’s interesting to note here that modern devices supply their interrupt configuration. The PCI standard solves the problem by requiring peripheral devices todeclare what interrupt line(s) they are going to use. The PCI standard is discussedin Chapter 15.25822 June 2001 16:39http://openlib.org.uaInstalling an Interrupt HandlerUnfortunately, not every device is programmer friendly, and autodetection mightrequire some probing.
The technique is quite simple: the driver tells the device togenerate interrupts and watches what happens. If everything goes well, only oneinterrupt line is activated.Though probing is simple in theory, the actual implementation might be unclear.We’ll look at two ways to perform the task: calling kernel-defined helper functionsand implementing our own version.Kernel-assisted probingThe Linux kernel offers a low-level facility for probing the interrupt number. Itonly works for nonshared interrupts, but then most hardware that is capable ofworking in a shared interrupt mode provides better ways of finding the configuredinterrupt number.