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