Tying Everything Together

You now know enough to write your first Newbus driver. Example 7-1 is a simple Newbus driver (based on code written by Murray Stokely) for a fictitious PCI device.

Note

Take a quick look at this code and try to discern some of its structure. If you don’t understand all of it, don’t worry; an explanation follows.

Example 7-1. foo_pci.c

#include <sys/param.h>
  #include <sys/module.h>
  #include <sys/kernel.h>
  #include <sys/systm.h>

  #include <sys/conf.h>
  #include <sys/uio.h>
  #include <sys/bus.h>

  #include <dev/pci/pcireg.h>
  #include <dev/pci/pcivar.h>

 struct foo_pci_softc {
         device_t        device;
         struct cdev     *cdev;
  };

  static d_open_t         foo_pci_open;
  static d_close_t        foo_pci_close;
  static d_read_t         foo_pci_read;
  static d_write_t        foo_pci_write;

 static struct cdevsw foo_pci_cdevsw = {
          .d_version =    D_VERSION,
          .d_open =       foo_pci_open,
          .d_close =      foo_pci_close,
          .d_read =       foo_pci_read,
          .d_write =      foo_pci_write,
          .d_name =       "foo_pci"
  };

 static devclass_t foo_pci_devclass;

  static int
  foo_pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
  {
          struct foo_pci_softc *sc;

          sc = dev->si_drv1;
          device_printf(sc->device, "opened successfully
");
          return (0);
  }

  static int
  foo_pci_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
  {
          struct foo_pci_softc *sc;

          sc = dev->si_drv1;
          device_printf(sc->device, "closed
");
          return (0);
  }

  static int
  foo_pci_read(struct cdev *dev, struct uio *uio, int ioflag)
  {
          struct foo_pci_softc *sc;

          sc = dev->si_drv1;
          device_printf(sc->device, "read request = %dB
", uio->uio_resid);
          return (0);
  }

  static int
  foo_pci_write(struct cdev *dev, struct uio *uio, int ioflag)
  {
          struct foo_pci_softc *sc;

          sc = dev->si_drv1;
          device_printf(sc->device, "write request = %dB
", uio->uio_resid);
          return (0);
  }

  static struct _pcsid {
          uint32_t        type;
          const char      *desc;
  } pci_ids[] = {
          { 0x1234abcd, "RED PCI Widget" },
          { 0x4321fedc, "BLU PCI Widget" },
          { 0x00000000, NULL }
  };

  static int
  foo_pci_probe(device_t dev)
  {
          uint32_t type = pci_get_devid(dev);
          struct _pcsid *ep = pci_ids;

          while (ep->type && ep->type != type)
                  ep++;
          if (ep->desc) {
                  device_set_desc(dev, ep->desc);
                  return (BUS_PROBE_DEFAULT);
          }

          return (ENXIO);
  }

  static int
  foo_pci_attach(device_t dev)
  {
          struct foo_pci_softc *sc = device_get_softc(dev);
          int unit = device_get_unit(dev);

          sc->device = dev;
          sc->cdev = make_dev(&foo_pci_cdevsw, unit, UID_ROOT, GID_WHEEL,
              0600, "foo_pci%d", unit);
          sc->cdev->si_drv1 = sc;

          return (0);
  }

  static int
  foo_pci_detach(device_t dev)
  {
          struct foo_pci_softc *sc = device_get_softc(dev);

          destroy_dev(sc->cdev);
          return (0);
  }

  static device_method_t foo_pci_methods[] = {
          /* Device interface. */
          DEVMETHOD(device_probe,         foo_pci_probe),
          DEVMETHOD(device_attach,        foo_pci_attach),
          DEVMETHOD(device_detach,        foo_pci_detach),
          { 0, 0 }
  };

  static driver_t foo_pci_driver = {
          "foo_pci",
          foo_pci_methods,
          sizeof(struct foo_pci_softc)
  };
 DRIVER_MODULE(foo_pci, pci, foo_pci_driver, foo_pci_devclass, 0, 0);

This driver begins by defining its software context, which will maintain a pointer to its device and the cdev returned by the make_dev call.

Next, its character device switch table is defined. This table contains four d_foo functions named foo_pci_open, foo_pci_close, foo_pci_read, and foo_pci_write. I’ll describe these functions in d_foo Functions in d_foo Functions.

Then a devclass_t variable is declared. This variable is passed to the DRIVER_MODULE macro as its devclass argument.

Finally, the d_foo and device_foo functions are defined. These functions are described in the order they would execute.

foo_pci_probe Function

The foo_pci_probe function is the device_probe implementation for this driver. Before I walk through this function, a description of the pci_ids array (found around the middle of Example 7-1) is needed.

static struct _pcsid {
      uint32_t        type;
      const char      *desc;
} pci_ids[] = {
        { 0x1234abcd, "RED PCI Widget" },
        { 0x4321fedc, "BLU PCI Widget" },
        { 0x00000000, NULL }
};

This array is composed of three _pcsid structures. Each _pcsid structure contains a PCI ID and a description of the PCI device. As you might have guessed, pci_ids lists the devices that Example 7-1 supports.

Now that I’ve described pci_ids, let’s walk through foo_pci_probe.

static int
foo_pci_probe(device_t dev)
{
        uint32_t type = pci_get_devid(dev);
        struct _pcsid *ep = pci_ids;

      while (ep->type && ep->type != type)
                ep++;
        if (ep->desc) {
              device_set_desc(dev, ep->desc);
              return (BUS_PROBE_DEFAULT);
        }

        return (ENXIO);
}

Here, dev describes an identified device found on the PCI bus. So this function begins by obtaining the PCI ID of dev. Then it determines if dev’s PCI ID is listed in pci_ids. If it is, dev’s verbose description is set and the success code BUS_PROBE_DEFAULT is returned.

Note

The verbose description is printed to the system console when foo_pci_attach executes.

foo_pci_attach Function

The foo_pci_attach function is the device_attach implementation for this driver. Here is its function definition (again):

static int
foo_pci_attach(device_t dev)
{
        struct foo_pci_softc *sc = device_get_softc(dev);
        int unit = device_get_unit(dev);

        sc->device = dev;
        sc->cdev = make_dev(&foo_pci_cdevsw, unit, UID_ROOT, GID_WHEEL,
            0600, "foo_pci%d", unit);
        sc->cdev->si_drv1 = sc;

        return (0);
}

Here, dev describes a device under this driver’s control. Thus, this function starts by getting dev’s software context and unit number. Then a character device node is created and the variables sc->device and sc->cdev->si_drv1 are set to dev and sc, respectively.

Note

The d_foo functions (described next) use sc->device and cdev->si_drv1 to gain access to dev and sc.

d_foo Functions

Because every d_foo function in Example 7-1 just prints a debug message (that is to say, they’re all basically the same), I’m only going to walk through one of them: foo_pci_open.

static int
foo_pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
{
        struct foo_pci_softc *sc;

      sc = dev->si_drv1;
      device_printf(sc->device, "opened successfully
");
        return (0);
}

Here, dev is the cdev returned by the make_dev call in foo_pci_attach. So, this function first obtains its software context. Then it prints a debug message.

foo_pci_detach Function

The foo_pci_detach function is the device_detach implementation for this driver. Here is its function definition (again):

static int
foo_pci_detach(device_t dev)
{
        struct foo_pci_softc *sc = device_get_softc(dev);

      destroy_dev(sc->cdev);
        return (0);
}

Here, dev describes a device under this driver’s control. Thus, this function simply obtains dev’s software context to destroy its device node.

Don’t Panic

Now that we’ve discussed Example 7-1, let’s give it a try:

$ sudo kldload ./foo_pci.ko
$ kldstat
Id Refs Address    Size     Name
 1    3 0xc0400000 c9f490   kernel
 2    1 0xc3af0000 2000     foo_pci.ko
$ ls -l /dev/foo*
ls: /dev/foo*: No such file or directory

Of course, it fails miserably, because foo_pci_probe is probing for fictitious PCI devices. Before concluding this chapter, one additional topic bears mentioning.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.119.17