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