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 */