FreeBSD Device Driver Template/Skeleton (Kernel Module)
This is a FreeBSD 5.3 Device Driver Template/Skeleton. The driver creates a device entry in
/dev/, which can be used to communicate with the kernel module (read, write, ioctl).
- Compile the kernel module with "make" (Makefile)
- Load the module with "load.sh"
- Test the module with "devtest"
"devtest" does an ioctl (IO CONTROL) (look at the kernel log in /var/log/messages). Furthermore
it writes a string to kernel space and reads it back.
If you want to implement a blocking read operation, see the
FreeBSD Parallel Port Interrupt Device Driver Template/Skeleton
for usage of tsleep and wakeup.
Compilation is done with "gcc" - it is installed by default with the CD set.
skeleton.c
/* FreeBSD 5.3 Device Driver / Kernel Module Template/Skeleton
*******************************************************
Compile with: make
Load the module with: kldload -v ./skeleton.ko
Test with "devtest.c"
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/errno.h>
#include <sys/conf.h>
#include <sys/module.h>
#include <sys/uio.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
struct skeleton_softc
{
bus_space_tag_t bst;
bus_space_handle_t bsh;
dev_t dev0;
dev_t dev1;
u_int32_t open_mask;
u_int32_t read_mask;
struct resource *res;
int rid;
};
static devclass_t skeleton_devclass;
static d_open_t skeleton_open;
static d_close_t skeleton_close;
static d_read_t skeleton_read;
static d_write_t skeleton_write;
static d_ioctl_t skeleton_ioctl;
#define Nskeleton 2
#define MAJOR 199
#define MAXMSGLEN 128
#define TEST1 1
static struct cdevsw skeleton_cdevsw = {
.d_version = D_VERSION,
.d_name = "skeleton",
.d_flags = D_NEEDGIANT,
.d_open = skeleton_open, /* open */
.d_close = skeleton_close, /* close */
.d_read = skeleton_read, /* read */
.d_write = skeleton_write, /* write */
.d_ioctl = skeleton_ioctl /* ioctl */
};
static int skeleton_open(struct cdev *dev, int flags, int fmt, struct thread *td) {
int unit = minor(dev) >> 16;
int skeleton = minor(dev) & 0xff;
struct skeleton_softc *sc;
if (skeleton >= Nskeleton)
return ENXIO;
sc = devclass_get_softc(skeleton_devclass, unit);
if (sc == NULL)
return ENXIO;
if (sc->open_mask & (1 << skeleton))
return EBUSY;
sc->open_mask |= 1 << skeleton;
sc->read_mask |= 1 << skeleton;
return 0;
}
static int skeleton_close(struct cdev *dev, int flags, int fmt, struct thread *td) {
int unit = minor(dev) >> 16;
int skeleton = minor(dev) & 0xff;
struct skeleton_softc *sc;
if (skeleton >= Nskeleton)
return ENXIO;
sc = devclass_get_softc(skeleton_devclass, unit);
if (sc == NULL)
return ENXIO;
sc->open_mask &= ~(1 << skeleton);
return 0;
}
char teststr[128];
static int skeleton_read(struct cdev *dev, struct uio *uio, int flag) {
int resid = MAXMSGLEN;
int error = 0;
//printf("reading...\n");
do {
if (uio->uio_resid < resid)
resid = uio->uio_resid;
error = uiomove(teststr, resid, uio);
} while (resid > 0 && error == 0);
return(error);
}
static int skeleton_write(struct cdev *dev, struct uio *uio, int flag) {
int resid = MAXMSGLEN;
int error = 0;
//printf("writing...\n");
do {
if (uio->uio_resid < resid)
resid = uio->uio_resid;
error = uiomove(teststr, resid, uio);
} while (resid > 0 && error == 0);
return(error);
}
static int skeleton_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
int fflag, struct thread *td)
{
int error = 0;
switch(cmd) {
case TEST1:
printf("Skeleton IOCTL: TEST1\n");
break;
default:
break;
}
return(error);
}
static void skeleton_identify (driver_t *driver, device_t parent) {
devclass_t dc;
device_t child;
dc = devclass_find("skeleton");
if (devclass_get_device(dc, 0) == NULL) {
child = BUS_ADD_CHILD(parent, 0, "skeleton", -1);
}
}
static int skeleton_probe(device_t dev) {
if (device_get_unit(dev) != 0)
return (ENXIO);
device_set_desc(dev, "FreeBSD Device Driver Template/Skeleton");
return (0);
}
static int skeleton_attach(device_t dev) {
struct skeleton_softc *sc;
sc = (struct skeleton_softc *) device_get_softc(dev);
sc->rid = 0;
sc->dev0 = make_dev(&skeleton_cdevsw, 0, UID_ROOT, GID_WHEEL, 0644, "skeleton0");
// sc->dev1 = make_dev(&skeleton_cdevsw, 1, UID_ROOT, GID_WHEEL, 0644, "skeleton1");
sc->open_mask = 0;
sc->read_mask = 0;
return 0;
}
static int skeleton_detach(device_t dev) {
struct skeleton_softc *sc;
sc = (struct skeleton_softc *) device_get_softc(dev);
destroy_dev(sc->dev0);
// destroy_dev(sc->dev1);
bus_generic_detach(dev);
return 0;
}
static device_method_t skeleton_methods[] = {
/* Device interface */
DEVMETHOD(device_identify, skeleton_identify),
DEVMETHOD(device_probe, skeleton_probe),
DEVMETHOD(device_attach, skeleton_attach),
DEVMETHOD(device_detach, skeleton_detach),
{ 0, 0 }
};
static driver_t skeleton_driver = {
"skeleton",
skeleton_methods,
sizeof(struct skeleton_softc),
};
DRIVER_MODULE(skeleton, isa, skeleton_driver, skeleton_devclass, 0, 0);
devtest.c
// compile with: gcc -o devtest devtest.c
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#define TEST1 1
int main(void) {
int error, fd, wlen;
double testval;
char teststr[128];
char mystring[] = "hello captain!";
fd = open("/dev/skeleton0", O_RDWR);
if (fd == -1)
err(1, "open");
error = ioctl(fd, TEST1, NULL);
if (error == -1)
err(1, "ioctl");
wlen = strlen(mystring) + 1;
error = write(fd, mystring, wlen);
if (error == -1)
err(1, "write");
error = read(fd, teststr, 128);
if (error == -1)
err(1, "read");
printf("read2: %s\n", teststr);
close(fd);
exit(0);
}
Makefile
SRCS=skeleton.c
KMOD=skeleton
.include <bsd.kmod.mk>
load.sh
#!/bin/sh
kldunload skeleton
kldload -v ./skeleton.ko
Last-Modified: Sat, 04 Feb 2006 16:03:13 GMT