ISA(9)
πΈ NAME πΈ
okay so bestie, this is the ISA(9) page from the Kernel Developer's Manual, and it's giving Industry-standard Architecture π
. It covers ISA, isa_intr_alloc, isa_intr_establish, isa_intr_disestablish, isa_intr_evcnt, isa_dmamap_create, isa_dmamap_destroy, isa_dmamem_alloc, isa_dmamem_free, isa_dmamem_map, isa_dmamem_unmap, isa_malloc, isa_free, isa_dmastart, isa_dmaabort, isa_dmacount, isa_dmadone, isa_dmamaxsize, isa_drq_alloc, isa_drq_free, isa_drq_isfree, isa_dmacascade, and isa_mappage. That's the whole squad, no cap. π«Ά
β¨ SYNOPSIS β¨
okay so here's the whole include-and-prototype situation, copied exactly because we do not play with signatures π :
#include <sys/bus.h>
#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>
int
isa_intr_alloc(isa_chipset_tag_t ic, int mask, int type, int *irq);
const struct evcnt *
isa_intr_evcnt(isa_chipset_tag_t ic, int irq);
void *
isa_intr_establish(isa_chipset_tag_t ic, int irq, int type, int level,
int (*handler)(void *), void *arg);
void
isa_intr_disestablish(isa_chipset_tag_t ic, void *ih);
#include <dev/isa/isadmareg.h>
#include <dev/isa/isadmavar.h>
int
isa_dmamap_create(isa_chipset_tag_t ic, int chan, bus_size_t size,
int flags);
void
isa_dmamap_destroy(isa_chipset_tag_t ic, int chan);
int
isa_dmamem_alloc(isa_chipset_tag_t ic, int chan, bus_size_t size,
bus_addr_t *addrp, int flags);
void
isa_dmamem_free(isa_chipset_tag_t ic, int chan, bus_addr_t addr,
bus_size_t size);
int
isa_dmamem_map(isa_chipset_tag_t ic, int chan, bus_addr_t addr,
bus_size_t size, void **kvap, int flags);
void
isa_dmamem_unmap(isa_chipset_tag_t ic, int chan, void *kva,
size_t size);
void *
isa_malloc(isa_chipset_tag_t ic, int chan, size_t size, int pool,
int flags);
void
isa_free(void *addrp, int pool);
int
isa_dmastart(isa_chipset_tag_t ic, int chan, bus_addr_t addr,
bus_size_t size, struct lwp *lwp, int flags, int bf);
void
isa_dmaabort(isa_chipset_tag_t ic, int chan);
bus_size_t
isa_dmacount(isa_chipset_tag_t ic, int chan);
void
isa_dmadone(isa_chipset_tag_t ic, int chan);
bus_size_t
isa_dmamaxsize(isa_chipset_tag_t ic, int chan);
int
isa_drq_alloc(isa_chipset_tag_t ic, int chan);
int
isa_drq_free(isa_chipset_tag_t ic, int chan);
int
isa_drq_isfree(isa_chipset_tag_t ic, int chan);
int
isa_dmacascade(isa_chipset_tag_t ic, int chan);
paddr_t
isa_mappage(void *mem, off_t offset, int prot);
lowkey a lot of prototypes, but we move π¦
π DESCRIPTION π
okay so the machine-independent ISA subsystem is literally the support squad for the ISA bus π . The ISA bus made its debut on the IBM PC/AT, and it's an extension of the bus from the original IBM PC. Basically it's the host bus of the Intel 80286 processor, but because everyone and their mom adopted it as a de facto standard, it showed up on systems without Intel processors too. No cap.
The ISA bus has a 16-bit data bus, a 24-bit memory address bus, a 16-bit I/O address bus, and it runs at 8MHz. It gives you 15 interrupt lines and 8 DMA channels, with DMA transfers of 64KB or 128KB depending on the width of the channel you're using. Historically, some devices only decoded the 10 lowest bits of the I/O address bus, which prevented use of the full 16-bit address space. Not iconic of them, but okay. π
On newer machines, the ISA bus is no longer connected directly to the host bus β it's usually connected via a PCI-ISA bridge. Either way, the bus looks the same to the device driver. Slay for abstraction π .
π¦ DATA TYPES π¦
Drivers for devices attached to the ISA bus will make use of these data types, so let's get into it:
isa_chipset_tag_tβ Chipset tag for the ISA bus. Simple, iconic, does what it says. β¨struct isa_attach_argsβ Location hints for devices are recorded in this structure. It contains the following members:
bus_space_tag_t ia_iot; /* isa i/o space tag */
bus_space_tag_t ia_memt; /* isa mem space tag */
bus_dma_tag_t ia_dmat; /* DMA tag */
isa_chipset_tag_t ia_ic;
int ia_iobase; /* base i/o address */
int ia_iosize; /* span of ports used */
int ia_maddr; /* physical mem addr */
u_int ia_msize; /* size of memory */
int ia_irq; /* interrupt request */
int ia_drq; /* DMA request */
int ia_drq2; /* second DMA request */
void *ia_aux; /* driver specific */
that's the whole struct bestie, every member accounted for π«Ά
π FUNCTIONS π
okay so here's the function glossary, and yes I'm keeping every single one because we don't gatekeep π
isa_intr_alloc(ic, mask, type, irq)
This function is generally not required by device drivers. It is used by bridges attaching other busses to the ISA bus. So if that's not you, you can scroll past, bestie. π
isa_intr_evcnt(ic, irq)
Returns the event counter associated with interrupt line irq. That's it, that's the function. β¨
isa_intr_establish(ic, irq, type, level, handler, arg)
To establish an ISA interrupt handler, a driver calls isa_intr_establish() with the interrupt number irq, type type, and level level. When the interrupt occurs, the function handler is called with argument arg. Valid values for type are:
IST_NONEβ Reserve interrupt, but don't actually establish.IST_EDGEβ Edge-triggered interrupt.IST_LEVELβ Level-triggered interrupt.IST_PULSEβ Pulse-triggered interrupt.
isa_intr_establish() returns an opaque handle to an event descriptor if it succeeds, and returns NULL on failure. So like, check your pointers, bestie π
isa_intr_disestablish(ic, ih)
Dis-establish the interrupt handler with handle ih. That handle was returned from isa_intr_establish(). Bye bye handler π
isa_drq_alloc(ic, chan)
Reserves the DMA channel chan for future use. Normally, this call precedes an isa_dmamap_create() call. Important, bestie:
π¨ It is an error to start DMA on a channel that has not been reserved with isa_drq_alloc().
So don't skip this step, fr fr.
isa_drq_free(ic, chan)
Marks the DMA channel chan as available again. Releasing your fave channel back to the wild π
isa_dmamap_create(ic, chan, size, flags)
Creates a DMA map for channel chan. It is initialised to accept maximum DMA transfers of size size. Valid values for the flags argument are the same as for bus_dmamap_create() (see bus_dma(9)). This function returns zero on success or an error value on failure. Slay. β¨
isa_dmamap_destroy(ic, chan)
Destroy the DMA map for DMA channel chan. It's giving cleanup era. π§Ή
isa_dmamem_alloc(ic, chan, size, addrp, flags)
Allocate DMA-safe memory of size size for channel chan. Valid values for the flags argument are the same as for bus_dmamem_alloc() (see bus_dma(9)). The bus-address of the memory is returned in addrp. This function returns zero on success or an error value on failure. Iconic allocation moment π
isa_dmamem_free(ic, chan, addr, size)
Frees memory previously allocated by isa_dmamem_alloc() for channel chan. The bus-address and size of the memory are specified by addr and size respectively. Bye memory, you served π
isa_dmamem_map(ic, chan, addr, size, kvap, flags)
Maps DMA-safe memory (allocated with isa_dmamem_alloc()) specified by bus-address addr and of size size into kernel virtual address space for DMA channel chan. Valid values for the flags argument are the same as for bus_dmamem_map() (see bus_dma(9)). The kernel virtual address is returned in kvap. This function returns zero on success or an error value on failure. Map it, bestie πΊοΈ
isa_dmamem_unmap(ic, chan, kva, size)
Unmaps memory (previously mapped with isa_dmamem_map()) of size size for channel chan. The kernel virtual address space used by the mapping is freed. Unmap era, we love to see it. β¨
isa_malloc(ic, chan, size, pool, flags)
This function is a shortcut for allocating and mapping DMA-safe memory in a single step. The arguments correspond with the arguments to isa_dmamem_alloc() and isa_dmamem_map(). The argument pool is a pool to record the memory allocation. This function returns a pointer to the DMA-safe memory. One-stop shop, no cap. ποΈ
isa_free(addrp, pool)
This function is a shortcut for unmapping and deallocating DMA-safe memory in a single step. It replaces isa_dmamem_unmap() and isa_dmamem_free(). The argument addrp is the pointer to the DMA-safe memory returned by isa_malloc(). The argument pool is the same as the value passed to isa_malloc(). Two-for-one cleanup, periodt. π
isa_dmastart(ic, chan, addr, size, lwp, flags, bf)
Load DMA memory specified by address addr of size size into the DMA controller at channel chan and set it in motion. The argument lwp is used to indicate the address space in which the buffer is located. If NULL, the buffer is assumed to be in kernel space. Otherwise, the buffer is assumed to be in lwp lwp's address space. The argument flags describes the type of ISA DMA. Valid values are:
DMAMODE_WRITEβ DMA transfer from host to device.DMAMODE_READβ DMA transfer to host from device.DMAMODE_SINGLEβ Transfer buffer once and stop.DMAMODE_DEMANDβ Demand mode.DMAMODE_LOOPβ Transfer buffer continuously in loop until notified to stop.DMAMODE_LOOPDEMANDβ Transfer buffer continuously in loop and demand mode.
The argument bf is the bus-space flags. Valid values are the same as for bus_dmamap_load() (see bus_dma(9)). DMA go brrr π₯
isa_dmaabort(ic, chan)
Abort a DMA transfer on channel chan. Not the vibe, but sometimes you gotta. π
isa_dmacount(ic, chan)
Returns the offset in the DMA memory of the current DMA transfer on channel chan. Like a progress bar but for DMA. π
isa_dmadone(ic, chan)
Unloads the DMA memory on channel chan after a DMA transfer has completed. Wrap it up, bestie. π
isa_dmamaxsize(ic, chan)
Returns the maximum allowable DMA transfer size for channel chan. Know your limits, slay. π
isa_drq_isfree(ic, chan)
If the ia_drq or ia_drq2 members of struct isa_attach_args are wildcarded, then the driver is expected to probe the hardware for valid DMA channels. In this case, the driver can check to see if the hardware-supported DMA channel chan is available for use. Detective era π
isa_dmacascade(ic, chan)
Programs the 8237 DMA controller channel chan to accept external DMA control by the device hardware. Let the hardware take the wheel π
isa_mappage(mem, offset, prot)
Provides support for user mmap(2)'ing of DMA-safe memory. Userland mapping? We love to see it. πΊοΈ
π AUTOCONFIGURATION π
okay so here's the thing about the ISA bus bestie β it's an indirect-connection bus π which basically means nothing just shows up and announces itself. During autoconfiguration, every single driver has to go probe the bus to check if its device is even there. like, detective mode. π΅οΈββοΈ
An ISA driver gets handed a pointer to struct isa_attach_args that hints at "locations" on the ISA bus where the device might be chillin'. Your faves to work with are ia_iobase, ia_iosize, ia_maddr, and ia_msize β¨
Not all of these hints are gonna be necessary though β locators can be wildcarded with IOBASEUNK and MADDRUNK for ia_iobase and ia_maddr respectively. And if a driver can probe the device for configuration info at default locations, it's totally allowed to update the members of struct isa_attach_args. glow-up moment π
The IRQ and DMA locators can also be wildcarded, with IRQUNK and DRQUNK respectively. π
Then during the driver attach step, the I/O and memory address spaces should be mapped β see bus_space(9) for that whole situation π
π DMA SUPPORT π
Extensive DMA facilities are provided for the ISA bus, and honestly? iconic. A driver can use up to two DMA channels simultaneously π₯ The DMA channels allocated during autoconfiguration get passed to the driver during the driver attach using the ia_drq and ia_drq2 members of struct isa_attach_args.
Before you go allocating resources for DMA transfers on the ISA bus, a driver should check the max allowable DMA transfer size for the DMA channel using isa_dmamaxsize(). vibe check first, always π«Ά
First up, create a DMA map with isa_dmamap_create(). A DMA map describes how DMA memory is loaded into the DMA controllers. And listen β only DMA-safe memory can be used for DMA transfers, no cap. DMA-safe memory is allocated using isa_dmamem_alloc(). The memory allocated by isa_dmamem_alloc() then must be mapped into kernel virtual address space by isa_dmamem_map() so the driver can actually access it. π€
So for a DMA transfer from the host to the device, the driver fills the DMA memory with the data to be transferred. The DMA-transfer of the memory is started using isa_dmastart() with flags containing DMAMODE_WRITE. When the DMA transfer is completed, a call to isa_dmadone() cleans up the DMA transfer by unloading the memory from the controller. clean. β¨
For a DMA transfer from the device to the host, the DMA-transfer is started using isa_dmastart() with flags containing DMAMODE_READ. When the DMA transfer is completed, a call to isa_dmadone() cleans up the DMA transfer by unloading the memory from the controller. The memory can now be accessed by the driver. π¦
And when the DMA resources are no longer required? Release them using isa_dmamem_unmap(), isa_dmamem_free() and isa_dmamap_destroy(). say bye π
π CODE REFERENCES π
The ISA subsystem itself lives within these files, fr fr:
sys/dev/isa/isa.c
sys/dev/isa/isadma.c
π SEE ALSO π
π°οΈ HISTORY π°οΈ
The machine-independent ISA subsystem appeared in NetBSD 1.2. throwback era π
π BUGS π
okay so the previous behaviour of isa_intr_establish() was to invoke panic() on failure π scary. isa_intr_establish() now returns NULL on failure. BUT β some old drivers written for the former behaviour just straight up discard the return value. π so heads up, iykyk.
not me warning you about legacy drivers that ignore return values β check your old code bestie, it's giving footgun π
and that's the whole page! πΈ this beauty comes from Debian, dated January 29, 2006, for ISA(9) β a certified classic in the manpage canon π hope you slayed your ISA driver setup, mwah π
π¦ where this page comes from
- nixpkgs unstable: netbsd-source 11.0
- Copyright (c) 2001, 2005, 2006 The NetBSD Foundation, Inc
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms