From owner-freebsd-hackers Sat Feb 1 21:45:12 1997 Return-Path: Received: (from root@localhost) by freefall.freebsd.org (8.8.5/8.8.5) id VAA02158 for hackers-outgoing; Sat, 1 Feb 1997 21:45:12 -0800 (PST) Received: from alpo.whistle.com (alpo.whistle.com [207.76.204.38]) by freefall.freebsd.org (8.8.5/8.8.5) with ESMTP id VAA02151 for ; Sat, 1 Feb 1997 21:45:07 -0800 (PST) Received: from current1.whistle.com (current1.whistle.com [207.76.205.22]) by alpo.whistle.com (8.8.5/8.8.4) with SMTP id VAA21438; Sat, 1 Feb 1997 21:39:28 -0800 (PST) Message-ID: <32F42817.794BDF32@whistle.com> Date: Sat, 01 Feb 1997 21:37:27 -0800 From: Julian Elischer Organization: Whistle Communications X-Mailer: Mozilla 3.0Gold (X11; I; FreeBSD 2.2-CURRENT i386) MIME-Version: 1.0 To: Michael Smith CC: "Brian J. McGovern" , hackers@freebsd.org Subject: SAMPLE DEVICE DRIVER. References: <199702020111.LAA15091@genesis.atrad.adelaide.edu.au> Content-Type: multipart/mixed; boundary="------------1CFBAE3959E2B60015FB7483" Sender: owner-hackers@freebsd.org X-Loop: FreeBSD.org Precedence: bulk This is a multi-part message in MIME format. --------------1CFBAE3959E2B60015FB7483 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Michael Smith wrote: > > I'm assuming that this is meant to be a pseudo-device, correct? > > > static struct cdevsw foo_cdevsw = > > { > > nxopen, nxclose, fooread, nxwrite, > > nxioctl, nxstop, nxreset, nxdevtotty, > > nxselect, nxmmap, NULL, "foo", NULL, -1 > > }; > > You probably want nullopen and nullclose there, so that the device can > be opened and closed. > > If this is a pseudo-device, you're going to want some code here to > install you cdevsw and any devfs nodes you plan on advertising. I > _strongly_ suggest looking at the logic in vn_drvinit, which does just > that (as well as installing a shutdown hook). Even if it's a REAL device you want that.. here is a shell script that writes andevice driver for you, configures it into the kerenl tree and compiles it for you. Once you have run this once, you should just alternate between editing the .c file it writes, and compiling the kernel.. edit /sys/i386/conf/FOO to set the correct h/w configuration. run it as follows: cp makedriver /tmp cd /tmp sh ./makedriver foo [shell script makes a foo driver..] cd /sys/compile/FOO get kernel, reboot, try.. it makes 4 files /sys/i386/conf/FOO /sys/i386/conf/files.FOO /sys/i386/isa/foo.c /sys/fooio.h this is all you need to link your kernel in.... it is for a REAL device driver (with hardware) it is an ISA driver. I'd like to make similar "builders" for PCI and EISA.. anyone care to adapt it? oh yeah this assumes you can write to your sources.. --------------1CFBAE3959E2B60015FB7483 Content-Type: text/plain; charset=us-ascii; name="makedriver" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="makedriver" #!/bin/sh # This writes a skeleton driver and puts it into the kernel tree for you #arg1 is lowercase "foo" # # 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 UPPER=`echo ${1} |tr "[:lower:]" "[:upper:]"` cat >files.${UPPER} <${UPPER} <>${UPPER} cat >>${UPPER} <../isa/${1}.c < #include #include /* SYSINIT stuff */ #include /* cdevsw stuff */ #include /* malloc region definitions */ #include /* DELAY() */ #include /* ISA bus port definitions etc. */ #include /* ISA bus configuration structures */ #include /* ${1} IOCTL definitions */ #ifdef DEVFS #include /* DEVFS defintitions */ #endif /* DEVFS */ /* Function prototypes (these should all be static except for ${1}intr()) */ 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_select_t ${1}select; static int ${1}probe (struct isa_device *); static int ${1}attach (struct isa_device *); /* void ${1}intr(int unit);*//* actually defined in ioconf.h (generated file) */ #define CDEV_MAJOR 20 static struct cdevsw ${1}_cdevsw = { ${1}open, ${1}close, ${1}read, ${1}write, ${1}ioctl, nullstop, nullreset, nodevtotty, ${1}select, ${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]; /* * 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, 0, DV_CHR, UID_ROOT, GID_KMEM, 0640, "${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 */ 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}select(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 */ } /* * Now for some driver initialisation. * Occurs ONCE during boot (very early). */ 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) DONE cat >../../sys/${1}io.h < #endif #include /* * define an ioctl here */ #define DHIOCRESET _IO('D', 0) /* reset the ${1} device */ #endif DONE config ${UPPER} cd ../../compile/${UPPER} make depend make ${1}.o make exit #--------------end of script--------------- # #you also need to add an entry into the cdevsw[] #array in conf.c, but it's too hard to do in a script.. # #edit to your taste.. # # --------------1CFBAE3959E2B60015FB7483--