The following was on netbsd-users. Would this be useful to
add to the
NetBSD Internals book (htdocs/docs/internals/en/) to start
"my first
NetBSD device driver"?
(I bcc'd a few developers who work on that book.)
On Sat, 15 Mar 2008, Toru Nishimura wrote to netbsd-users:
> Date: Sat, 15 Mar 2008 21:35:27 +0900
> From: Toru Nishimura <locore64 alkyltechnology.com>
> To: netbsd-users netbsd.org
> Subject: 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
registersn");
> 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 drivern");
> 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
>
>
Jeremy C. Reed
|