#!/bin/sh
# This writes a skeleton driver and puts it into the kernel tree for you
#
# arg1 is lowercase "foo"
# arg2 path to the kernel sources, "/sys" if omitted
#
# Trust me, RUN THIS SCRIPT :)
#
# $FreeBSD$
#
#-------cut here------------------

if [ "${1}X" = "X" ]
then
	echo "Hey , how about some help here.. give me a device name!"
	exit 1
fi
if [ "X${2}" = "X" ]; then
	TOP=`cd /sys; pwd -P`
	echo "Using ${TOP} as the path to the kernel sources!"
else
	TOP=${2}
fi

for i in "" "conf" "i386" "i386/conf" "dev" "sys" "modules"
do
	if [ -d ${TOP}/${i} ]
	then
		continue
	fi
	echo "${TOP}/${i}: no such directory."
	echo "Please, correct the error and try again."
	exit 1
done

UPPER=`echo ${1} |tr "[:lower:]" "[:upper:]"`

if [ -d ${TOP}/modules/${1} ]; then
	echo "There appears to already be a module called ${1}"
	echo -n "Should it be overwritten? [Y]"
	read VAL
	if [ "-z" "$VAL" ]; then
		VAL=YES
	fi
	case ${VAL} in
	[yY]*)
		echo "Cleaning up from prior runs"
		rm -rf ${TOP}/dev/${1}
		rm -rf ${TOP}/modules/${1}
		rm ${TOP}/conf/files.${UPPER}
		rm ${TOP}/i386/conf/${UPPER}
		rm ${TOP}/sys/${1}io.h
		;;
	*)
		exit 1
		;;
	esac
fi

echo "The following files will be created:"
echo ${TOP}/modules/${1}
echo ${TOP}/conf/files.${UPPER}
echo ${TOP}/i386/conf/${UPPER}
echo ${TOP}/dev/${1}
echo ${TOP}/dev/${1}/${1}.c
echo ${TOP}/sys/${1}io.h
echo ${TOP}/modules/${1}
echo ${TOP}/modules/${1}/Makefile

mkdir ${TOP}/modules/${1}

cat >${TOP}/conf/files.${UPPER} <<DONE
dev/${1}/${1}.c      optional ${1}
DONE

cat >${TOP}/i386/conf/${UPPER} <<DONE
# Configuration file for kernel type: ${UPPER}
# \$FreeBSD\$

files		"${TOP}/conf/files.${UPPER}"

include		GENERIC

ident		${UPPER}

# trust me, you'll need this
options		KDB
options		DDB
device		${1}
DONE

if [ ! -d ${TOP}/dev/${1} ]; then
	mkdir -p ${TOP}/dev/${1}
fi

cat >${TOP}/dev/${1}/${1}.c <<DONE
/*
 * Copyright (c) [year] [your name]
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * ${1} driver
 */

#include <sys/cdefs.h>
__FBSDID("\$FreeBSD\$");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>		/* SYSINIT stuff */
#include <sys/uio.h>		/* SYSINIT stuff */
#include <sys/conf.h>		/* cdevsw stuff */
#include <sys/malloc.h>		/* malloc region definitions */
#include <sys/proc.h>
#include <sys/${1}io.h>		/* ${1} IOCTL definitions */

#include <machine/clock.h>	/* DELAY() */

#define N${UPPER}	3	/* defines number of instances */

/* XXX These should be defined in terms of bus-space ops. */
#define ${UPPER}_INB(port) inb(port)
#define ${UPPER}_OUTB(port, val) (port, (val))

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

#define CDEV_MAJOR 20
static struct cdevsw ${1}_cdevsw = {
	.d_version =	D_VERSION,
	.d_open =	${1}open,
	.d_close =	${1}close,
	.d_read =	${1}read,
	.d_write =	${1}write,
	.d_ioctl =	${1}ioctl,
	.d_poll =	${1}poll,
	.d_mmap =	${1}mmap,
	.d_name =	"${1}",
};

/*
 * device  specific Misc defines
 */
#define BUFFERSIZE 1024
#define UNIT(dev) dev2unit(dev)	/* assume one minor number per unit */

/*
 * One of these per allocated device
 */
struct ${1}_softc {
	u_long	iobase;
	char	buffer[BUFFERSIZE];
  	struct cdev *dev;
};

typedef	struct ${1}_softc *sc_p;

static sc_p sca[N${UPPER}];

/*
 * 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("%s: bad unit %d\n", __func__, unit);		\
		return (RETVAL);					\
	}								\
	if (scp == NULL) { 						\
		printf("%s: unit %d not attached\n", __func__, unit);	\
		return (RETVAL);					\
	}								\
} while (0)

#ifdef	DIAGNOSTIC
#define	CHECKUNIT_DIAG(RETVAL) CHECKUNIT(RETVAL)
#else	/* DIAGNOSTIC */
#define	CHECKUNIT_DIAG(RETVAL)
#endif 	/* DIAGNOSTIC */

static int
${1}ioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
{
	int unit = UNIT(dev);
	sc_p scp  = sca[unit];

	CHECKUNIT_DIAG(ENXIO);

	switch (cmd) {
	    case DHIOCRESET:
		/*  whatever resets it */
		(void)scp; /* Delete this line after using scp. */
#if 0
		${UPPER}_OUTB(scp->iobase, 0xff);
#endif
		break;
	    default:
		return ENXIO;
	}
	return (0);
}

/*
 * You also need read, write, open, close routines.
 * This should get you started
 */
static int
${1}open(struct cdev *dev, int oflags, int devtype, struct thread *td)
{
	int unit = UNIT(dev);
	sc_p scp  = sca[unit];

	CHECKUNIT(ENXIO);

	(void)scp; /* Delete this line after using scp. */
	/*
	 * Do processing
	 */
	return (0);
}

static int
${1}close(struct cdev *dev, int fflag, int devtype, struct thread *td)
{
	int unit = UNIT(dev);
	sc_p scp  = sca[unit];

	CHECKUNIT_DIAG(ENXIO);

	(void)scp; /* Delete this line after using scp. */
	/*
	 * Do processing
	 */
	return (0);
}

static int
${1}read(struct cdev *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(struct cdev *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(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
{
	int unit = UNIT(dev);
	sc_p scp  = sca[unit];

	CHECKUNIT_DIAG(-1);

	(void)scp; /* Delete this line after using scp. */
	/*
	 * 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(struct cdev *dev, int which, struct thread *td)
{
	int unit = UNIT(dev);
	sc_p scp  = sca[unit];

	CHECKUNIT_DIAG(ENXIO);

	(void)scp; /* Delete this line after using scp. */
	/*
	 * Do processing
	 */
	return (0); /* this is the wrong value I'm sure */
}

/*
 * Now  for some driver initialisation.
 * Occurs ONCE during boot (very early).
 */
static void
${1}_drvinit(void *unused)
{
	int	unit;
	sc_p	scp;

	for (unit = 0; unit < N${UPPER}; unit++) {
		/*
		 * Allocate storage for this instance .
		 */
		scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT | M_ZERO);
		if( scp == NULL) {
			printf("${1}%d failed to allocate strorage\n", unit);
			return;
		}
		sca[unit] = scp;
    		scp->dev = make_dev(&${1}_cdevsw, unit,
			UID_ROOT, GID_KMEM, 0640, "${1}%d", unit);
	}
}

SYSINIT(${1}dev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR,
		${1}_drvinit, NULL);
DONE

cat >${TOP}/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 ${TOP}/modules/${1} ]; then
	mkdir -p ${TOP}/modules/${1}
fi

cat >${TOP}/modules/${1}/Makefile <<DONE
#	${UPPER} Loadable Kernel Module
#
# \$FreeBSD\$

.PATH:  \${.CURDIR}/../../dev/${1}
KMOD    = ${1}
SRCS    = ${1}.c

.include <bsd.kmod.mk>
DONE

echo -n "Do you want to build the '${1}' module? [Y]"
read VAL
if [ "-z" "$VAL" ]; then
	VAL=YES
fi
case ${VAL} in
[yY]*)
	(cd ${TOP}/modules/${1}; make depend; make )
	;;
*)
#	exit
	;;
esac

echo ""
echo -n "Do you want to build the '${UPPER}' kernel? [Y]"
read VAL
if [ "-z" "$VAL" ]; then
	VAL=YES
fi
case ${VAL} in
[yY]*)
	(
	 cd ${TOP}/i386/conf; \
	 config ${UPPER}; \
	 cd ${TOP}/i386/compile/${UPPER}; \
	 make depend; \
	 make; \
	)
	;;
*)
#	exit
	;;
esac

#--------------end of script---------------
#
#edit to your taste..
#
#
