Captain's Universe Home
Captain's Universe Home
Cosmic Ray Muon DetectorTeleGarden Pages
Time on MarsBryophyllum Plants
Jupiter Radio AstronomyAncient Pages
Salzburg Tourist GuideEarth Magnetometer
  H O M E     AJAX & MORE     LINUX & MORE     RTAI     XENOMAI     ADEOS IPIPE      
    JAVA & BROWSERS     *NIX     ELECTRONICS     REVIEWS     ARTEMIA     FAIRY SHRIMP      


OpenBSD Device Driver Template/Skeleton (Kernel Module Example)


This is a OpenBSD 3.7 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 "compile.sh"
  • Create the device node /dev/ourdev with "dev-install.sh"
  • Load the module with "install.sh"
  • Test the module with "chardevtest" or "test.sh"

Troubleshooting:
Problem:
modload: not loading symbols: kernel does not support symbol table loading: Operation not permitted
modload: can't reserve memory: Operation not permitted
Solution:
change securelevel to -1 in /etc/rc.securelevel and reboot.

Problem:
/usr/include/sys/systm.h:308:33: lib/libkern/libkern.h: No such file or directory Solution:
Install kernel source: Get sys.tar.gz from ftp://ftp.openbsd.org/pub/OpenBSD/3.7/
Untar it with: "tar xzf sys.tar.gz" in /usr/src

"chardevtest" does some ioctl (IO Control), which can be used to change some settings in the kernel module and reads/writes from/to the character device /dev/ourdev.

"test.sh" also reads/writes from/to the device /dev/ourdev with "dd" and "echo".

Compilation is done with "cc", which is installed by default when installing the bare bones system from CD. Make sure you also add the pure-ftpd FTP server to the CD when making your own CD set.

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.


/* OpenBSD 3.7 Device Driver / Kernel Module Template/Skeleton
   *******************************************************
   (Based on the template by Peter Werner)

 Compile with: compile.sh
 Install the device entry in /dev with dev-install.sh
 Load the module with install.sh

 Test with chardevtest.c or test.sh

 TROUBLESHOTING:
 **************
 /usr/include/sys/systm.h:308:33: lib/libkern/libkern.h: No such file or directory
 -> install kernel source
 Get sys.tar.gz from ftp://ftp.openbsd.org/pub/OpenBSD/3.7/
 Untar it with:
 # tar xzf sys.tar.gz
 in /usr/src

 modload: not loading symbols: kernel does not support symbol table loading:
    Operation not permitted
 modload: can't reserve memory: Operation not permitted
 --> change securelevel to -1 in /etc/rc.securelevel and reboot.

*/

#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/exec.h>
#include <sys/conf.h>
#include <sys/lkm.h>

#define MAXMSGLEN 100

struct ourdev_io {
	int value;
	char msg[MAXMSGLEN];
};

#define ODREAD		_IOR('O', 0, double)
#define ODWRITE	_IOW('O', 1, double)

// prototypes for our supported operations, namely open, close, read and ioctl 
int	ourdevopen __P((dev_t dev, int oflags, int devtype, struct proc *p));
int 	ourdevclose __P((dev_t dev, int fflag, int devtype, struct proc *p));
int	ourdevread __P((dev_t dev, struct uio *uio, int ioflag));
int	ourdevwrite __P((dev_t dev, struct uio *uio, int ioflag));
int	ourdevioctl __P((dev_t dev, u_long cmd, caddr_t data, int fflag, 
			struct proc *p));
int	ourdev_handler __P((struct lkm_table *lkmtp, int cmd));

/* 
 * struct ourdev_io is defined in common.h, our device will use the ioctl call
 * to get and set its values, along with the read operation on the device.
 */
static struct ourdev_io dio;

/* 
 * Here we initialise the operations vector for our device. 
 */
cdev_decl(ourdev);
static struct cdevsw cdev_ourdev = { 	dev_init(1,ourdev,open),
                                       dev_init(1,ourdev,close),
                                       dev_init(1,ourdev,read),
                                       dev_init(1,ourdev,write),
                                       dev_init(1,ourdev,ioctl),
                                       (dev_type_stop((*))) lkmenodev,
                                       (dev_type_mmap((*))) lkmenodev
                                    };
/* 
 * Initialise an internal structure for the lkm interface. The first argument
 * is the name of the module, which will appear when viewed with modstat.  The
 * second is the type of the device, in this case LM_DT_CHAR for a character 
 * device. Thirdly we have the position in the cdevsw[] table we want our 
 * operations structure stored. As with the system call, the value -1 means we
 * dont mind where it is and the next available slot should be allocated. 
 * Finally we pass our initialised struct cdevsw
 */
MOD_DEV("ourdev", LM_DT_CHAR, -1, &cdev_ourdev)

// The actions for when the device is opened, in this case just say hello. 
int
ourdevopen(dev, oflags, devtype, p)
	dev_t dev;
	int oflags, devtype;
	struct proc *p;
{
//	printf("device opened, hi!\n");
	return(0);
}

// Actions for when the device is closed, again just print a small message. 
int
ourdevclose(dev, fflag, devtype, p)
	dev_t dev;
	int fflag, devtype;
	struct proc *p;
{
//	printf("device closed! bye!\n");
	return(0);
}

// Actions for the read operation on the device.  Here we copy out the current
// value of the string stored in our internal struct ourdev_io. 
int
ourdevread(dev, uio, ioflag)
	dev_t dev;
	struct uio *uio;
	int	ioflag;
{
	int resid = MAXMSGLEN;
	int error = 0;

	//printf("reading...\n");
	do {
		if (uio->uio_resid < resid)
			resid = uio->uio_resid;
		error = uiomove(dio.msg, resid, uio);	
	} while (resid > 0 && error == 0);
	return(error);
}

// Actions for the writing operation on the device.
int
ourdevwrite(dev, uio, ioflag)
	dev_t dev;
	struct uio *uio;
	int	ioflag;
{
	int resid = MAXMSGLEN;
	int error = 0;
	//printf("writing...\n");
	do {
		if (uio->uio_resid < resid)
			resid = uio->uio_resid;
		error = uiomove(dio.msg, resid, uio);	
	} while (resid > 0 && error == 0);
	return(error);
}

/* 
 * Code for the ioctl operation. We define two possible operations, one for 
 * reading the current values of the internal struct ourdev_io, and one for
 * setting the values. We default to returning the error "Inappropriate ioctl 
 * for device".
 */
double testval = 3.14159;
int
ourdevioctl(dev, cmd, data, fflag, p)
	dev_t dev;
	u_long cmd;
	caddr_t data;
	int fflag;
	struct proc *p;
{
	struct ourdev_io *d;
	int error = 0;
	double *userval;

	switch(cmd) {
	case ODREAD:
		userval = (double*)data;
		*userval = testval;
		break;

	case ODWRITE:
		if ((fflag & FWRITE) == 0)
			return(EPERM);
		userval = (double*)data;
		testval = *userval;
		break;

	default:
		error = ENOTTY;
		break;
	}
	
	return(error);
}

/*
 * Our external entry point. Much like the system call example, we have a 
 * handler for when the module is loaded, but unlike the system call we take
 * no special action when the module is unloaded.
 */
int
ourdev(lkmtp, cmd, ver)
	struct lkm_table *lkmtp;
	int cmd;
	int ver;
{
	DISPATCH(lkmtp, cmd, ver, ourdev_handler, lkm_nofunc, lkm_nofunc)
}

/*
 * Our handler for when the module is loaded. We set up our internal structure
 * with some initial values, which can later be changed using ioctl(2). This 
 * will only be used when the module is loaded, but we check the action anyway.
 */
int
ourdev_handler(lkmtp, cmd)
	struct lkm_table *lkmtp;
	int cmd;
{
	struct lkm_dev *args = lkmtp->private.lkm_dev;
	if (cmd == LKM_E_LOAD) {
		dio.value = 13;
		strncpy(dio.msg,"hello world!\n", MAXMSGLEN - 1);
		printf("loading module %s\n", args->lkm_name);
	}
	return 0;
}

chardevtest.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 ODREAD	_IOR('O', 0, double)
#define ODWRITE	_IOW('O', 1, double)

int main(void) {
	int error, fd, wlen;
	double testval;
	char teststr[128];
	char mystring[] = "hello captain!";
	
	fd = open("/dev/ourdev", O_RDWR);
	if (fd == -1)
		err(1, "open");

	error = ioctl(fd, ODREAD, &testval);
	if (error == -1)
		err(1, "ioctl1");
	printf("1. %f\n", testval);

	testval = 2.718282;
	error = ioctl(fd, ODWRITE, &testval);
	if (error == -1)
		err(1, "ioctl2");

	error = ioctl(fd, ODREAD, &testval);
	if (error == -1)
		err(1, "ioctl3");
	printf("2. %f\n", testval);

	error = read(fd, teststr, 128);
	if (error == -1)
		err(1, "read");
	printf("read1: %s\n", teststr);

	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);
}
compile.sh
#!/bin/sh
cc -D_KERNEL -I/sys -c chardev.c
cc -o chardevtest chardevtest.c

dev-install.sh
#!/bin/sh
MAJOR=`modstat -n ourdev | tail -1 | awk '{print $3}'`
mknod -m 644 /dev/ourdev c $MAJOR 0
echo "created device /dev/ourdev, major number $MAJOR"
ls -l /dev/ourdev

install.sh
#!/bin/sh
modunload -n ourdev
modload -o ourdev.o -eourdev chardev.o
ls -al /dev/ourdev

test.sh
#!/bin/sh
dd if=/dev/ourdev of=/dev/fd/1 count=1 bs=100
echo "hello captain!\n" > /dev/ourdev
dd if=/dev/ourdev of=/dev/fd/1 count=1 bs=100

Last-Modified: Sat, 04 Feb 2006 16:03:05 GMT

Google
 
Web www.captain.at
go to top
© 1996-2010 . All rights reserved.
No reproduction, distribution, publishing or transmission of the copyrighted materials at this site is permitted. Policy
go to top