freebsd-src/share/examples/drivers/make_device_driver.sh
Bruce Evans 64b2faf8a4 Removed all `vector xxxintr' specifications. Interrupt handlers are now
configured in drivers.

Attempted to update the generated interrupt handler attachment to the
current "temporary" method.  Not tested.  To test it, someone would first
have to fix the bitrot in the ioctl command arg type.
1998-10-22 16:10:29 +00:00

502 lines
10 KiB
Bash
Executable file

#!/bin/sh
# This writes a skeleton driver and puts it into the kernel tree for you
#arg1 is lowercase "foo"
#
# It also creates a directory under /usr/src/lkm to help you create
#loadable kernel modules, though without much use except for development.
#
# Trust me, RUN THIS SCRIPT :)
#
#-------cut here------------------
cd /sys/i386/conf
if [ "${1}X" = "X" ]
then
echo "Hey , how about some help here.. give me a device name!"
exit 1
fi
if [ -d /usr/src/lkm ]
then
mkdir /usr/src/lkm/${1}
fi
UPPER=`echo ${1} |tr "[:lower:]" "[:upper:]"`
cat >files.${UPPER} <<DONE
i386/isa/${1}.c optional ${1} device-driver
DONE
cat >${UPPER} <<DONE
# Configuration file for kernel type: ${UPPER}
ident ${UPPER}
# \$Id: make_device_driver.sh,v 1.3 1998/01/12 07:47:03 julian Exp $"
DONE
grep -v GENERIC < GENERIC >>${UPPER}
cat >>${UPPER} <<DONE
# trust me, you'll need this
options DDB
device ${1}0 at isa? port 0x234 bio irq 5
DONE
cat >../isa/${1}.c <<DONE
/*
* Copyright ME
*
* ${1} driver
* \$Id: make_device_driver.sh,v 1.3 1998/01/12 07:47:03 julian Exp $
*/
#include "${1}.h" /* generated file.. defines N${UPPER} */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h> /* SYSINIT stuff */
#include <sys/conf.h> /* cdevsw stuff */
#include <sys/malloc.h> /* malloc region definitions */
#include <machine/clock.h> /* DELAY() */
#include <i386/isa/isa.h> /* ISA bus port definitions etc. */
#include <i386/isa/isa_device.h>/* ISA bus configuration structures */
#include <sys/${1}io.h> /* ${1} IOCTL definitions */
#ifdef DEVFS
#include <sys/devfsext.h> /* DEVFS defintitions */
#endif /* DEVFS */
/* Function prototypes (these should all be static) */
static d_open_t ${1}open;
static d_close_t ${1}close;
static d_read_t ${1}read;
static d_write_t ${1}write;
static d_ioctl_t ${1}ioctl;
static d_mmap_t ${1}mmap;
static d_poll_t ${1}poll;
static int ${1}probe (struct isa_device *);
static int ${1}attach (struct isa_device *);
#ifdef ${UPPER}_MODULE
static ointhand2_t ${1}intr; /* should actually have type inthand2_t */
#endif
#define CDEV_MAJOR 20
static struct cdevsw ${1}_cdevsw = {
${1}open,
${1}close,
${1}read,
${1}write,
${1}ioctl,
nullstop,
nullreset,
nodevtotty,
${1}poll,
${1}mmap,
NULL,
"${1}",
NULL,
-1 };
struct isa_driver ${1}driver = {
${1}probe,
${1}attach,
"${1}" };
/*
* device specific Misc defines
*/
#define BUFFERSIZE 1024
#define NUMPORTS 4
#define UNIT(dev) minor(dev) /* assume one minor number per unit */
/*
* One of these per allocated device
*/
struct ${1}_softc {
struct isa_device *dev;
char buffer[BUFFERSIZE];
#ifdef DEVFS
static void *devfs_token;
#endif
} ;
typedef struct ${1}_softc *sc_p;
static sc_p sca[N${UPPER}];
/* add your own test to see if it exists */
/* should return the number of ports needed */
static int
${1}probe (struct isa_device *dev)
{
char val;
int unit = dev->id_unit;
sc_p scp = sca[unit];
/*
* Check the unit makes sense.
*/
if (unit > N${UPPER}) {
printf("bad unit (%d)\n", unit);
return (0);
}
if (scp) {
printf("unit $d already attached\n", unit);
return (0);
}
/*
* try see if the device is there.
*/
val = inb (dev->id_iobase);
if ( val != 42 ) {
return (0);
}
/*
* ok, we got one we think
* do some further (this time possibly destructive) tests.
*/
outb (dev->id_iobase, 0xff);
DELAY (10000); /* 10 ms delay */
val = inb (dev->id_iobase) & 0x0f;
return ((val & 0x0f) == 0x0f)? NUMPORTS : 0 ;
}
/*
* Called if the probe succeeded.
* We can be destructive here as we know we have the device.
* we can also trust the unit number.
*/
static int
${1}attach (struct isa_device *dev)
{
int unit = dev->id_unit;
sc_p scp = sca[unit];
/*
* Attach our interrupt handler to the device struct. Our caller
* will attach it to the hardware soon after we return.
*/
dev->id_ointr = ${1}intr;
/*
* Allocate storage for this instance .
*/
scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT);
if( scp == NULL) {
printf("${1}%d failed to allocage driver strorage\n", unit);
return (0);
}
bzero(scp, sizeof(*scp));
sca[unit] = scp;
/*
* Store whatever seems wise.
*/
scp->dev = dev;
#if DEVFS
scp->devfs_token = devfs_add_devswf(&${1}_cdevsw, unit, DV_CHR,
UID_ROOT, GID_KMEM, 0600, "${1}%d", unit);
#endif
return 1;
}
/*
* Macro to check that the unit number is valid
* Often this isn't needed as once the open() is performed,
* the unit number is pretty much safe.. The exception would be if we
* implemented devices that could "go away". in which case all these routines
* would be wise to check the number, DIAGNOSTIC or not.
*/
#define CHECKUNIT(RETVAL) \
do { /* the do-while is a safe way to do this grouping */ \
if (unit > N${UPPER}) { \
printf(__FUNCTION__ ":bad unit $d\n", unit); \
return (RETVAL); \
} \
if (scp == NULL) { \
printf( __FUNCTION__ ": unit $d not attached\n", unit);\
return (RETVAL); \
} \
} while (0)
#ifdef DIAGNOSTIC
#define CHECKUNIT_DIAG(RETVAL) CHECKUNIT(RETVAL)
#else /* DIAGNOSTIC */
#define CHECKUNIT_DIAG(RETVAL)
#endif /* DIAGNOSTIC */
static void
${1}intr(int unit)
{
sc_p scp = sca[unit];
/*
* well we got an interupt, now what?
* Theoretically we don't need to check the unit.
*/
return;
}
int ${1}ioctl (dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
CHECKUNIT_DIAG(ENXIO);
switch (cmd) {
case DHIOCRESET:
/* whatever resets it */
outb(scp->dev->id_iobase, 0xff);
break;
default:
return ENXIO;
}
return (0);
}
/*
* You also need read, write, open, close routines.
* This should get you started
*/
static int
${1}open(dev_t dev, int oflags, int devtype, struct proc *p)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
CHECKUNIT(ENXIO);
/*
* Do processing
*/
return (0);
}
static int
${1}close(dev_t dev, int fflag, int devtype, struct proc *p)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
CHECKUNIT_DIAG(ENXIO);
/*
* Do processing
*/
return (0);
}
static int
${1}read(dev_t dev, struct uio *uio, int ioflag)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
int toread;
CHECKUNIT_DIAG(ENXIO);
/*
* Do processing
* read from buffer
*/
toread = (min(uio->uio_resid, sizeof(scp->buffer)));
return(uiomove(scp->buffer, toread, uio));
}
static int
${1}write(dev_t dev, struct uio *uio, int ioflag)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
int towrite;
CHECKUNIT_DIAG(ENXIO);
/*
* Do processing
* write to buffer
*/
towrite = (min(uio->uio_resid, sizeof(scp->buffer)));
return(uiomove(scp->buffer, towrite, uio));
}
static int
${1}mmap(dev_t dev, int offset, int nprot)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
CHECKUNIT_DIAG(-1);
/*
* Do processing
*/
#if 0 /* if we had a frame buffer or whatever.. do this */
if (offset > FRAMEBUFFERSIZE - PAGE_SIZE) {
return (-1);
}
return i386_btop((FRAMEBASE + offset));
#else
return (-1);
#endif
}
static int
${1}poll(dev_t dev, int which, struct proc *p)
{
int unit = UNIT (dev);
sc_p scp = sca[unit];
CHECKUNIT_DIAG(ENXIO);
/*
* Do processing
*/
return (0); /* this is the wrong value I'm sure */
}
#ifndef ${UPPER}_MODULE
/*
* Now for some driver initialisation.
* Occurs ONCE during boot (very early).
* This is if we are NOT a loadable module.
*/
static void
${1}_drvinit(void *unused)
{
dev_t dev;
dev = makedev(CDEV_MAJOR, 0);
cdevsw_add(&dev, &${1}_cdevsw, NULL);
}
SYSINIT(${1}dev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR,
${1}_drvinit, NULL)
#else /* ${UPPER}_MODULE */
/* Here is the support for if we ARE a loadable kernel module */
#include <sys/exec.h>
#include <sys/sysent.h>
#include <sys/lkm.h>
MOD_DEV (${1}, LM_DT_CHAR, CDEV_MAJOR, &${1}_cdevsw);
static struct isa_device dev = {0, &${1}driver, BASE_IO, IRQ, DMA, (caddr_t) PHYS_IO, PHYS_IO_SIZE, INT_INT, 0, FLAGS, 0, 0, 0, 0, 1, 0, 0};
static int
${1}_load (struct lkm_table *lkmtp, int cmd)
{
if (${1}probe (&dev)) {
${1}attach (&dev);
uprintf ("${1} driver loaded\n");
uprintf ("${1}: interrupts not hooked\n");
return 0;
} else {
uprintf ("${1} driver: probe failed\n");
return 1;
}
}
static int
${1}_unload (struct lkm_table *lkmtp, int cmd)
{
uprintf ("${1} driver unloaded\n");
return 0;
}
static int
${1}_stat (struct lkm_table *lkmtp, int cmd)
{
return 0;
}
int
${1}_mod (struct lkm_table *lkmtp, int cmd, int ver)
{
MOD_DISPATCH(${1}, lkmtp, cmd, ver,
${1}_load, ${1}_unload, ${1}_stat);
}
#endif /* ${UPPER}_MODULE */
DONE
cat >../../sys/${1}io.h <<DONE
/*
* Definitions needed to access the ${1} device (ioctls etc)
* see mtio.h , ioctl.h as examples
*/
#ifndef SYS_DHIO_H
#define SYS_DHIO_H
#ifndef KERNEL
#include <sys/types.h>
#endif
#include <sys/ioccom.h>
/*
* define an ioctl here
*/
#define DHIOCRESET _IO('D', 0) /* reset the ${1} device */
#endif
DONE
if [ -d /usr/src/lkm/${1} ]
then
cat >/usr/src/lkm/${1}/Makefile <<DONE
# ${UPPER} Loadable Kernel Module
#
# This happens not to work, actually. It's written for
# a character ISA device driver, but they cannot be
# be made into lkm's, because you have to hard code
# everything you'll otherwise enter into the kernel
# configuration file.
.PATH: \${.CURDIR}/../../sys/i386/isa
KMOD = ${1}_mod
SRCS = ${1}.c ${1}.h
CFLAGS += -I. -D${UPPER}_MODULE
CLEANFILES += ${1}.h
BASE_IO=0 # Base IO address
IRQ=0 # IRQ number
DMA=-1 # DMA channel
PHYS_IO=0 # Physical IO Memory base address
PHYS_IO_SIZE=0 # Physical IO Memory size
INT_INT=0 # Interrupt interface
FLAGS=0 # Flags
CFLAGS+= -DBASE_IO=\${BASE_IO} -DIRQ=\${IRQ} -DDMA=\${DMA} -DPHYS_IO=\${PHYS_IO} -DPHYS_IO_SIZE=\${PHYS_IO_SIZE} -DINT_INT=\${INT_INT} -DFLAGS=\${FLAGS}
${1}.h:
echo "#define N${UPPER} 1" > ${1}.h
afterinstall:
\${INSTALL} -c -o \${BINOWN} -g \${BINGRP} -m \${BINMODE} \
\${.CURDIR}/${1} \${DESTDIR}/usr/bin
.include <bsd.kmod.mk>
DONE
fi
config ${UPPER}
cd ../../compile/${UPPER}
make depend
make ${1}.o
make
exit
#--------------end of script---------------
#
#edit to your taste..
#
#