There are three types of addresses: main memory addresses, I/O addresses and configuration addresses. On the PCI bus, configuration addresses constitute a separate address space just like I/O addresses do. Except for the complicated case of ISA configuration addresses, whether or not an address on the bus is a memory address, I/O address, or configuration address depends only on the voltage on other wires (traces) of the bus.
For the ISA bus, there is technically no configuration address space, but there is a special way for the CPU to access PnP configuration registers on the PnP cards. For this purpose 3 @ I/O addresses are allocated. This is not 3 addresses for each card but 3 addresses shared by all cards.
These 3 addresses are named read-port, write-port, and address-port. Each port is just one byte in size. Each PnP card has many configuration registers so that just 3 addresses are not even sufficient for these registers on a single card. To communicate with a certain card, a specially-assigned card number (handle) is sent to all cards at the write-port address. After that the only card still listening is the card with this handle. Then the address of the configuration register (of that card) is sent to the address-port (of all cards --but only one is listening). Next communication takes place with one configuration register on that card by either doing a read on the read-port or a write on the write-port.
The write-port is always at A79 and the address-port is always at 279 (hex). But the read-port is not fixed but is set by the configuration software at some address that will supposedly not conflict with any other ISA card. If there is a conflict, it will change the address. All PnP cards get "programmed" with this address. Thus if you use say isapnp to set or check configuration data it must determine this read-port address.
The term "address" is sometimes used in this document to mean a contiguous range of addresses. Since addresses are given in bytes, a single address only contains one byte but I/O (and main memory) addresses need more than this. So a range of say 8 bytes is often used for I/O address while the range for main memory addresses allocated to a device is much larger. For a serial port (an I/O device) it's sufficient to give the starting I/O address of the device (such as 3F8) since it's well known that the range of addresses for serial port is only 8 bytes. The starting address is known as the "base address".
For ISA, to access both I/O and (main) memory address "spaces" the same address bus is used (the wires used for the address are shared). How does the device know whether or not an address which appears on the address bus is a memory address or I/O address? Well, there are 4 dedicated wires on the bus that convey this information and more. If a certain one of these 4 wires is asserted, it says that the CPU wants to read from an I/O address, and the main memory ignores the address on the bus. The other 3 wires serve similar purposes. In summary: Read and write wires exist for both main memory and I/O addresses (4 wires in all).
For the PCI bus it's the same basic idea also using 4 wires but it's done a little differently. Instead of only one or the four wires being asserted, a binary number is put on the wires (16 different possibilities). Thus more info may be conveyed. Four of these 16 numbers serve the I/O and memory spaces as in the above paragraph. In addition there is also configuration address space which uses up two more numbers. Ten extra numbers are left over for other purposes.
On the ISA bus, there's a method built into each PnP card for checking that there are no other cards that use the same address. If two or more cards use the same IO address, neither card is likely to work right (if at all). Good PnP software should assign bus-resources so as to avoid this conflict, but even in this case a legacy card might be lurking somewhere with the same address.
The test works by a card putting a test number in its own IO registers. Then the PnP software reads it and verifies that it reads the same test number. If not, something is wrong (such as another card with the same address. It repeats the same test with another test number. Since it actually checks the range of IO addresses assigned to the card, it's called a "range check". It could be better called an address-conflict test. If there is an address conflict you get an error message and need to resolve it yourself.
Traditionally, most I/O devices used only I/O memory to communicate with the CPU. For example, the serial port does this. The device driver, running on the CPU would read and write data to/from the I/O address space and main memory. A faster way would be for the device itself to put the data directly into main memory. One way to do this is by using DMA Channels or bus mastering. Another way is to allocate some space in main memory to the device. This way the device reads and writes directly to main memory without having to bother with DMA or bus mastering. Such a device may also use IO addresses.
Interrupts convey a lot of information but only indirectly. The interrupt signal (a voltage on a wire) just tells a chip called the interrupt controller that a certain device needs attention. The interrupt controller then signals the CPU. The CPU finds the driver for this device and runs a part of it known as an "interrupt service routine" (or "interrupt handler"). This "routine" tries to find out what has happened and then deals with the problem such as transferring bytes from (or to) the device. This program (routine) can easily find out what has happened since the device has registers at addresses known to the the driver software (provided the IRQ number and the I/O address of the device has been set correctly). These registers contain status information about the device . The software reads the contents of these registers and by inspecting the contents, finds out what happened, and takes appropriate action..
Thus each device driver needs to know what interrupt number (IRQ) to listen to. On the PCI bus (and for the serial ports on the ISA bus starting with Kernel 2.2) it's possible for two (or more) devices to share the same IRQ number. When such an interrupt is issued, the CPU runs all interrupt service routines for all devices using that interrupt. The first thing the first service routine does is to check to see if an interrupt actually happened for its device. If there was no interrupt (false alarm) it likely will exit and the next service routine starts, etc.
PCI interrupts are different but since they are normally mapped to IRQ's they behave in about the same way. A major difference is that PCI interrupts may be shared. For example IRQ5 may be shared between two PCI devices. This sharing ability is automatic: you don't need special hardware or software. There have been some reports of situations where such sharing didn't work, but it's likely due to a defect in the device driver software. All device drivers for PCI devices are supposed to provide for interrupt sharing. Note that you can't share the same interrupt between the PCI and ISA bus. However, illegal sharing will work provided the devices which are in conflict are not in use at the same time. "In use" here means that a program is running which "opened" the device in its C programming code.
You may need to know some of the details of the PCI interrupt system in order to set up the BIOS's CMOS or to set jumpers on old PCI cards. Each PCI card has 4 possible interrupts: INTA#, INTB#, INTC#, INTD#. Thus for a 7-slot system there could be 7 x 4 = 28 different interrupt lines. But the specs permit a fewer number of interrupt lines. This is not too restrictive since interrupts may be shared. Many PCI buses seem to be made with only 4 interrupt lines. Call these lines (wires or traces) W, X, Y, Z. Suppose we designate the B interrupt from slot 3 as interrupt 3B. Then wire W could be used to share interrupts 1A, 2B, 3C, 4D, 5A, 6B, 7C. This is done by physically connecting wire W to wires 1A, 2B, etc. Likewise wire X could be connected to wires 1B, 2C, 3D, 4A, 5B, 6C, 7D. Then on startup, the BIOS maps the X, W, Y, Z to IRQ's. After that it writes the IRQ that each device is mapped to into a hardware register in each device. Then and anything interrogating the device can find out what IRQ it uses.
The above mentioned wires X, W, Y, Z are labeled per PCI specs as INTA#, INTB#, INTC# and INTD#. This official PCI notation is confusing since now INTA# has 2 possible meanings depending on whether we are talking about a slot or the PCI bus. For example, if 3C is mapped to X then we say that INTC# of slot 3 is cabled to INTA# (X) of the PCI bus. Confusing notation.
There's another requirement also. A PCI slot must use the lower interrupt letters first. Thus if a slot only uses one interrupt, it must be INTA#. If it uses 2 interrupts they must be INTA# and INTB#, etc. A card in a slot may have up to 8 devices on it but there are only 4 PCI interrupts for it. This is OK since interrupts may be shared so that each of the 8 devices (if they exist) can have an interrupt. The PCI interrupt letter of a device is often fixed and hardwired into the device.
The BIOS assigns IRQs (interrupts) so as to avoid conflicts with the IRQs it knows about on the ISA bus. Sometimes in the CMOS BIOS menu one may assign IRQs to PCI cards (but it's not simple as explained above). There's a situation where Windows zeroed out all the IRQ numbers in the PCI cards after the IRQ mappings had been set. Then someone running Windows booted Linux from Windows with the result that Linux only found only incorrect IRQs of zero.
You might reason that since the PCI is using IRQ's (ISA bus) it might be slow, etc. Not really. The ISA Interrupt Controller Chip(s) has a direct interrupt wire going to the CPU so it can get immediate attention. While signals on the ISA address and data buses need to go thru the PCI bus to get to the CPU, the IRQ interrupt signals go there almost directly.
This is only for the ISA bus. Isolation is a complex method of assigning a temporary handle (id number or Card Select Number = CSN) to each PnP device on the ISA bus. Since there are more efficient (but more complex) ways to do this, some might claim that it's a simple method. Only one write address is used for PnP writes to all PnP devices so that writing to this address goes to all PnP device that are listening. This write address is used to send (assign) a unique handle to each PnP device. To assign this handle requires that only one device be listening when the handle is sent (written) to this common address. All PnP devices have a unique serial number which they use for the process of isolation. Doing isolation is something like a game. It's done using the equivalent of just one common bus wire connecting all PnP devices and the isolation program.
For the first round of the "game" all PnP devices listen on this wire and send out simultaneously a sequence of bits to the wire. The allowed bits are either a 1 (positive voltage) or an "open 0" of no voltage (open circuit or tri-state). Each PnP device just starts to sequentially send out its serial number, bit-by-bit, starting with the high-order bit, on this wire. If any device sends a 1, a 1 will be heard on the wire by all other devices. If all devices send an "open 0" nothing will be heard on the wire. The object is to eliminate (by the end of this first round) all but highest serial number device. "Eliminate" means to cease to listen anymore to the write address that all devices still in the game are still listening to. This is also called "dropping out". (Note that all serial numbers are of the same length.)
First consider only the high order bit of the serial number which is put on the wire first by all devices which have no handle yet. If any PnP device sends out a 0 (open 0) but hears a 1, this means that some other PnP device has a higher serial number, so it temporarily drops out of this round and doesn't listen anymore until the round is finished (when a handle is assigned to the winner: the highest serial number). Now the devices still in the game all have the same leading digit (a 1) so we may strip off this digit and consider only the resulting "stripped serial number" for future participation in this round. Then go to the start of this paragraph and repeat until the entire serial number has been examined for each device (see below for the all-0 case).
Thus it's clear that the highest serial number will not be eliminated from the game. But what happens if the leading digits (of the possibly stripped serial numbers) are all 0? In this case an "open 0" is sent on the line and all participants stay in the game. If they all have a leading 0 then this is a tie and the 0's are stripped off just like the 1's were in the above paragraph. The game then continues as the next digit (of the serial number) is sent out.
At the end of the round (after the low-order bit of the serial number has been sent out by whatever participants remain) only one PnP device with the highest serial number remains. It then gets assigned a handle and drops out of the game permanently. Then all the dropouts from the last round (that don't have a handle yet) reenter the game and a new round begins with one less participant. Eventually, all PnP devices are assigned handles. It's easy to prove that this algorithm works.
Once all handles are assigned, they are used to address each PnP device and send it a configuration as well as to read configuration info from the PnP device. Note that these handles are only used for PnP configuration and are not used for normal communication with the PnP device. When the computer starts up, all of the handles are lost so that a PnP BIOS usually does the isolation process again each time you start your PC.
END OF Plug-and-Play-HOWTO