NetBSD-Users archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
re: Accessing a hardware device
David Dudley asked;
I've got an application where I need to access the hardware registers
of a specific PCI device. This device doesn't have a driver associated
with it (and my application doesn't need one either), and the
application is planned to handle direct interface to the hardware.
It should be a rather easy task, just as you expect, a sort of "my first
NetBSD device driver" practice. All you want is a way to mmap
PCI device register address range to your own program which
knows and handles everything specific to the HW.
- It's necessary for you to have a small device driver anyway. It provides
userland an entry point to acccess. The program structure would
be like as below;
fd = open("/dev/mypcihw", 2);
...
ptr = mmap((void *)0, 0x1000, PROT_READ|PROT_WRITE,
MAP_FILE|MAP_SHARED, fd, (off_t)0);
when successful the pointer refers to 0x1000 useful address range to
map target PCI device register.
- plan and assign cdev name and major number. Beyond 110 is a good
candicate. Hereafter I assume you choose i386 path to go. Edit
i386/conf/majors.i386 to reflect your intent.
- Let's say to place a source file as i386/pci/ mypcihw.c. Edit
i386/conf/files.i386
to add the new file. The file outlook would be like as below;
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <uvm/uvm_extern.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivarh>
#include <dev/pci/pcidevs.h>
#include <machine/bus.h>
struct mypcihw_softc {
struct device sc_dev;
bus_space_tag_t sc_st;
bus_space_handle_t sc_sh;
paddr_t sc_bar0;
};
static int int mypcihw_match(struct device *, struct cfdata *, void *);
static void mypcihw_attach(struct device *, struct device *, void *);
CFATTACH_DECL(mypcihw, sizeof(struct mypcihw_softc),
mypcihw_match, mypcihw_attach);
static int
mypcihw_match(struct device *parent, struct cfdta *match, void *aux)
{
struct pci_attach_args *pa = aux;
return (PCI_VENDOR(pa->pa_id) == MYPCIHWDEV
&& PCI_PRODUCT(pa->pa_id) == MYPCIHWPROD);
}
Let's say BAR0 (offset 0x10) defines MEM style register ranges.
mypcihw_attach() does as simple initialization as follows;
static void
mypcihw_attach(struct device *parent, struct device *self, void *aux)
{
struct mypcihw_softc *sc = (void *)self; /* would be argued aginst here */
struct pci_attach_args *pa = aux;
pci_chipset_tag_t pc = pa->pa_pc;
if (pci_mapreg_map(pa, 0x10,
PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT,
0, &sc->sc_st, &sc->sc_sh, NULL, NULL) != 0) {
printf(": unable to map device registers\n");
return;
}
sc->sc_bar0 = pci_conf_read(pc, pa->pa_tag, 0x10);
/* might be necessary to have a global initialization of the HW here */
printf(": my own PCI device driver\n");
return;
}
Now let's define three cdev routines for this.
extern struct cfdriver mypcihw_cd;
static dev_type_open(mypcihwopen);
static dev_type_close(mypcihwclose);
static dev_type_mmap(mypcihwmmap);
static int
mypcihwopen(dev_t dev, int flag, int mode, struct lwp *l)
{
struct mypcihw_softc *sc;
sc = device_lookup(&mypcihw_cd, minor(dev));
if (sc == NULL)
return ENXIO;
/* might want to have exclusive access here */
/* open-time (re-)initialization */
return 0;
}
static int
mypcihwclose(dev_t dev, int flag, int mode, struct lwp *l)
{
/* close-time terminating reset op */
return 0;
}
Then here is the most interesting part to provide mmap interface.
static paddr_t
mypcihwmmap(dev_t dev, off_t off, int flag)
{
struct mypcihw_softc *sc;
sc = device_lookup(&mypcihw_cd, minor(dev));
if (off >= 0x1000)
return -1;
return sc->sc_bar0; /* hopefully BAR0 is 0x1000 aligned */
}
I once made mmap style device driver for a simple PCI card with
PLX PCI-localbus bridge chip and it worked well as expected.
Toru Nishimura/ALKYL Technology
Home |
Main Index |
Thread Index |
Old Index