tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

MI overrides of bus_dma(9), bus_space(9), pci(9)



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
dyoung%ojctech.com@localhost      Urbana, IL * (217) 278-3933


Home | Main Index | Thread Index | Old Index