xref: /freebsd/share/examples/drivers/make_device_driver.sh (revision d1ba25f456132eabc6f1244e4bbbf3d19e8f3a31)
1#!/bin/sh
2# This writes a skeleton driver and puts it into the kernel tree for you
3#arg1 is lowercase "foo"
4#
5# It also creates a directory under /usr/src/lkm to help you create
6#loadable kernel modules, though without much use except for development.
7#
8# Trust me, RUN THIS SCRIPT :)
9# $FreeBSD$"
10#
11#-------cut here------------------
12if [ "${1}X" = "X" ]
13then
14	echo "Hey , how about some help here.. give me a device name!"
15	exit 1
16fi
17UPPER=`echo ${1} |tr "[:lower:]" "[:upper:]"`
18
19HERE=`pwd`
20cd /sys
21TOP=`pwd`
22
23RCS_KEYWORD=FreeBSD
24
25echo ${TOP}/modules/${1}
26echo ${TOP}/i386/conf/files.${UPPER}
27echo ${TOP}/i386/conf/${UPPER}
28echo ${TOP}/dev/${1}
29echo ${TOP}/dev/${1}/${1}.c
30echo ${TOP}/sys/${1}io.h
31echo ${TOP}/modules/${1}
32echo ${TOP}/modules/${1}/Makefile
33
34rm -rf ${TOP}/dev/${1}
35rm -rf ${TOP}/modules/${1}
36rm ${TOP}/i386/conf/files.${UPPER}
37rm ${TOP}/i386/conf/${UPPER}
38rm ${TOP}/sys/${1}io.h
39
40if [ -d ${TOP}/modules/${1} ]
41then
42	echo "There appears to already be a module called ${1}"
43	exit 1
44else
45	mkdir ${TOP}/modules/${1}
46fi
47
48#######################################################################
49#######################################################################
50#
51# Create configuration information needed to create a kernel
52# containing this driver.
53#
54# Not really needed if we are going to do this as a module.
55#######################################################################
56# First add the file to a local file list.
57#######################################################################
58
59cat >${TOP}/i386/conf/files.${UPPER} <<DONE
60dev/${1}/${1}.c	 optional ${1}
61DONE
62
63#######################################################################
64# Then create a configuration file for a kernel that contains this driver.
65#######################################################################
66cat >${TOP}/i386/conf/${UPPER} <<DONE
67# Configuration file for kernel type: ${UPPER}
68ident	${UPPER}
69# \$${RCS_KEYWORD}: $
70DONE
71
72grep -v GENERIC < /sys/i386/conf/GENERIC >>${TOP}/i386/conf/${UPPER}
73
74cat >>${TOP}/i386/conf/${UPPER} <<DONE
75options		DDB		# trust me, you'll need this
76device		${1}
77DONE
78
79if [ ! -d ${TOP}/dev/${1} ]
80then
81	mkdir -p ${TOP}/dev/${1}
82fi
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99cat >${TOP}/dev/${1}/${1}.c <<DONE
100/*
101 * Copyright (c) [year] [your name]
102 * All rights reserved.
103 *
104 * Redistribution and use in source and binary forms, with or without
105 * modification, are permitted provided that the following conditions
106 * are met:
107 * 1. Redistributions of source code must retain the above copyright
108 *    notice, this list of conditions and the following disclaimer.
109 * 2. Redistributions in binary form must reproduce the above copyright
110 *    notice, this list of conditions and the following disclaimer in the
111 *    documentation and/or other materials provided with the distribution.
112 *
113 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
114 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
115 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
116 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
117 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
118 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
119 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
120 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
121 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
122 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
123 * SUCH DAMAGE.
124 *
125 *
126 * ${1} driver
127 * \$${RCS_KEYWORD}: $
128 */
129
130
131#include <sys/param.h>
132#include <sys/systm.h>
133#include <sys/conf.h>		/* cdevsw stuff */
134#include <sys/kernel.h>		/* SYSINIT stuff */
135#include <sys/uio.h>		/* SYSINIT stuff */
136#include <sys/malloc.h>		/* malloc region definitions */
137#include <sys/module.h>
138#include <sys/bus.h>
139#include <machine/bus.h>
140#include <machine/resource.h>
141#include <sys/rman.h>
142#include <sys/time.h>
143
144#include <isa/isavar.h>
145#include "isa_if.h"
146#include <sys/${1}io.h>		/* ${1} IOCTL definitions */
147
148#define ${UPPER}DEV2SOFTC(dev) ((dev)->si_drv1)
149#define ${UPPER}_INB(port) bus_space_read_1( bt, bh, (port))
150#define ${UPPER}_OUTB(port, val) bus_space_write_1( bt, bh, (port), (val))
151#define SOME_PORT 123
152#define EXPECTED_VALUE 0x42
153
154
155/* Function prototypes (these should all be static) */
156static int ${1}_isa_probe (device_t);
157static int ${1}_isa_attach (device_t);
158static int ${1}_isa_detach (device_t);
159static int ${1}_deallocate_resources(device_t device);
160static int ${1}_allocate_resources(device_t device);
161
162static d_open_t		${1}open;
163static d_close_t	${1}close;
164static d_read_t		${1}read;
165static d_write_t	${1}write;
166static d_ioctl_t	${1}ioctl;
167static d_mmap_t		${1}mmap;
168static d_poll_t		${1}poll;
169static	void		${1}intr(void *arg);
170 
171#define CDEV_MAJOR 20
172static struct cdevsw ${1}_cdevsw = {
173	/* open */	${1}open,
174	/* close */	${1}close,
175	/* read */	${1}read,
176	/* write */	${1}write,
177	/* ioctl */	${1}ioctl,
178	/* poll */	${1}poll,
179	/* mmap */	${1}mmap,
180	/* strategy */	nostrategy,	/* not a block type device */
181	/* name */	"${1}",
182	/* maj */	CDEV_MAJOR,
183	/* dump */	nodump,		/* not a block type device */
184	/* psize */	nopsize,	/* not a block type device */
185	/* flags */	0,
186	/* bmaj */	-1
187};
188 
189/* 
190 * device specific Misc defines 
191 */
192#define BUFFERSIZE 1024
193#define NUMPORTS 4
194#define MEMSIZE	1024*1024 /* imaginable h/w buffer size */
195
196/*
197 * One of these per allocated device
198 */
199struct ${1}_softc {
200	bus_space_tag_t bt;
201	bus_space_handle_t bh;
202	int rid_ioport;
203	int rid_memory;
204	int rid_irq;
205	int rid_drq;
206	struct resource* res_ioport;	/* resource for port range */
207	struct resource* res_memory;	/* resource for mem range */
208	struct resource* res_irq;	/* resource for irq range */
209	struct resource* res_drq;	/* resource for dma channel */
210	device_t device;
211	dev_t dev;
212	void	*intr_cookie;
213	char	buffer[BUFFERSIZE];
214} ;
215
216typedef	struct ${1}_softc *sc_p;
217
218static devclass_t ${1}_devclass;
219
220static struct isa_pnp_id ${1}_ids[] = {
221	{0x12345678, "ABCco Widget"},
222	{0xfedcba98, "shining moon Widget ripoff"},
223	{0}
224};
225
226static device_method_t ${1}_methods[] = {
227	DEVMETHOD(device_probe,		${1}_isa_probe),
228	DEVMETHOD(device_attach,	${1}_isa_attach),
229	DEVMETHOD(device_detach,	${1}_isa_detach),
230	{ 0, 0 }
231};
232
233static driver_t ${1}_isa_driver = {
234	"${1}",
235	${1}_methods,
236	sizeof (struct ${1}_softc)
237};
238
239DRIVER_MODULE(${1}, isa, ${1}_isa_driver, ${1}_devclass, 0, 0);
240
241
242/*
243 * The ISA code calls this for each device it knows about,
244 * whether via the PNP code or via the hints etc.
245 */
246static int
247${1}_isa_probe (device_t device)
248{
249	int error;
250	sc_p scp = device_get_softc(device);
251
252	bzero(scp, sizeof(*scp));
253	scp->device = device;
254
255	/*
256	 * Check for a PNP match..
257	 * There are several possible outcomes.
258	 * error == 0		We match a PNP device (possibly several?).
259	 * error == ENXIO,	It is a PNP device but not ours.
260	 * error == ENOENT,	I is not a PNP device.. try heuristic probes.
261	 *    -- logic from if_ed_isa.c, added info from isa/isa_if.m:
262	 */
263	error = ISA_PNP_PROBE(device_get_parent(device), device, ${1}_ids);
264	switch (error) {
265	case 0:
266		/*
267		 * We found a PNP device.
268		 * Do nothing, as it's all done in attach()
269		 */
270		break;
271	case ENOENT:
272		/*
273		 * Well it didn't show up in the PNP tables
274		 * so look directly at known ports (if we have any)
275		 * in case we are looking for an old pre-PNP card.
276		 *
277		 * I think the ports etc should come from a 'hints' section
278		 * buried somewhere. XXX - still not figured out.
279		 * which is read in by code in isa/isahint.c
280		 */
281#if 0 /* till I work out how to find ht eport from the hints */
282		if ( ${UPPER}_INB(SOME_PORT) != EXPECTED_VALUE) {
283			/* 
284			 * It isn't what we expected, so quit looking for it.
285			 */
286			error = ENXIO;
287		} else {
288			/*
289			 * We found one..
290			 */
291			error = 0;
292		}
293#endif
294		break;
295	case  ENXIO:
296		/* not ours, leave imediatly */
297	default:
298		error = ENXIO;
299	}
300	return (error);
301}
302
303/*
304 * Called if the probe succeeded.
305 * We can be destructive here as we know we have the device.
306 */
307static int
308${1}_isa_attach (device_t device)
309{
310	int	unit	= device_get_unit(device);
311	sc_p	scp	= device_get_softc(device);
312	device_t parent	= device_get_parent(device);
313	bus_space_handle_t  bh;
314	bus_space_tag_t bt;
315
316	scp->dev->si_drv1 = scp;
317	scp->dev = make_dev(&${1}_cdevsw, 0,
318			UID_ROOT, GID_OPERATOR, 0600, "${1}%d", unit);
319
320	if (${1}_allocate_resources(device)) {
321		goto errexit;
322	}
323
324	scp->bt = bt = rman_get_bustag(scp->res_ioport);
325	scp->bh = bh = rman_get_bushandle(scp->res_ioport);
326
327	/* register the interrupt handler */
328	if (scp->res_irq) {
329		/* default to the tty mask for registration */  /* XXX */
330		if (BUS_SETUP_INTR(parent, device, scp->res_irq, INTR_TYPE_TTY,
331				${1}intr, scp, &scp->intr_cookie) == 0) {
332			/* do something if successfull */
333		}
334	}
335	return 0;
336
337errexit:
338	/*
339	 * Undo anything we may have done
340	 */
341	${1}_isa_detach(device);
342	return (ENXIO);
343}
344
345static int
346${1}_isa_detach (device_t device)
347{
348	sc_p scp = device_get_softc(device);
349	device_t parent = device_get_parent(device);
350
351	/*
352	 * At this point stick a strong piece of wood into the device
353	 * to make sure it is stopped safely. The alternative is to 
354	 * simply REFUSE to detach if it's busy. What you do depends on 
355	 * your specific situation.
356	 */
357	/* ZAP some register */
358
359	/*
360	 * Take our interrupt handler out of the list of handlers
361	 * that can handle this irq.
362	 */
363	if (scp->intr_cookie != NULL) {
364		if (BUS_TEARDOWN_INTR(parent, device,
365			scp->res_irq, scp->intr_cookie) != 0) {
366				printf("intr teardown failed.. continuing\n");
367		}
368		scp->intr_cookie = NULL;
369	}
370
371	/*
372	 * deallocate any system resources we may have
373	 * allocated on behalf of this driver.
374	 */
375	return ${1}_deallocate_resources(device);
376}
377
378static int
379${1}_allocate_resources(device_t device)
380{
381	int error;
382	sc_p scp = device_get_softc(device);
383	int	size = 16; /* SIZE of port range used */
384
385	scp->res_ioport = bus_alloc_resource(device, SYS_RES_IOPORT,
386			&scp->rid_ioport, 0ul, ~0ul, size, RF_ACTIVE);
387	if (scp->res_ioport == NULL) {
388		goto errexit;
389	}
390
391	scp->res_irq = bus_alloc_resource(device, SYS_RES_IRQ,
392			&scp->rid_irq, 0ul, ~0ul, 1, RF_SHAREABLE);
393	if (scp->res_irq == NULL) {
394		goto errexit;
395	}
396
397	scp->res_drq = bus_alloc_resource(device, SYS_RES_DRQ,
398			&scp->rid_drq, 0ul, ~0ul, 1, RF_ACTIVE);
399	if (scp->res_drq == NULL) {
400		goto errexit;
401	}
402
403	scp->res_memory = bus_alloc_resource(device, SYS_RES_IOPORT,
404			&scp->rid_memory, 0ul, ~0ul, MSIZE, RF_ACTIVE);
405	if (scp->res_memory == NULL) {
406		goto errexit;
407	}
408
409	return (0);
410
411errexit:
412	error = ENXIO;
413	/* cleanup anything we may have assigned. */
414	${1}_deallocate_resources(device);
415	return (ENXIO); /* for want of a better idea */
416}
417
418static int
419${1}_deallocate_resources(device_t device)
420{
421	sc_p scp = device_get_softc(device);
422
423	if (scp->res_irq != 0) {
424		bus_deactivate_resource(device, SYS_RES_IRQ,
425			scp->rid_irq, scp->res_irq);
426		bus_release_resource(device, SYS_RES_IRQ,
427			scp->rid_irq, scp->res_irq);
428		scp->res_irq = 0;
429	}
430	if (scp->res_ioport != 0) {
431		bus_deactivate_resource(device, SYS_RES_IOPORT,
432			scp->rid_ioport, scp->res_ioport);
433		bus_release_resource(device, SYS_RES_IOPORT,
434			scp->rid_ioport, scp->res_ioport);
435		scp->res_ioport = 0;
436	}
437	if (scp->res_ioport != 0) {
438		bus_deactivate_resource(device, SYS_RES_MEMORY,
439			scp->rid_memory, scp->res_memory);
440		bus_release_resource(device, SYS_RES_MEMORY,
441			scp->rid_memory, scp->res_memory);
442		scp->res_ioport = 0;
443	}
444	if (scp->res_drq != 0) {
445		bus_deactivate_resource(device, SYS_RES_DRQ,
446			scp->rid_drq, scp->res_drq);
447		bus_release_resource(device, SYS_RES_DRQ,
448			scp->rid_drq, scp->res_drq);
449		scp->res_drq = 0;
450	}
451	if (scp->dev) {
452		destroy_dev(scp->dev);
453	}
454	return (0);
455}
456
457static void
458${1}intr(void *arg)
459{
460	sc_p scp	= arg;
461
462	/* 
463	 * well we got an interupt, now what?
464	 */
465	return;
466}
467
468static int
469${1}ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
470{
471	sc_p scp	= ${UPPER}DEV2SOFTC(dev);
472	bus_space_handle_t  bh = scp->bh;
473	bus_space_tag_t bt = scp->bt;
474
475	switch (cmd) {
476	case DHIOCRESET:
477		/* whatever resets it */
478		${UPPER}_OUTB(SOME_PORT, 0xff) ;
479		break;
480	default:
481		return ENXIO;
482	}
483	return (0);
484}
485/*
486 * You also need read, write, open, close routines.
487 * This should get you started
488 */
489static int
490${1}open(dev_t dev, int oflags, int devtype, struct proc *p)
491{
492	sc_p scp	= ${UPPER}DEV2SOFTC(dev);
493
494	/* 
495	 * Do processing
496	 */
497	return (0);
498}
499
500static int
501${1}close(dev_t dev, int fflag, int devtype, struct proc *p)
502{
503	sc_p scp	= ${UPPER}DEV2SOFTC(dev);
504
505	/* 
506	 * Do processing
507	 */
508	return (0);
509}
510
511static int
512${1}read(dev_t dev, struct uio *uio, int ioflag)
513{
514	sc_p scp	= ${UPPER}DEV2SOFTC(dev);
515	int	 toread;
516
517
518	/* 
519	 * Do processing
520	 * read from buffer
521	 */
522	toread = (min(uio->uio_resid, sizeof(scp->buffer)));
523	return(uiomove(scp->buffer, toread, uio));
524}
525
526static int
527${1}write(dev_t dev, struct uio *uio, int ioflag)
528{
529	sc_p scp	= ${UPPER}DEV2SOFTC(dev);
530	int	towrite;
531
532	/* 
533	 * Do processing
534	 * write to buffer
535	 */
536	towrite = (min(uio->uio_resid, sizeof(scp->buffer)));
537	return(uiomove(scp->buffer, towrite, uio));
538}
539
540static int
541${1}mmap(dev_t dev, vm_offset_t offset, int nprot)
542{
543	sc_p scp	= ${UPPER}DEV2SOFTC(dev);
544
545	/* 
546	 * Do processing
547	 */
548#if 0	/* if we had a frame buffer or whatever.. do this */
549	if (offset > FRAMEBUFFERSIZE - PAGE_SIZE) {
550		return (-1);
551	}
552	return i386_btop((FRAMEBASE + offset));
553#else
554	return (-1);
555#endif
556}
557
558static int
559${1}poll(dev_t dev, int which, struct proc *p)
560{
561	sc_p scp	= ${UPPER}DEV2SOFTC(dev);
562
563	/* 
564	 * Do processing
565	 */
566	return (0); /* this is the wrong value I'm sure */
567}
568
569DONE
570
571cat >${TOP}/sys/${1}io.h <<DONE
572/*
573 * Definitions needed to access the ${1} device (ioctls etc)
574 * see mtio.h , ioctl.h as examples
575 */
576#ifndef SYS_DHIO_H
577#define SYS_DHIO_H
578
579#ifndef KERNEL
580#include <sys/types.h>
581#endif
582#include <sys/ioccom.h>
583
584/*
585 * define an ioctl here
586 */
587#define DHIOCRESET _IO('D', 0) /* reset the ${1} device */
588#endif
589DONE
590
591if [ ! -d ${TOP}/modules/${1} ]
592then
593	mkdir -p ${TOP}/modules/${1}
594fi
595
596cat >${TOP}/modules/${1}/Makefile <<DONE
597#	${UPPER} Loadable Kernel Module
598#
599# \$${RCS_KEYWORD}: $
600 
601.PATH:  \${.CURDIR}/../../dev/${1}
602KMOD    = ${1}
603SRCS    = ${1}.c
604SRCS    += opt_inet.h device_if.h bus_if.h pci_if.h isa_if.h
605  
606# you may need to do this is your device is an if_xxx driver
607opt_inet.h:
608	echo "#define INET 1" > opt_inet.h
609	   
610.include <bsd.kmod.mk>
611DONE
612
613(cd ${TOP}/modules/${1}; make depend; make )
614exit
615
616config ${UPPER}
617cd ../../compile/${UPPER}
618make depend
619make ${1}.o
620make
621exit
622
623#--------------end of script---------------
624#
625#edit to your taste..
626#
627#
628
629
630
631
632