I would like for MI drivers to be able to override pci(9), bus_space(9), and bus_dma(9) behavior for the purpose of handling exceptions, managing bus resources, creating test harnesses, and counting events.
Where pci(9), bus_space(9), and bus_dma(9) implementations are MD, today, I would like to split them into MI and MD parts. This will involve creating MI types, struct bus_space_tag and struct pci_chipset_tag and corresponding MI typedefs, bus_space_tag_t and pci_chipset_tag_t. Every MD tag will embed an MI tag, and a "pure" MI tag can derive from an MD tag or from an MI tag. pci_chipset_tag_t, for example, will be defined: struct pci_chipset_tag; typedef struct pci_chipset_tag *pci_chipset_tag_t; struct pci_chipset_tag { /* If pc_super != NULL, his tag derives from pc_super. */ pci_chipset_tag_t pc_super; /* MI override functions. */ pcireg_t (*pc_conf_read)(pci_chipset_tag_t, pcitag_t, int); void (*pc_conf_write)(pci_chipset_tag_t, pcitag_t, int, pcireg_t); int (*pc_intr_map)(struct pci_attach_args *, pci_intr_handle_t *); const char *(*pc_intr_string)(pci_chipset_tag_t, pci_intr_handle_t); const struct evcnt *(*pc_intr_evcnt)(pci_chipset_tag_t, pci_intr_handle_t); void *(*pc_intr_establish)(pci_chipset_tag_t, pci_intr_handle_t, int, int (*)(void *), void *); void (*pc_intr_disestablish)(pci_chipset_tag_t, void *); pcitag_t (*pc_make_tag)(pci_chipset_tag_t, int, int, int); void (*pc_decompose_tag)(pci_chipset_tag_t, pcitag_t, int *, int *, int *); }; pc.pc_super points to the tag from which pc derives. The implementation of a pci(9) routine such as pci_conf_read(9) or pci_conf_write(9) will remain MD, but I will add a short "preamble" that will check for and call an MI override, if any override exists: pcireg_t pci_conf_read(pci_chipset_tag_t pc0, pcitag_t tag, int reg) { if (pc != NULL) { if (pc->pc_conf_read != NULL) return (*pc->pc_conf_read)(pc, tag, reg); else if (pc->pc_super != NULL) return pci_conf_read(pc->pc_super, tag, reg); } /* MD implementation */ } It is necessary to check every ancestor for an override function until either an override is found, or ancestors run out. If ancestors run out, the typical MD behavior occurs. EXAMPLE Here are fragments of a PCI driver, xxx, that derives a new pci_chipset_tag_t. It overrides the pci_conf_read(9) behavior in order to count each PCI Configuration Space read before it is made. By convention, xxx points the new tag's MI pc_super member at the old tag, so that the new tag can call on struct xxx_pci_chipset_tag { struct pci_chipset_tag xpc_pc; struct evcnt xpc_reads; }; static pcireg_t xxx_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg) { struct xxx_pci_chipset_tag *xpc; xpc = (struct xxx_pci_chipset_tag *)pc; xpc->xpc_reads.ev_count++; return pci_conf_read(pc->pc_super, tag, reg); } pci_chipset_tag_t xxx_pci_chipset_tag_create(struct xxx_softc *sc) { struct xxx_pci_chipset_tag *xpc; pci_chipset_tag_t pc; xpc = &sc->sc_xpc; pc = &xpc->xpc_pc; pc->pc_super = sc->sc_pc; pc->pc_conf_read = xxx_conf_read; evcnt_attach_dynamic(&xpc->xpc_ev, EVCNT_TYPE_MISC, NULL, device_xname(sc->sc_dev), "cfg space rd"); return pc; } MI overrides of bus_space(9) and bus_dma(9) will work analogously to the above. Thoughts? Dave -- David Young OJC Technologies dyo...@ojctech.com Urbana, IL * (217) 278-3933