NetBSD-Docs archive

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

document to add to internals book? (was re: Accessing a hardware device)



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@localhost>
> To: netbsd-users%netbsd.org@localhost
> 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 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
> 
> 

  Jeremy C. Reed


Home | Main Index | Thread Index | Old Index