FreeBSD Device Driver Writer's Guide : Linking Into the Kernel. : Loadable Kernel Module (LKM)
Previous: Reboot.
Next: Device Type Idiosyncrasies

4.2. Loadable Kernel Module (LKM)

There are really no defined procedures for writing an LKM driver. The following is my own conception after experimenting with the LKM device interface and looking at the standard device driver model, this is one way of adding an LKM interface to an existing driver without touching the original driver source (or binary). It is recommended though, that if you plan to release source to your driver, the LKM specific parts should be part of the driver itself, conditionally compiled on the LKM macro (i.e. #ifdef LKM).

This section will focus on writing the LKM specific part of the driver. We will assume that we have written a driver which will drop into the standard device driver model, which we would now like to implement as an LKM. We will use the pcaudio driver as a sample driver, and develop an LKM front-end. The source and makefile for the pcaudio LKM, ``pcaudio_lkm.c'' and ``Makefile'', should be placed in /usr/src/lkm/pcaudio. What follows is a breakdown of pcaudio_lkm.c.

Lines 17 - 26

-- This includes the file ``pca.h'' and conditionally compiles the rest of the LKM on whether or not we have a pcaudio device defined. This mimics the behavior of config. In a standard device driver, config(8) generates the pca.h file from the number pca devices in the config file.


    17  /* 
    18   * figure out how many devices we have..
    19   */
    20
    21  #include "pca.h"
    22
    23  /*
    24   * if we have at least one ...
    25   */
    26  #if NPCA > 0

Lines 27 - 37

-- Includes required files from various include directories.


    27  #include <sys/param.h>
    28  #include <sys/systm.h>
    29  #include <sys/exec.h>
    30  #include <sys/conf.h>
    31  #include <sys/sysent.h>
    32  #include <sys/lkm.h>
    33  #include <sys/errno.h>
    34  #include <i386/isa/isa_device.h>
    35  #include <i386/isa/isa.h>
    36
    37

Lines 38 - 51

-- Declares the device driver entry points as external.


    38  /*
    39   * declare your entry points as externs
    40   */
    41
    42  extern int pcaprobe(struct isa_device *);
    43  extern int pcaattach(struct isa_device *);
    44  extern int pcaopen(dev_t, int, int, struct proc *);
    45  extern int pcaclose(dev_t, int, int, struct proc *);
    46  extern int pcawrite(dev_t, struct uio *, int);
    47  extern int pcaioctl(dev_t, int, caddr_t);
    48  extern int pcaselect(dev_t, int, struct proc *);
    49  extern void pcaintr(struct clockframe *);
    50  extern struct isa_driver pcadriver;
    51

Lines 52 - 70

-- This is creates the device switch entry table for your driver. This table gets swapped wholesale into the system device switch at the location specified by your major number. In the standard model, these are in /usr/src/sys/i386/i386/conf.c. NOTE: you cannot pick a device major number higher than what exists in conf.c, for example at present, conf.c rev 1.85, there are 67 slots for character devices, you cannot use a (character) major device number 67 or greater, without first reserving space in conf.c.


    52  /*
    53   * build your device switch entry table
    54   */
    55
    56  static struct cdevsw pcacdevsw = {
    57    (d_open_t *)      pcaopen, /* open */
    58    (d_close_t *)     pcaclose, /* close */
    59    (d_rdwr_t *)      enodev, /* read */
    60    (d_rdwr_t *)      pcawrite, /* write */
    61    (d_ioctl_t *)     pcaioctl, /* ioctl */
    62    (d_stop_t *)      enodev, /* stop?? */
    63    (d_reset_t *)     enodev, /* reset */
    64    (d_ttycv_t *)     enodev, /* ttys */
    65    (d_select_t *)    pcaselect, /* select */
    66    (d_mmap_t *)      enodev, /* mmap */
    67    (d_strategy_t *)  enodev /* strategy */
    68  };
    69
    70

Lines 71 - 131

-- This section is analogous to the config file declaration of your device. The members of the isa_device structure are filled in by what is known about your device, I/O port, shared memory segment, etc. We will probably never have a need for two pcaudio devices in the kernel, but this example shows how multiple devices can be supported.


    71  /*
    72   * this lkm arbitrarily supports two 
    73   * instantiations of the pc-audio device.
    74   *
    75   * this is for illustration purposes
    76   * only, it doesn't make much sense
    77   * to have two of these beasts...
    78   */
    79
    80
    81  /*
    82   * these have a direct correlation to the
    83   * config file entries...
    84   */
    85  struct isa_device pcadev[NPCA] = {
    86    {
    87      11,         /* device id */
    88      &pcadriver,  /* driver pointer */
    89      IO_TIMER1,         /* base io address */
    90      -1,      /* interrupt */
    91      -1,         /* dma channel */
    92      (caddr_t)-1,    /* physical io memory */
    93      0,     /* size of io memory */
    94      pcaintr ,       /* interrupt interface */
    95      0,          /* unit number */
    96      0,     /* flags */
    97      0,          /* scsi id */
    98      0,          /* is alive */
    99      0,          /* flags for register_intr */
   100      0,          /* hot eject device support */
   101      1           /* is device enabled */
   102    },
   103  #if NPCA >1
   104    {
   105
   106   /*
   107    * these are all zeros, because it doesn't make
   108    * much sense to be here
   109    * but it may make sense for your device
   110    */
   111
   112      0,         /* device id */
   113      &pcadriver,  /* driver pointer */
   114      0,         /* base io address */
   115      -1,      /* interrupt */
   116      -1,         /* dma channel */
   117      -1,    /* physical io memory */
   118      0,     /* size of io memory */
   119      NULL,       /* interrupt interface */
   120      1,          /* unit number */
   121      0,     /* flags */
   122      0,          /* scsi id */
   123      0,          /* is alive */
   124      0,          /* flags for register_intr */
   125      0,          /* hot eject device support */
   126      1           /* is device enabled */
   127    },
   128  #endif
   129
   130  };
   131

Lines 132 - 139

-- This calls the C-preprocessor macro MOD_DEV, which sets up an LKM device driver, as opposed to an LKM filesystem, or an LKM system call.


   132  /*
   133   * this macro maps to a function which 
   134   * sets the LKM up for a driver
   135   * as opposed to a filesystem, system call, or misc
   136   * LKM.
   137   */
   138  MOD_DEV("pcaudio_mod", LM_DT_CHAR, 24, &pcacdevsw);
   139

Lines 140 - 168

-- This is the function which will be called when the driver is loaded. This function tries to work like sys/i386/isa/isa.c which does the probe/attach calls for a driver at boot time. The biggest trick here is that it maps the physical address of the shared memory segment, which is specified in the isa_device structure to a kernel virtual address. Normally the physical address is put in the config file which builds the isa_device structures in /usr/src/sys/compile/KERNEL/ioconf.c. The probe/attach sequence of /usr/src/sys/isa/isa.c translates the physical address to a virtual one so that in your probe/attach routines you can do things like

(int *)id->id_maddr = something;
and just refer to the shared memory segment via pointers.
   140  /*
   141   * this function is called when the module is
   142   * loaded; it tries to mimic the behavior
   143   * of the standard probe/attach stuff from
   144   * isa.c
   145   */
   146  int
   147  pcaload(){
   148    int i;
   149    uprintf("PC Audio Driver Loaded\n");
   150    for (i=0; i<NPCA; i++){
   151      /*
   152       * this maps the shared memory address
   153       * from physical to virtual, to be
   154       * consistent with the way
   155       * /usr/src/sys/i386/isa.c handles it.
   156       */
   157      pcadev[i].id_maddr -=0xa0000;
   158      pcadev[i].id_maddr += atdevbase;
   159      if ((*pcadriver.probe)(pcadev+i)) {
   160        (*(pcadriver.attach))(pcadev+i);
   161      } else {
   162        uprintf("PC Audio Probe Failed\n");
   163        return(1);
   164      }
   165    }
   166      return 0;
   167  }
   168

Lines 169 - 179

-- This is the function called when your driver is unloaded; it just displays a message to that effect.


   169  /*
   170   * this function is called
   171   * when the module is unloaded
   172   */
   173
   174  int
   175  pcaunload(){
   176    uprintf("PC Audio Driver Unloaded\n");
   177    return 0;
   178  }
   179

Lines 180 - 190

-- This is the entry point which is specified on the command line of the modload. By convention it is named <dev>_mod. This is how it is defined in bsd.lkm.mk, the makefile which builds the LKM. If you name your module following this convention, you can do ``make load'' and ``make unload'' from /usr/src/lkm/pcaudio.

Note: this has gone through many revisions from release 2.0 to 2.1. It may or may not be possible to write a module which is portable across all three releases.


   180  /*
   181   * this is the entry point specified
   182   * on the modload command line
   183   */
   184
   185  int
   186  pcaudio_mod(struct lkm_table *lkmtp, int cmd, int ver)
   187  {
   188          DISPATCH(lkmtp, cmd, ver, pcaload, pcaunload, nosys);
   189  }
   190
   191  #endif /* NICP > 0 */


FreeBSD Device Driver Writer's Guide : Linking Into the Kernel. : Loadable Kernel Module (LKM)
Previous: Reboot.
Next: Device Type Idiosyncrasies
freebsd-questions@freebsd.org