Index: head/lib/libc/stdlib/qsort.c =================================================================== --- head/lib/libc/stdlib/qsort.c (revision 17970) +++ head/lib/libc/stdlib/qsort.c (revision 17971) @@ -1,178 +1,178 @@ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) #if 0 static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; #endif static const char rcsid[] = - "$Id: qsort.c,v 1.3 1995/12/26 13:24:58 bde Exp $"; + "$Id: qsort.c,v 1.4 1996/04/19 18:40:20 bde Exp $"; #endif /* LIBC_SCCS and not lint */ #include typedef int cmp_t __P((const void *, const void *)); static inline char *med3 __P((char *, char *, char *, cmp_t *)); static inline void swapfunc __P((char *, char *, int, int)); #define min(a, b) (a) < (b) ? a : b /* * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". */ #define swapcode(TYPE, parmi, parmj, n) { \ long i = (n) / sizeof (TYPE); \ register TYPE *pi = (TYPE *) (parmi); \ register TYPE *pj = (TYPE *) (parmj); \ do { \ register TYPE t = *pi; \ *pi++ = *pj; \ *pj++ = t; \ } while (--i > 0); \ } #define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; static inline void swapfunc(a, b, n, swaptype) char *a, *b; int n, swaptype; { if(swaptype <= 1) swapcode(long, a, b, n) else swapcode(char, a, b, n) } #define swap(a, b) \ if (swaptype == 0) { \ long t = *(long *)(a); \ *(long *)(a) = *(long *)(b); \ *(long *)(b) = t; \ } else \ swapfunc(a, b, es, swaptype) #define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) static inline char * med3(a, b, c, cmp) char *a, *b, *c; cmp_t *cmp; { return cmp(a, b) < 0 ? (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a )) :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); } void qsort(a, n, es, cmp) void *a; size_t n, es; cmp_t *cmp; { char *pa, *pb, *pc, *pd, *pl, *pm, *pn; int d, r, swaptype, swap_cnt; loop: SWAPINIT(a, es); swap_cnt = 0; if (n < 7) { - for (pm = a + es; pm < (char *) a + n * es; pm += es) - for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; pl -= es) swap(pl, pl - es); return; } - pm = a + (n / 2) * es; + pm = (char *)a + (n / 2) * es; if (n > 7) { pl = a; - pn = a + (n - 1) * es; + pn = (char *)a + (n - 1) * es; if (n > 40) { d = (n / 8) * es; pl = med3(pl, pl + d, pl + 2 * d, cmp); pm = med3(pm - d, pm, pm + d, cmp); pn = med3(pn - 2 * d, pn - d, pn, cmp); } pm = med3(pl, pm, pn, cmp); } swap(a, pm); - pa = pb = a + es; + pa = pb = (char *)a + es; - pc = pd = a + (n - 1) * es; + pc = pd = (char *)a + (n - 1) * es; for (;;) { while (pb <= pc && (r = cmp(pb, a)) <= 0) { if (r == 0) { swap_cnt = 1; swap(pa, pb); pa += es; } pb += es; } while (pb <= pc && (r = cmp(pc, a)) >= 0) { if (r == 0) { swap_cnt = 1; swap(pc, pd); pd -= es; } pc -= es; } if (pb > pc) break; swap(pb, pc); swap_cnt = 1; pb += es; pc -= es; } if (swap_cnt == 0) { /* Switch to insertion sort */ - for (pm = a + es; pm < (char *) a + n * es; pm += es) - for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; pl -= es) swap(pl, pl - es); return; } - pn = a + n * es; + pn = (char *)a + n * es; r = min(pa - (char *)a, pb - pa); vecswap(a, pb - r, r); r = min(pd - pc, pn - pd - es); vecswap(pb, pn - r, r); if ((r = pb - pa) > es) qsort(a, r / es, es, cmp); if ((r = pd - pc) > es) { /* Iterate rather than recurse to save stack space */ a = pn - r; n = r / es; goto loop; } /* qsort(pn - r, r / es, es, cmp);*/ } Index: head/share/mk/bsd.kmod.mk =================================================================== --- head/share/mk/bsd.kmod.mk (revision 17970) +++ head/share/mk/bsd.kmod.mk (revision 17971) @@ -1,212 +1,212 @@ # From: @(#)bsd.prog.mk 5.26 (Berkeley) 6/25/91 -# $Id: bsd.kmod.mk,v 1.23 1996/06/24 04:24:00 jkh Exp $ +# $Id: bsd.kmod.mk,v 1.24 1996/06/30 22:16:19 jkh Exp $ # # The include file handles installing Loadable Kernel Modules. # includes the file named "../Makefile.inc" if it exists, # as well as the include file , , and # may be # # # +++ variables +++ # # CLEANFILES Additional files to remove for the clean and cleandir targets. # # DISTRIBUTION Name of distribution. [bin] # # EXPORT_SYMS ??? # # KERN Main Kernel source directory. [${.CURDIR}/../../sys/kern] # # KMOD The name of the loadable kernel module to build. # # KMODDIR Base path for loadable kernel modules # (see lkm(4)). [/lkm] # # KMODOWN LKM owner. [${BINOWN}] # # KMODGRP LKM group. [${BINGRP}] # # KMODMODE LKM mode. [${BINMODE}] # # LINKS The list of LKM links; should be full pathnames, the # linked-to file coming first, followed by the linked # file. The files are hard-linked. For example, to link # /lkm/master and /lkm/meister, use: # # LINKS= /lkm/master /lkm/meister # # LN_FLAGS Flags for ln(1) (see variable LINKS) # # NOMAN LKM does not have a manual page if set. # # PROG The name of the loadable kernel module to build. # If not supplied, ${KMOD} is used. # # PSEUDO_LKM ??? # # SRCS List of source files # # SUBDIR A list of subdirectories that should be built as well. # Each of the targets will execute the same target in the # subdirectories. # # DESTDIR, DISTDIR are set by other Makefiles (e.g. bsd.own.mk) # # # +++ targets +++ # # distribute: # This is a variant of install, which will # put the stuff into the right "distribution". # # install: # install the program and its manual pages; if the Makefile # does not itself define the target install, the targets # beforeinstall and afterinstall may also be used to cause # actions immediately before and after the install target # is executed. # # load: # Load LKM. # # tags: # Create a tags file for the source files. # # unload: # Unload LKM. # # bsd.obj.mk: clean, cleandir and obj # bsd.dep.mk: depend # bsd.man.mk: maninstall # .if exists(${.CURDIR}/../Makefile.inc) .include "${.CURDIR}/../Makefile.inc" .endif .SUFFIXES: .out .o .c .cc .cxx .C .y .l .s .S # # Assume that we are in /usr/src/foo/bar, so /sys is # ${.CURDIR}/../../sys. We don't bother adding a .PATH since nothing # actually lives in /sys directly. # CWARNFLAGS?= -W -Wreturn-type -Wcomment -Wredundant-decls -Wimplicit \ -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes \ - -Winline -Wunused + -Winline -Wunused -Wpointer-arith CFLAGS+=${COPTS} -DKERNEL -DACTUALLY_LKM_NOT_KERNEL -I${.CURDIR}/../../sys \ ${CWARNFLAGS} EXPORT_SYMS?= _${KMOD} .if defined(VFS_LKM) CFLAGS+= -DVFS_LKM -DMODVNOPS=${KMOD}vnops -I. SRCS+= vnode_if.h CLEANFILES+= vnode_if.h vnode_if.c .endif .if defined(PSEUDO_LKM) CFLAGS+= -DPSEUDO_LKM .endif DPSRCS+= ${SRCS:M*.h} OBJS+= ${SRCS:N*.h:R:S/$/.o/g} .if !defined(PROG) PROG= ${KMOD}.o .endif ${PROG}: ${DPSRCS} ${OBJS} ${DPADD} ${LD} -r ${LDFLAGS} -o tmp.o ${OBJS} .if defined(EXPORT_SYMS) @rm -f symb.tmp @for i in ${EXPORT_SYMS} ; do echo $$i >> symb.tmp ; done symorder -c symb.tmp tmp.o @rm -f symb.tmp .endif mv tmp.o ${.TARGET} .if !defined(NOMAN) .include .if !defined(_MANPAGES) || empty(_MANPAGES) MAN1= ${KMOD}.4 .endif .elif !target(maninstall) maninstall: _SUBDIR all-man: .endif .MAIN: all all: ${PROG} all-man _SUBDIR CLEANFILES+=${PROG} ${OBJS} .if !target(install) .if !target(beforeinstall) beforeinstall: .endif .if !target(afterinstall) afterinstall: .endif realinstall: _SUBDIR ${INSTALL} ${COPY} -o ${KMODOWN} -g ${KMODGRP} -m ${KMODMODE} \ ${INSTALLFLAGS} ${PROG} ${DESTDIR}${KMODDIR} .if defined(LINKS) && !empty(LINKS) @set ${LINKS}; \ while test $$# -ge 2; do \ l=${DESTDIR}$$1; \ shift; \ t=${DESTDIR}$$1; \ shift; \ ${ECHO} $$t -\> $$l; \ rm -f $$t; \ ln ${LN_FLAGS} $$l $$t; \ done; true .endif install: afterinstall _SUBDIR .if !defined(NOMAN) afterinstall: realinstall maninstall .else afterinstall: realinstall .endif realinstall: beforeinstall .endif DISTRIBUTION?= bin .if !target(distribute) distribute: _SUBDIR cd ${.CURDIR} ; $(MAKE) install DESTDIR=${DISTDIR}/${DISTRIBUTION} SHARED=copies .endif .if !target(tags) tags: ${SRCS} _SUBDIR .if defined(PROG) -cd ${.CURDIR}; ctags -f /dev/stdout ${.ALLSRC} | \ sed "s;\${.CURDIR}/;;" > tags .endif .endif .if !target(load) load: ${PROG} /sbin/modload -o ${KMOD} -e${KMOD} ${PROG} .endif .if !target(unload) unload: ${PROG} /sbin/modunload -n ${KMOD} .endif KERN= ${.CURDIR}/../../sys/kern vnode_if.h: ${KERN}/vnode_if.sh ${KERN}/vnode_if.src sh ${KERN}/vnode_if.sh ${KERN}/vnode_if.src ./vnode_if.h: vnode_if.h .include .include Index: head/sys/conf/Makefile.i386 =================================================================== --- head/sys/conf/Makefile.i386 (revision 17970) +++ head/sys/conf/Makefile.i386 (revision 17971) @@ -1,192 +1,192 @@ # Makefile.i386 -- with config changes. # Copyright 1990 W. Jolitz # from: @(#)Makefile.i386 7.1 5/10/91 -# $Id: Makefile.i386,v 1.85 1996/06/12 05:54:46 gpalmer Exp $ +# $Id: Makefile.i386,v 1.86 1996/08/21 16:31:34 ache Exp $ # # Makefile for FreeBSD # # This makefile is constructed from a machine description: # config machineid # Most changes should be made in the machine description # /sys/i386/conf/``machineid'' # after which you should do # config machineid # Generic makefile changes should be made in # /sys/i386/conf/Makefile.i386 # after which config should be rerun for all machines. # CC?= cc CPP?= cpp LD?= /usr/bin/ld .if exists(./@/.) S= ./@ .else S= ../.. .endif I386= ${S}/i386 CWARNFLAGS?= -W -Wreturn-type -Wcomment -Wredundant-decls -Wimplicit \ -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes \ - -Winline -Wunused + -Winline -Wunused -Wpointer-arith # # The following flags are next up for working on: # -Wall # # When working on removing warnings from code, the `-Werror' flag should be # of material assistance. # COPTFLAGS?=-O # Not ready for -I- yet. #include "foo.h" where foo.h is in the srcdir fails. INCLUDES= -nostdinc -I. -I$S -I$S/sys # This hack is to allow kernel compiles to succeed on machines w/out srcdist .if exists($S/../include) INCLUDES+= -I$S/../include .else INCLUDES+= -I/usr/include .endif COPTS= ${INCLUDES} ${IDENT} -DKERNEL CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} LOAD_ADDRESS?= F0100000 NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $< NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $< # XXX LOCORE means "don't declare C stuff" not "for locore.s". NORMAL_S= ${CC} -c -x assembler-with-cpp -DLOCORE ${COPTS} $< DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $< DRIVER_S= ${CC} -c -x assembler-with-cpp -DLOCORE ${COPTS} $< PROFILE_C= ${CC} -c ${CFLAGS} ${PARAM} $< SYSTEM_CFILES= ioconf.c param.c vnode_if.c config.c SYSTEM_SFILES= ${I386}/i386/locore.s SYSTEM_OBJS= locore.o vnode_if.o ${OBJS} ioconf.o param.o config.o SYSTEM_DEP= Makefile symbols.exclude symbols.sort ${SYSTEM_OBJS} SYSTEM_LD_HEAD= @echo loading $@; rm -f $@ SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X ${SYSTEM_OBJS} vers.o .if ${CFLAGS:M-g} == "" SYMORDER_EXCLUDE=-x symbols.exclude .endif SYSTEM_LD_TAIL= @echo rearranging symbols; \ symorder -m ${SYMORDER_EXCLUDE} symbols.sort $@; \ size $@; chmod 755 $@ %BEFORE_DEPEND %OBJS %CFILES %SFILES %LOAD %CLEAN clean: rm -f *.o *.s eddep errs genassym kernel linterrs \ makelinks param.c symbols.exclude symbols.sort tags \ vers.c vnode_if.c vnode_if.h ${CLEAN} #lint: /tmp param.c # @lint -hbxn -DGENERIC -Dvolatile= ${COPTS} ${PARAM} \ # ${I386}/i386/Locore.c ${CFILES} ioconf.c param.c | \ # grep -v 'struct/union .* never defined' | \ # grep -v 'possible pointer alignment problem' symbols.exclude: Makefile echo "gcc2_compiled." >symbols.exclude echo "___gnu_compiled_c" >>symbols.exclude symbols.sort: ${I386}/i386/symbols.raw grep -v '^#' ${I386}/i386/symbols.raw \ | sed 's/^ //' | sort -u > symbols.sort locore.o: ${I386}/i386/locore.s assym.s ${NORMAL_S} # everything potentially depends on the Makefile since everything potentially # depends on the options. Some things are more dependent on the Makefile for # historical reasons. machdep.o: Makefile # the following is necessary because autoconf.o depends on #if GENERIC autoconf.o: Makefile # XXX - may no longer be needed locore.o: Makefile # depends on KDB (cons.o also depends on GENERIC) trap.o cons.o: Makefile # this rule stops ./assym.s in .depend from causing problems ./assym.s: assym.s assym.s: genassym ./genassym >assym.s # Some of the defines that genassym outputs may well depend on the # value of kernel options. genassym.o: ${I386}/i386/genassym.c Makefile ${CC} -c ${CFLAGS} ${PARAM} -UKERNEL ${I386}/i386/genassym.c genassym: genassym.o ${CC} -static ${CFLAGS} ${PARAM} genassym.o -o $@ # XXX this assumes that the options for NORMAL_C* and DRIVER_C* are identical. depend: assym.s param.c vnode_if.h ${BEFORE_DEPEND} rm -f .newdep mkdep -a -f .newdep ${COPTS} ${CFILES} ${SYSTEM_CFILES} mkdep -a -f .newdep ${COPTS} ${PARAM} -UKERNEL ${I386}/i386/genassym.c MKDEP_CPP="${CPP}" ; export MKDEP_CPP ; \ mkdep -a -f .newdep -DLOCORE ${COPTS} ${SFILES} ${SYSTEM_SFILES} rm -f .depend mv -f .newdep .depend links: egrep '#if' ${CFILES:Nswapkernel.c} | sed -f $S/conf/defines | \ sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink echo ${CFILES:Nswapkernel.c} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \ sort -u | comm -23 - dontlink | \ sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks sh makelinks && rm -f dontlink tags: @echo "see $S/kern/Makefile for tags" install: @if [ ! -f kernel ] ; then \ echo "You must first build your kernel before trying to install." ; \ exit 1 ; \ fi chflags noschg /kernel mv /kernel /kernel.old if [ `/usr/sbin/sysctl -n kern.bootfile` = /kernel ] ; then \ /usr/sbin/sysctl -w kern.bootfile=/kernel.old ; \ mv -f /var/db/kvm_kernel.db /var/db/kvm_kernel.old.db ; \ fi install -c -m 555 -o root -g wheel -fschg kernel / ioconf.o: ioconf.c $S/sys/param.h $S/sys/buf.h \ ${I386}/isa/isa_device.h ${I386}/isa/isa.h ${I386}/isa/icu.h ${CC} -c ${CFLAGS} ioconf.c param.c: $S/conf/param.c -rm -f param.c cp $S/conf/param.c . param.o: param.c Makefile ${CC} -c ${CFLAGS} ${PARAM} param.c vers.o: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP} sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT} ${CC} ${CFLAGS} -c vers.c vnode_if.c: $S/kern/vnode_if.sh $S/kern/vnode_if.src sh $S/kern/vnode_if.sh $S/kern/vnode_if.src vnode_if.h: $S/kern/vnode_if.sh $S/kern/vnode_if.src sh $S/kern/vnode_if.sh $S/kern/vnode_if.src %RULES # DO NOT DELETE THIS LINE -- make depend uses it Index: head/sys/conf/Makefile.powerpc =================================================================== --- head/sys/conf/Makefile.powerpc (revision 17970) +++ head/sys/conf/Makefile.powerpc (revision 17971) @@ -1,192 +1,192 @@ # Makefile.i386 -- with config changes. # Copyright 1990 W. Jolitz # from: @(#)Makefile.i386 7.1 5/10/91 -# $Id: Makefile.i386,v 1.85 1996/06/12 05:54:46 gpalmer Exp $ +# $Id: Makefile.i386,v 1.86 1996/08/21 16:31:34 ache Exp $ # # Makefile for FreeBSD # # This makefile is constructed from a machine description: # config machineid # Most changes should be made in the machine description # /sys/i386/conf/``machineid'' # after which you should do # config machineid # Generic makefile changes should be made in # /sys/i386/conf/Makefile.i386 # after which config should be rerun for all machines. # CC?= cc CPP?= cpp LD?= /usr/bin/ld .if exists(./@/.) S= ./@ .else S= ../.. .endif I386= ${S}/i386 CWARNFLAGS?= -W -Wreturn-type -Wcomment -Wredundant-decls -Wimplicit \ -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes \ - -Winline -Wunused + -Winline -Wunused -Wpointer-arith # # The following flags are next up for working on: # -Wall # # When working on removing warnings from code, the `-Werror' flag should be # of material assistance. # COPTFLAGS?=-O # Not ready for -I- yet. #include "foo.h" where foo.h is in the srcdir fails. INCLUDES= -nostdinc -I. -I$S -I$S/sys # This hack is to allow kernel compiles to succeed on machines w/out srcdist .if exists($S/../include) INCLUDES+= -I$S/../include .else INCLUDES+= -I/usr/include .endif COPTS= ${INCLUDES} ${IDENT} -DKERNEL CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} LOAD_ADDRESS?= F0100000 NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $< NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $< # XXX LOCORE means "don't declare C stuff" not "for locore.s". NORMAL_S= ${CC} -c -x assembler-with-cpp -DLOCORE ${COPTS} $< DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $< DRIVER_S= ${CC} -c -x assembler-with-cpp -DLOCORE ${COPTS} $< PROFILE_C= ${CC} -c ${CFLAGS} ${PARAM} $< SYSTEM_CFILES= ioconf.c param.c vnode_if.c config.c SYSTEM_SFILES= ${I386}/i386/locore.s SYSTEM_OBJS= locore.o vnode_if.o ${OBJS} ioconf.o param.o config.o SYSTEM_DEP= Makefile symbols.exclude symbols.sort ${SYSTEM_OBJS} SYSTEM_LD_HEAD= @echo loading $@; rm -f $@ SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X ${SYSTEM_OBJS} vers.o .if ${CFLAGS:M-g} == "" SYMORDER_EXCLUDE=-x symbols.exclude .endif SYSTEM_LD_TAIL= @echo rearranging symbols; \ symorder -m ${SYMORDER_EXCLUDE} symbols.sort $@; \ size $@; chmod 755 $@ %BEFORE_DEPEND %OBJS %CFILES %SFILES %LOAD %CLEAN clean: rm -f *.o *.s eddep errs genassym kernel linterrs \ makelinks param.c symbols.exclude symbols.sort tags \ vers.c vnode_if.c vnode_if.h ${CLEAN} #lint: /tmp param.c # @lint -hbxn -DGENERIC -Dvolatile= ${COPTS} ${PARAM} \ # ${I386}/i386/Locore.c ${CFILES} ioconf.c param.c | \ # grep -v 'struct/union .* never defined' | \ # grep -v 'possible pointer alignment problem' symbols.exclude: Makefile echo "gcc2_compiled." >symbols.exclude echo "___gnu_compiled_c" >>symbols.exclude symbols.sort: ${I386}/i386/symbols.raw grep -v '^#' ${I386}/i386/symbols.raw \ | sed 's/^ //' | sort -u > symbols.sort locore.o: ${I386}/i386/locore.s assym.s ${NORMAL_S} # everything potentially depends on the Makefile since everything potentially # depends on the options. Some things are more dependent on the Makefile for # historical reasons. machdep.o: Makefile # the following is necessary because autoconf.o depends on #if GENERIC autoconf.o: Makefile # XXX - may no longer be needed locore.o: Makefile # depends on KDB (cons.o also depends on GENERIC) trap.o cons.o: Makefile # this rule stops ./assym.s in .depend from causing problems ./assym.s: assym.s assym.s: genassym ./genassym >assym.s # Some of the defines that genassym outputs may well depend on the # value of kernel options. genassym.o: ${I386}/i386/genassym.c Makefile ${CC} -c ${CFLAGS} ${PARAM} -UKERNEL ${I386}/i386/genassym.c genassym: genassym.o ${CC} -static ${CFLAGS} ${PARAM} genassym.o -o $@ # XXX this assumes that the options for NORMAL_C* and DRIVER_C* are identical. depend: assym.s param.c vnode_if.h ${BEFORE_DEPEND} rm -f .newdep mkdep -a -f .newdep ${COPTS} ${CFILES} ${SYSTEM_CFILES} mkdep -a -f .newdep ${COPTS} ${PARAM} -UKERNEL ${I386}/i386/genassym.c MKDEP_CPP="${CPP}" ; export MKDEP_CPP ; \ mkdep -a -f .newdep -DLOCORE ${COPTS} ${SFILES} ${SYSTEM_SFILES} rm -f .depend mv -f .newdep .depend links: egrep '#if' ${CFILES:Nswapkernel.c} | sed -f $S/conf/defines | \ sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink echo ${CFILES:Nswapkernel.c} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \ sort -u | comm -23 - dontlink | \ sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks sh makelinks && rm -f dontlink tags: @echo "see $S/kern/Makefile for tags" install: @if [ ! -f kernel ] ; then \ echo "You must first build your kernel before trying to install." ; \ exit 1 ; \ fi chflags noschg /kernel mv /kernel /kernel.old if [ `/usr/sbin/sysctl -n kern.bootfile` = /kernel ] ; then \ /usr/sbin/sysctl -w kern.bootfile=/kernel.old ; \ mv -f /var/db/kvm_kernel.db /var/db/kvm_kernel.old.db ; \ fi install -c -m 555 -o root -g wheel -fschg kernel / ioconf.o: ioconf.c $S/sys/param.h $S/sys/buf.h \ ${I386}/isa/isa_device.h ${I386}/isa/isa.h ${I386}/isa/icu.h ${CC} -c ${CFLAGS} ioconf.c param.c: $S/conf/param.c -rm -f param.c cp $S/conf/param.c . param.o: param.c Makefile ${CC} -c ${CFLAGS} ${PARAM} param.c vers.o: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP} sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT} ${CC} ${CFLAGS} -c vers.c vnode_if.c: $S/kern/vnode_if.sh $S/kern/vnode_if.src sh $S/kern/vnode_if.sh $S/kern/vnode_if.src vnode_if.h: $S/kern/vnode_if.sh $S/kern/vnode_if.src sh $S/kern/vnode_if.sh $S/kern/vnode_if.src %RULES # DO NOT DELETE THIS LINE -- make depend uses it Index: head/sys/conf/kmod.mk =================================================================== --- head/sys/conf/kmod.mk (revision 17970) +++ head/sys/conf/kmod.mk (revision 17971) @@ -1,212 +1,212 @@ # From: @(#)bsd.prog.mk 5.26 (Berkeley) 6/25/91 -# $Id: bsd.kmod.mk,v 1.23 1996/06/24 04:24:00 jkh Exp $ +# $Id: bsd.kmod.mk,v 1.24 1996/06/30 22:16:19 jkh Exp $ # # The include file handles installing Loadable Kernel Modules. # includes the file named "../Makefile.inc" if it exists, # as well as the include file , , and # may be # # # +++ variables +++ # # CLEANFILES Additional files to remove for the clean and cleandir targets. # # DISTRIBUTION Name of distribution. [bin] # # EXPORT_SYMS ??? # # KERN Main Kernel source directory. [${.CURDIR}/../../sys/kern] # # KMOD The name of the loadable kernel module to build. # # KMODDIR Base path for loadable kernel modules # (see lkm(4)). [/lkm] # # KMODOWN LKM owner. [${BINOWN}] # # KMODGRP LKM group. [${BINGRP}] # # KMODMODE LKM mode. [${BINMODE}] # # LINKS The list of LKM links; should be full pathnames, the # linked-to file coming first, followed by the linked # file. The files are hard-linked. For example, to link # /lkm/master and /lkm/meister, use: # # LINKS= /lkm/master /lkm/meister # # LN_FLAGS Flags for ln(1) (see variable LINKS) # # NOMAN LKM does not have a manual page if set. # # PROG The name of the loadable kernel module to build. # If not supplied, ${KMOD} is used. # # PSEUDO_LKM ??? # # SRCS List of source files # # SUBDIR A list of subdirectories that should be built as well. # Each of the targets will execute the same target in the # subdirectories. # # DESTDIR, DISTDIR are set by other Makefiles (e.g. bsd.own.mk) # # # +++ targets +++ # # distribute: # This is a variant of install, which will # put the stuff into the right "distribution". # # install: # install the program and its manual pages; if the Makefile # does not itself define the target install, the targets # beforeinstall and afterinstall may also be used to cause # actions immediately before and after the install target # is executed. # # load: # Load LKM. # # tags: # Create a tags file for the source files. # # unload: # Unload LKM. # # bsd.obj.mk: clean, cleandir and obj # bsd.dep.mk: depend # bsd.man.mk: maninstall # .if exists(${.CURDIR}/../Makefile.inc) .include "${.CURDIR}/../Makefile.inc" .endif .SUFFIXES: .out .o .c .cc .cxx .C .y .l .s .S # # Assume that we are in /usr/src/foo/bar, so /sys is # ${.CURDIR}/../../sys. We don't bother adding a .PATH since nothing # actually lives in /sys directly. # CWARNFLAGS?= -W -Wreturn-type -Wcomment -Wredundant-decls -Wimplicit \ -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes \ - -Winline -Wunused + -Winline -Wunused -Wpointer-arith CFLAGS+=${COPTS} -DKERNEL -DACTUALLY_LKM_NOT_KERNEL -I${.CURDIR}/../../sys \ ${CWARNFLAGS} EXPORT_SYMS?= _${KMOD} .if defined(VFS_LKM) CFLAGS+= -DVFS_LKM -DMODVNOPS=${KMOD}vnops -I. SRCS+= vnode_if.h CLEANFILES+= vnode_if.h vnode_if.c .endif .if defined(PSEUDO_LKM) CFLAGS+= -DPSEUDO_LKM .endif DPSRCS+= ${SRCS:M*.h} OBJS+= ${SRCS:N*.h:R:S/$/.o/g} .if !defined(PROG) PROG= ${KMOD}.o .endif ${PROG}: ${DPSRCS} ${OBJS} ${DPADD} ${LD} -r ${LDFLAGS} -o tmp.o ${OBJS} .if defined(EXPORT_SYMS) @rm -f symb.tmp @for i in ${EXPORT_SYMS} ; do echo $$i >> symb.tmp ; done symorder -c symb.tmp tmp.o @rm -f symb.tmp .endif mv tmp.o ${.TARGET} .if !defined(NOMAN) .include .if !defined(_MANPAGES) || empty(_MANPAGES) MAN1= ${KMOD}.4 .endif .elif !target(maninstall) maninstall: _SUBDIR all-man: .endif .MAIN: all all: ${PROG} all-man _SUBDIR CLEANFILES+=${PROG} ${OBJS} .if !target(install) .if !target(beforeinstall) beforeinstall: .endif .if !target(afterinstall) afterinstall: .endif realinstall: _SUBDIR ${INSTALL} ${COPY} -o ${KMODOWN} -g ${KMODGRP} -m ${KMODMODE} \ ${INSTALLFLAGS} ${PROG} ${DESTDIR}${KMODDIR} .if defined(LINKS) && !empty(LINKS) @set ${LINKS}; \ while test $$# -ge 2; do \ l=${DESTDIR}$$1; \ shift; \ t=${DESTDIR}$$1; \ shift; \ ${ECHO} $$t -\> $$l; \ rm -f $$t; \ ln ${LN_FLAGS} $$l $$t; \ done; true .endif install: afterinstall _SUBDIR .if !defined(NOMAN) afterinstall: realinstall maninstall .else afterinstall: realinstall .endif realinstall: beforeinstall .endif DISTRIBUTION?= bin .if !target(distribute) distribute: _SUBDIR cd ${.CURDIR} ; $(MAKE) install DESTDIR=${DISTDIR}/${DISTRIBUTION} SHARED=copies .endif .if !target(tags) tags: ${SRCS} _SUBDIR .if defined(PROG) -cd ${.CURDIR}; ctags -f /dev/stdout ${.ALLSRC} | \ sed "s;\${.CURDIR}/;;" > tags .endif .endif .if !target(load) load: ${PROG} /sbin/modload -o ${KMOD} -e${KMOD} ${PROG} .endif .if !target(unload) unload: ${PROG} /sbin/modunload -n ${KMOD} .endif KERN= ${.CURDIR}/../../sys/kern vnode_if.h: ${KERN}/vnode_if.sh ${KERN}/vnode_if.src sh ${KERN}/vnode_if.sh ${KERN}/vnode_if.src ./vnode_if.h: vnode_if.h .include .include Index: head/sys/dev/eisa/eisaconf.c =================================================================== --- head/sys/dev/eisa/eisaconf.c (revision 17970) +++ head/sys/dev/eisa/eisaconf.c (revision 17971) @@ -1,761 +1,761 @@ /* * EISA bus probe and attach routines * * Copyright (c) 1995, 1996 Justin T. Gibbs. * 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 immediately at the beginning of the file, without modification, * 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * - * $Id: eisaconf.c,v 1.19 1996/04/20 21:21:49 gibbs Exp $ + * $Id: eisaconf.c,v 1.20 1996/06/12 05:02:41 gpalmer Exp $ */ #include #include #include #include #include #include /* For kdc_isa */ #include #include #include /* Hmmm. Interrupt stuff? */ struct eisa_device_node{ struct eisa_device dev; struct eisa_device_node *next; }; extern struct kern_devconf kdc_cpu0; struct kern_devconf kdc_eisa0 = { 0, 0, 0, /* filled in by dev_attach */ "eisa", 0, { MDDT_BUS, 0 }, 0, 0, 0, BUS_EXTERNALLEN, &kdc_cpu0, /* parent is the CPU */ 0, /* no parentdata */ DC_BUSY, /* busses are always busy */ NULL, DC_CLS_BUS /* class */ }; /* * This should probably be a list of "struct device" once it exists. * A struct device will incorperate ioconf and driver entry point data * regardless of how its attached to the system (via unions) as well * as more generic information that all device types should support (unit * number, if its installed, etc). */ static struct eisa_device_node *eisa_dev_list; static struct eisa_device_node **eisa_dev_list_tail = &eisa_dev_list; static u_long eisa_unit; static struct eisa_driver mainboard_drv = { "eisa", NULL, NULL, NULL, &eisa_unit }; /* * Add the mainboard_drv to the eisa driver linkerset so that it is * defined even if no EISA drivers are linked into the kernel. */ DATA_SET (eisadriver_set, mainboard_drv); /* * Local function declarations and static variables */ void eisa_reg_print __P((struct eisa_device *e_dev, char *string, char *separator)); static int eisa_add_resvaddr __P((struct eisa_device *e_dev, struct resvlist *head, u_long base, u_long size, int flags)); static int eisa_reg_resvaddr __P((struct eisa_device *e_dev, struct resvlist *head, resvaddr_t *resvaddr, int *reg_count)); /* * Keep some state about what we've printed so far * to make probe output pretty. */ static struct { int in_registration;/* reg_start has been called */ int num_interrupts; int num_ioaddrs; int num_maddrs; int column; /* How much we have output so far. */ #define MAX_COL 80 } reg_state; /* ** probe for EISA devices */ void eisa_configure() { int i,slot; char *id_string; struct eisa_device_node *dev_node; struct eisa_driver **e_drvp; struct eisa_driver *e_drv; struct eisa_device *e_dev; int eisaBase = 0xc80; eisa_id_t eisa_id; e_drvp = (struct eisa_driver**)eisadriver_set.ls_items; for (slot = 0; slot < EISA_SLOTS; eisaBase+=0x1000, slot++) { int id_size = sizeof(eisa_id); eisa_id = 0; for( i = 0; i < id_size; i++ ) { outb(eisaBase,0x80 + i); /*Some cards require priming*/ eisa_id |= inb(eisaBase+i) << ((id_size-i-1)*CHAR_BIT); } if (eisa_id & 0x80000000) continue; /* no EISA card in slot */ /* Prepare an eisa_device_node for this slot */ dev_node = (struct eisa_device_node *)malloc(sizeof(*dev_node), M_DEVBUF, M_NOWAIT); if (!dev_node) { printf("eisa0: cannot malloc eisa_device_node"); break; /* Try to attach what we have already */ } bzero(dev_node, sizeof(*dev_node)); e_dev = &(dev_node->dev); e_dev->id = eisa_id; /* * Add an EISA ID based descriptive name incase we don't * have a driver for it. We do this now instead of after all * probes because in the future, the eisa module will only * be responsible for creating the list of devices in the system * for the configuration manager to use. */ e_dev->full_name = (char *)malloc(8*sizeof(char), M_DEVBUF, M_NOWAIT); if (!e_dev->full_name) { panic("Eisa probe unable to malloc"); } sprintf(e_dev->full_name, "%c%c%c%x%x", EISA_MFCTR_CHAR0(e_dev->id), EISA_MFCTR_CHAR1(e_dev->id), EISA_MFCTR_CHAR2(e_dev->id), EISA_PRODUCT_ID(e_dev->id), EISA_REVISION_ID(e_dev->id)); e_dev->ioconf.slot = slot; /* Initialize our lists of reserved addresses */ LIST_INIT(&(e_dev->ioconf.ioaddrs)); LIST_INIT(&(e_dev->ioconf.maddrs)); *eisa_dev_list_tail = dev_node; eisa_dev_list_tail = &dev_node->next; } dev_node = eisa_dev_list; /* * "Attach" the system board */ /* The first will be the motherboard in a true EISA system */ if (dev_node && (dev_node->dev.ioconf.slot == 0)) { e_dev = &dev_node->dev; e_dev->driver = &mainboard_drv; e_dev->unit = (*e_dev->driver->unit)++; id_string = e_dev->full_name; e_dev->full_name = (char *)malloc(strlen(e_dev->full_name) + sizeof(" (System Board)") + 1, M_DEVBUF, M_NOWAIT); if (!e_dev->full_name) { panic("Eisa probe unable to malloc"); } sprintf(e_dev->full_name, "%s (System Board)", id_string); free(id_string, M_DEVBUF); printf("%s%ld: <%s>\n", e_dev->driver->name, e_dev->unit, e_dev->full_name); /* Should set the iosize, but I don't have a spec handy */ kdc_eisa0.kdc_description = (char *)malloc(strlen(e_dev->full_name) + sizeof("EISA bus <>") + 1, M_DEVBUF, M_NOWAIT); if (!kdc_eisa0.kdc_description) { panic("Eisa probe unable to malloc"); } sprintf((char *)kdc_eisa0.kdc_description, "EISA bus <%s>", e_dev->full_name); dev_attach(&kdc_eisa0); printf("Probing for devices on the EISA bus\n"); dev_node = dev_node->next; } if (!eisa_dev_list) { /* * No devices. */ return; } /* * See what devices we recognize. */ while((e_drv = *e_drvp++)) { if (e_drv->probe) (*e_drv->probe)(); } /* * Attach the devices we found in slot order */ for (; dev_node; dev_node=dev_node->next) { e_dev = &dev_node->dev; e_drv = e_dev->driver; if (e_drv) { /* * Determine the proper unit number for this device. * Here we should look in the device table generated * by config to see if this type of device is enabled * either generically or for this particular address * as well as determine if a reserved unit number * should be used. We should also ensure that the * "next availible unit number" skips over "wired" unit * numbers. This will be done after config is fixed or * some other configuration method is chosen. */ e_dev->unit = (*e_drv->unit)++; if ((*e_drv->attach)(e_dev) < 0) { /* Ensure registration has ended */ reg_state.in_registration = 0; printf("\n%s0:%d <%s> attach failed\n", mainboard_drv.name, e_dev->ioconf.slot, e_dev->full_name); continue; } /* Ensure registration has ended */ reg_state.in_registration = 0; e_dev->kdc->kdc_unit = e_dev->unit; } else { /* Announce unattached device */ printf("%s0:%d <%s=0x%x> unknown device\n", mainboard_drv.name, e_dev->ioconf.slot, e_dev->full_name, e_dev->id); } } } struct eisa_device * eisa_match_dev(e_dev, match_func) struct eisa_device *e_dev; char* (*match_func)(eisa_id_t); { struct eisa_device_node *e_node = eisa_dev_list; if (e_dev) { /* Start our search from the last successful match */ e_node = ((struct eisa_device_node *)e_dev)->next; } for(; e_node; e_node = e_node->next) { char *result; if (e_node->dev.driver) { /* Already claimed */ continue; } result = (*match_func)(e_node->dev.id); if (result) { free(e_node->dev.full_name, M_DEVBUF); e_node->dev.full_name = result; return (&(e_node->dev)); } } return NULL; } /* Interrupt and I/O space registration facitlities */ void eisa_reg_start(e_dev) struct eisa_device *e_dev; { /* * Announce the device. */ char *string; reg_state.in_registration = 1; reg_state.num_interrupts = 0; reg_state.num_ioaddrs = 0; reg_state.num_maddrs = 0; reg_state.column = 0; string = malloc(strlen(e_dev->full_name) + sizeof(" <>") + /*NULL*/1, M_TEMP, M_NOWAIT); if(!string) { printf("eisa0: cannot malloc device description string\n"); return; } sprintf(string, " <%s>", e_dev->full_name); eisa_reg_print(e_dev, string, /*separator=*/NULL); free(string, M_TEMP); } /* * Output registration information mindfull of screen wrap. * Output an optional character separator before the string * if the line does not wrap. */ void eisa_reg_print(e_dev, string, separator) struct eisa_device *e_dev; char *string; char *separator; { int len = strlen(string); if(separator) len++; if(reg_state.column + len > MAX_COL) { printf("\n"); reg_state.column = 0; } else if(separator) { printf("%c", *separator); reg_state.column++; } if(reg_state.column == 0) reg_state.column += printf("%s%ld:%s", e_dev->driver->name, e_dev->unit, string); else reg_state.column += printf("%s", string); } /* Interrupt and I/O space registration facitlities */ void eisa_reg_end(e_dev) struct eisa_device *e_dev; { if( reg_state.in_registration ) { /* * The device should have called eisa_registerdev() * during its probe. So hopefully we can use the kdc * to weed out ISA/VL devices that use EISA id registers. */ char string[25]; if (e_dev->kdc && (e_dev->kdc->kdc_parent == &kdc_isa0)) { sprintf(string, " on isa"); } else { sprintf(string, " on %s0 slot %d", mainboard_drv.name, e_dev->ioconf.slot); } eisa_reg_print(e_dev, string, NULL); printf("\n"); reg_state.in_registration = 0; } else printf("eisa_reg_end called outside of a " "registration session\n"); } int eisa_add_intr(e_dev, irq) struct eisa_device *e_dev; int irq; { e_dev->ioconf.irq |= 1ul << irq; return 0; } int eisa_reg_intr(e_dev, irq, func, arg, maskptr, shared) struct eisa_device *e_dev; int irq; void (*func)(void *); void *arg; u_int *maskptr; int shared; { int result; int s; char string[25]; char separator = ','; #if NOT_YET /* * Punt on conflict detection for the moment. * I want to develop a generic routine to do * this for all device types. */ int checkthese = CC_IRQ; if (haveseen_dev(dev, checkthese)) return 1; #endif if (reg_state.in_registration) { s = splhigh(); /* * This should really go to a routine that can optionally * handle shared interrupts. */ result = register_intr(irq, /* isa irq */ 0, /* deviced?? */ 0, /* flags? */ (inthand2_t*) func, /* handler */ maskptr, /* mask pointer */ (int)arg); /* handler arg */ if (result) { printf ("\neisa_reg_int: result=%d\n", result); splx(s); return (result); }; update_intr_masks(); splx(s); } else return EPERM; e_dev->ioconf.irq |= 1ul << irq; sprintf(string, " irq %d", irq); eisa_reg_print(e_dev, string, reg_state.num_interrupts ? &separator : NULL); reg_state.num_interrupts++; return (0); } int eisa_release_intr(e_dev, irq, func) struct eisa_device *e_dev; int irq; void (*func)(void *); { int result; int s; if (!(e_dev->ioconf.irq & (1ul << irq))) { printf("%s%ld: Attempted to release an interrupt (%d) " "it doesn't own\n", e_dev->driver->name, e_dev->unit, irq); return (-1); } s = splhigh(); INTRDIS ((1ul<ioconf.irq & (1ul << irq))) { printf("%s%ld: Attempted to enable an interrupt (%d) " "it doesn't own\n", e_dev->driver->name, e_dev->unit, irq); return (-1); } s = splhigh(); INTREN((1ul << irq)); splx(s); return 0; } static int eisa_add_resvaddr(e_dev, head, base, size, flags) struct eisa_device *e_dev; struct resvlist *head; u_long base; u_long size; int flags; { resvaddr_t *reservation; reservation = (resvaddr_t *)malloc(sizeof(resvaddr_t), M_DEVBUF, M_NOWAIT); if(!reservation) return (ENOMEM); reservation->addr = base; reservation->size = size; reservation->flags = flags; if (!head->lh_first) { LIST_INSERT_HEAD(head, reservation, links); } else { resvaddr_t *node; for(node = head->lh_first; node; node = node->links.le_next) { if (node->addr > reservation->addr) { /* * List is sorted in increasing * address order. */ LIST_INSERT_BEFORE(node, reservation, links); break; } if (node->addr == reservation->addr) { /* * If the entry we want to add * matches any already in here, * fail. */ free(reservation, M_DEVBUF); return (EEXIST); } if (!node->links.le_next) { LIST_INSERT_AFTER(node, reservation, links); break; } } } return (0); } int eisa_add_mspace(e_dev, mbase, msize, flags) struct eisa_device *e_dev; u_long mbase; u_long msize; int flags; { return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.maddrs), mbase, msize, flags); } int eisa_add_iospace(e_dev, iobase, iosize, flags) struct eisa_device *e_dev; u_long iobase; u_long iosize; int flags; { return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), iobase, iosize, flags); } static int eisa_reg_resvaddr(e_dev, head, resvaddr, reg_count) struct eisa_device *e_dev; struct resvlist *head; resvaddr_t *resvaddr; int *reg_count; { if (reg_state.in_registration) { resvaddr_t *node; /* * Ensure that this resvaddr is actually in the devices' * reservation list. */ for(node = head->lh_first; node; node = node->links.le_next) { if (node == resvaddr) { char buf[35]; char separator = ','; char *string = buf; if (*reg_count == 0) { /* First time */ string += sprintf(string, " at"); } if (node->size == 1 || (node->flags & RESVADDR_BITMASK)) sprintf(string, " 0x%lx", node->addr); else sprintf(string, " 0x%lx-0x%lx", node->addr, node->addr + node->size - 1); eisa_reg_print(e_dev, buf, *reg_count ? &separator : NULL); (*reg_count)++; e_dev->kdc->kdc_datalen += sizeof(resvaddr_t); return (0); } } return (ENOENT); } return EPERM; } int eisa_reg_mspace(e_dev, resvaddr) struct eisa_device *e_dev; resvaddr_t *resvaddr; { #ifdef NOT_YET /* * Punt on conflict detection for the moment. * I want to develop a generic routine to do * this for all device types. */ int checkthese = CC_MADDR; if (haveseen_dev(dev, checkthese)) return -1; #endif return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.maddrs), resvaddr, &(reg_state.num_maddrs))); } int eisa_reg_iospace(e_dev, resvaddr) struct eisa_device *e_dev; resvaddr_t *resvaddr; { #ifdef NOT_YET /* * Punt on conflict detection for the moment. * I want to develop a generic routine to do * this for all device types. */ int checkthese = CC_IOADDR; if (haveseen_dev(dev, checkthese)) return -1; #endif return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), resvaddr, &(reg_state.num_ioaddrs))); } int eisa_registerdev(e_dev, driver, kdc_template) struct eisa_device *e_dev; struct eisa_driver *driver; struct kern_devconf *kdc_template; { e_dev->driver = driver; /* Driver now owns this device */ e_dev->kdc = (struct kern_devconf *)malloc(sizeof(struct kern_devconf), M_DEVBUF, M_NOWAIT); if (!e_dev->kdc) { printf("WARNING: eisa_registerdev unable to malloc! " "Device kdc will not be registerd\n"); return 1; } bcopy(kdc_template, e_dev->kdc, sizeof(*kdc_template)); e_dev->kdc->kdc_description = e_dev->full_name; e_dev->kdc->kdc_parentdata = e_dev; dev_attach(e_dev->kdc); return (0); } int eisa_generic_externalize(struct kern_devconf *kdc, struct sysctl_req *req) { struct eisa_device *e_dev; resvaddr_t *node; void *buf; /* Temporary externalizing buffer */ void *bufp; /* Current offset in the buffer */ void *offset; /* Offset relative to target address space */ void *ioa_prev; /* Prev Node entries relative to target address space */ void *ma_prev; /* Prev Node entries relative to target address space */ int retval; - offset = req->oldptr + req->oldidx; + offset = (char *)req->oldptr + req->oldidx; buf = malloc(kdc->kdc_datalen, M_TEMP, M_NOWAIT); if (!buf) return 0; bufp = buf; bcopy(kdc->kdc_eisa, bufp, sizeof(struct eisa_device)); e_dev = bufp; /* Calculate initial prev nodes */ - ioa_prev = offset + ((void *)&(e_dev->ioconf.ioaddrs.lh_first) - - (void *)e_dev); - ma_prev = offset + ((void *)&(e_dev->ioconf.maddrs.lh_first) - - (void *)e_dev); + ioa_prev = (char *)offset + ((char *)&(e_dev->ioconf.ioaddrs.lh_first) + - (char *)e_dev); + ma_prev = (char *)offset + ((char *)&(e_dev->ioconf.maddrs.lh_first) + - (char *)e_dev); - offset += sizeof(*e_dev); - bufp += sizeof(*e_dev); + offset = (char *)offset + sizeof(*e_dev); + bufp = (char *)bufp + sizeof(*e_dev); if (e_dev->ioconf.ioaddrs.lh_first) { node = e_dev->ioconf.ioaddrs.lh_first; e_dev->ioconf.ioaddrs.lh_first = offset; for(;node;node = node->links.le_next) { resvaddr_t *out_node; bcopy(node, bufp, sizeof(resvaddr_t)); out_node = (resvaddr_t *)bufp; - bufp += sizeof(resvaddr_t); - offset += sizeof(resvaddr_t); + bufp = (char *)bufp + sizeof(resvaddr_t); + offset = (char *)offset + sizeof(resvaddr_t); out_node->links.le_prev = ioa_prev; - ioa_prev += sizeof(resvaddr_t); + ioa_prev = (char *)ioa_prev + sizeof(resvaddr_t); if (out_node->links.le_next) out_node->links.le_next = offset; } } if (e_dev->ioconf.maddrs.lh_first) { node = e_dev->ioconf.maddrs.lh_first; e_dev->ioconf.maddrs.lh_first = offset; for(;node;node = node->links.le_next) { resvaddr_t *out_node; bcopy(node, bufp, sizeof(resvaddr_t)); out_node = (resvaddr_t *)bufp; - bufp += sizeof(resvaddr_t); - offset += sizeof(resvaddr_t); + bufp = (char *)bufp + sizeof(resvaddr_t); + offset = (char *)offset + sizeof(resvaddr_t); out_node->links.le_prev = ma_prev; - ma_prev += sizeof(resvaddr_t); + ma_prev = (char *)ma_prev + sizeof(resvaddr_t); if (out_node->links.le_next) out_node->links.le_next = offset; } } retval = SYSCTL_OUT(req, buf, kdc->kdc_datalen); free(buf, M_TEMP); return retval; } Index: head/sys/i386/conf/Makefile.i386 =================================================================== --- head/sys/i386/conf/Makefile.i386 (revision 17970) +++ head/sys/i386/conf/Makefile.i386 (revision 17971) @@ -1,192 +1,192 @@ # Makefile.i386 -- with config changes. # Copyright 1990 W. Jolitz # from: @(#)Makefile.i386 7.1 5/10/91 -# $Id: Makefile.i386,v 1.85 1996/06/12 05:54:46 gpalmer Exp $ +# $Id: Makefile.i386,v 1.86 1996/08/21 16:31:34 ache Exp $ # # Makefile for FreeBSD # # This makefile is constructed from a machine description: # config machineid # Most changes should be made in the machine description # /sys/i386/conf/``machineid'' # after which you should do # config machineid # Generic makefile changes should be made in # /sys/i386/conf/Makefile.i386 # after which config should be rerun for all machines. # CC?= cc CPP?= cpp LD?= /usr/bin/ld .if exists(./@/.) S= ./@ .else S= ../.. .endif I386= ${S}/i386 CWARNFLAGS?= -W -Wreturn-type -Wcomment -Wredundant-decls -Wimplicit \ -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes \ - -Winline -Wunused + -Winline -Wunused -Wpointer-arith # # The following flags are next up for working on: # -Wall # # When working on removing warnings from code, the `-Werror' flag should be # of material assistance. # COPTFLAGS?=-O # Not ready for -I- yet. #include "foo.h" where foo.h is in the srcdir fails. INCLUDES= -nostdinc -I. -I$S -I$S/sys # This hack is to allow kernel compiles to succeed on machines w/out srcdist .if exists($S/../include) INCLUDES+= -I$S/../include .else INCLUDES+= -I/usr/include .endif COPTS= ${INCLUDES} ${IDENT} -DKERNEL CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS} LOAD_ADDRESS?= F0100000 NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $< NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $< # XXX LOCORE means "don't declare C stuff" not "for locore.s". NORMAL_S= ${CC} -c -x assembler-with-cpp -DLOCORE ${COPTS} $< DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $< DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $< DRIVER_S= ${CC} -c -x assembler-with-cpp -DLOCORE ${COPTS} $< PROFILE_C= ${CC} -c ${CFLAGS} ${PARAM} $< SYSTEM_CFILES= ioconf.c param.c vnode_if.c config.c SYSTEM_SFILES= ${I386}/i386/locore.s SYSTEM_OBJS= locore.o vnode_if.o ${OBJS} ioconf.o param.o config.o SYSTEM_DEP= Makefile symbols.exclude symbols.sort ${SYSTEM_OBJS} SYSTEM_LD_HEAD= @echo loading $@; rm -f $@ SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X ${SYSTEM_OBJS} vers.o .if ${CFLAGS:M-g} == "" SYMORDER_EXCLUDE=-x symbols.exclude .endif SYSTEM_LD_TAIL= @echo rearranging symbols; \ symorder -m ${SYMORDER_EXCLUDE} symbols.sort $@; \ size $@; chmod 755 $@ %BEFORE_DEPEND %OBJS %CFILES %SFILES %LOAD %CLEAN clean: rm -f *.o *.s eddep errs genassym kernel linterrs \ makelinks param.c symbols.exclude symbols.sort tags \ vers.c vnode_if.c vnode_if.h ${CLEAN} #lint: /tmp param.c # @lint -hbxn -DGENERIC -Dvolatile= ${COPTS} ${PARAM} \ # ${I386}/i386/Locore.c ${CFILES} ioconf.c param.c | \ # grep -v 'struct/union .* never defined' | \ # grep -v 'possible pointer alignment problem' symbols.exclude: Makefile echo "gcc2_compiled." >symbols.exclude echo "___gnu_compiled_c" >>symbols.exclude symbols.sort: ${I386}/i386/symbols.raw grep -v '^#' ${I386}/i386/symbols.raw \ | sed 's/^ //' | sort -u > symbols.sort locore.o: ${I386}/i386/locore.s assym.s ${NORMAL_S} # everything potentially depends on the Makefile since everything potentially # depends on the options. Some things are more dependent on the Makefile for # historical reasons. machdep.o: Makefile # the following is necessary because autoconf.o depends on #if GENERIC autoconf.o: Makefile # XXX - may no longer be needed locore.o: Makefile # depends on KDB (cons.o also depends on GENERIC) trap.o cons.o: Makefile # this rule stops ./assym.s in .depend from causing problems ./assym.s: assym.s assym.s: genassym ./genassym >assym.s # Some of the defines that genassym outputs may well depend on the # value of kernel options. genassym.o: ${I386}/i386/genassym.c Makefile ${CC} -c ${CFLAGS} ${PARAM} -UKERNEL ${I386}/i386/genassym.c genassym: genassym.o ${CC} -static ${CFLAGS} ${PARAM} genassym.o -o $@ # XXX this assumes that the options for NORMAL_C* and DRIVER_C* are identical. depend: assym.s param.c vnode_if.h ${BEFORE_DEPEND} rm -f .newdep mkdep -a -f .newdep ${COPTS} ${CFILES} ${SYSTEM_CFILES} mkdep -a -f .newdep ${COPTS} ${PARAM} -UKERNEL ${I386}/i386/genassym.c MKDEP_CPP="${CPP}" ; export MKDEP_CPP ; \ mkdep -a -f .newdep -DLOCORE ${COPTS} ${SFILES} ${SYSTEM_SFILES} rm -f .depend mv -f .newdep .depend links: egrep '#if' ${CFILES:Nswapkernel.c} | sed -f $S/conf/defines | \ sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink echo ${CFILES:Nswapkernel.c} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \ sort -u | comm -23 - dontlink | \ sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks sh makelinks && rm -f dontlink tags: @echo "see $S/kern/Makefile for tags" install: @if [ ! -f kernel ] ; then \ echo "You must first build your kernel before trying to install." ; \ exit 1 ; \ fi chflags noschg /kernel mv /kernel /kernel.old if [ `/usr/sbin/sysctl -n kern.bootfile` = /kernel ] ; then \ /usr/sbin/sysctl -w kern.bootfile=/kernel.old ; \ mv -f /var/db/kvm_kernel.db /var/db/kvm_kernel.old.db ; \ fi install -c -m 555 -o root -g wheel -fschg kernel / ioconf.o: ioconf.c $S/sys/param.h $S/sys/buf.h \ ${I386}/isa/isa_device.h ${I386}/isa/isa.h ${I386}/isa/icu.h ${CC} -c ${CFLAGS} ioconf.c param.c: $S/conf/param.c -rm -f param.c cp $S/conf/param.c . param.o: param.c Makefile ${CC} -c ${CFLAGS} ${PARAM} param.c vers.o: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP} sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT} ${CC} ${CFLAGS} -c vers.c vnode_if.c: $S/kern/vnode_if.sh $S/kern/vnode_if.src sh $S/kern/vnode_if.sh $S/kern/vnode_if.src vnode_if.h: $S/kern/vnode_if.sh $S/kern/vnode_if.src sh $S/kern/vnode_if.sh $S/kern/vnode_if.src %RULES # DO NOT DELETE THIS LINE -- make depend uses it Index: head/sys/i386/eisa/eisaconf.c =================================================================== --- head/sys/i386/eisa/eisaconf.c (revision 17970) +++ head/sys/i386/eisa/eisaconf.c (revision 17971) @@ -1,761 +1,761 @@ /* * EISA bus probe and attach routines * * Copyright (c) 1995, 1996 Justin T. Gibbs. * 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 immediately at the beginning of the file, without modification, * 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * - * $Id: eisaconf.c,v 1.19 1996/04/20 21:21:49 gibbs Exp $ + * $Id: eisaconf.c,v 1.20 1996/06/12 05:02:41 gpalmer Exp $ */ #include #include #include #include #include #include /* For kdc_isa */ #include #include #include /* Hmmm. Interrupt stuff? */ struct eisa_device_node{ struct eisa_device dev; struct eisa_device_node *next; }; extern struct kern_devconf kdc_cpu0; struct kern_devconf kdc_eisa0 = { 0, 0, 0, /* filled in by dev_attach */ "eisa", 0, { MDDT_BUS, 0 }, 0, 0, 0, BUS_EXTERNALLEN, &kdc_cpu0, /* parent is the CPU */ 0, /* no parentdata */ DC_BUSY, /* busses are always busy */ NULL, DC_CLS_BUS /* class */ }; /* * This should probably be a list of "struct device" once it exists. * A struct device will incorperate ioconf and driver entry point data * regardless of how its attached to the system (via unions) as well * as more generic information that all device types should support (unit * number, if its installed, etc). */ static struct eisa_device_node *eisa_dev_list; static struct eisa_device_node **eisa_dev_list_tail = &eisa_dev_list; static u_long eisa_unit; static struct eisa_driver mainboard_drv = { "eisa", NULL, NULL, NULL, &eisa_unit }; /* * Add the mainboard_drv to the eisa driver linkerset so that it is * defined even if no EISA drivers are linked into the kernel. */ DATA_SET (eisadriver_set, mainboard_drv); /* * Local function declarations and static variables */ void eisa_reg_print __P((struct eisa_device *e_dev, char *string, char *separator)); static int eisa_add_resvaddr __P((struct eisa_device *e_dev, struct resvlist *head, u_long base, u_long size, int flags)); static int eisa_reg_resvaddr __P((struct eisa_device *e_dev, struct resvlist *head, resvaddr_t *resvaddr, int *reg_count)); /* * Keep some state about what we've printed so far * to make probe output pretty. */ static struct { int in_registration;/* reg_start has been called */ int num_interrupts; int num_ioaddrs; int num_maddrs; int column; /* How much we have output so far. */ #define MAX_COL 80 } reg_state; /* ** probe for EISA devices */ void eisa_configure() { int i,slot; char *id_string; struct eisa_device_node *dev_node; struct eisa_driver **e_drvp; struct eisa_driver *e_drv; struct eisa_device *e_dev; int eisaBase = 0xc80; eisa_id_t eisa_id; e_drvp = (struct eisa_driver**)eisadriver_set.ls_items; for (slot = 0; slot < EISA_SLOTS; eisaBase+=0x1000, slot++) { int id_size = sizeof(eisa_id); eisa_id = 0; for( i = 0; i < id_size; i++ ) { outb(eisaBase,0x80 + i); /*Some cards require priming*/ eisa_id |= inb(eisaBase+i) << ((id_size-i-1)*CHAR_BIT); } if (eisa_id & 0x80000000) continue; /* no EISA card in slot */ /* Prepare an eisa_device_node for this slot */ dev_node = (struct eisa_device_node *)malloc(sizeof(*dev_node), M_DEVBUF, M_NOWAIT); if (!dev_node) { printf("eisa0: cannot malloc eisa_device_node"); break; /* Try to attach what we have already */ } bzero(dev_node, sizeof(*dev_node)); e_dev = &(dev_node->dev); e_dev->id = eisa_id; /* * Add an EISA ID based descriptive name incase we don't * have a driver for it. We do this now instead of after all * probes because in the future, the eisa module will only * be responsible for creating the list of devices in the system * for the configuration manager to use. */ e_dev->full_name = (char *)malloc(8*sizeof(char), M_DEVBUF, M_NOWAIT); if (!e_dev->full_name) { panic("Eisa probe unable to malloc"); } sprintf(e_dev->full_name, "%c%c%c%x%x", EISA_MFCTR_CHAR0(e_dev->id), EISA_MFCTR_CHAR1(e_dev->id), EISA_MFCTR_CHAR2(e_dev->id), EISA_PRODUCT_ID(e_dev->id), EISA_REVISION_ID(e_dev->id)); e_dev->ioconf.slot = slot; /* Initialize our lists of reserved addresses */ LIST_INIT(&(e_dev->ioconf.ioaddrs)); LIST_INIT(&(e_dev->ioconf.maddrs)); *eisa_dev_list_tail = dev_node; eisa_dev_list_tail = &dev_node->next; } dev_node = eisa_dev_list; /* * "Attach" the system board */ /* The first will be the motherboard in a true EISA system */ if (dev_node && (dev_node->dev.ioconf.slot == 0)) { e_dev = &dev_node->dev; e_dev->driver = &mainboard_drv; e_dev->unit = (*e_dev->driver->unit)++; id_string = e_dev->full_name; e_dev->full_name = (char *)malloc(strlen(e_dev->full_name) + sizeof(" (System Board)") + 1, M_DEVBUF, M_NOWAIT); if (!e_dev->full_name) { panic("Eisa probe unable to malloc"); } sprintf(e_dev->full_name, "%s (System Board)", id_string); free(id_string, M_DEVBUF); printf("%s%ld: <%s>\n", e_dev->driver->name, e_dev->unit, e_dev->full_name); /* Should set the iosize, but I don't have a spec handy */ kdc_eisa0.kdc_description = (char *)malloc(strlen(e_dev->full_name) + sizeof("EISA bus <>") + 1, M_DEVBUF, M_NOWAIT); if (!kdc_eisa0.kdc_description) { panic("Eisa probe unable to malloc"); } sprintf((char *)kdc_eisa0.kdc_description, "EISA bus <%s>", e_dev->full_name); dev_attach(&kdc_eisa0); printf("Probing for devices on the EISA bus\n"); dev_node = dev_node->next; } if (!eisa_dev_list) { /* * No devices. */ return; } /* * See what devices we recognize. */ while((e_drv = *e_drvp++)) { if (e_drv->probe) (*e_drv->probe)(); } /* * Attach the devices we found in slot order */ for (; dev_node; dev_node=dev_node->next) { e_dev = &dev_node->dev; e_drv = e_dev->driver; if (e_drv) { /* * Determine the proper unit number for this device. * Here we should look in the device table generated * by config to see if this type of device is enabled * either generically or for this particular address * as well as determine if a reserved unit number * should be used. We should also ensure that the * "next availible unit number" skips over "wired" unit * numbers. This will be done after config is fixed or * some other configuration method is chosen. */ e_dev->unit = (*e_drv->unit)++; if ((*e_drv->attach)(e_dev) < 0) { /* Ensure registration has ended */ reg_state.in_registration = 0; printf("\n%s0:%d <%s> attach failed\n", mainboard_drv.name, e_dev->ioconf.slot, e_dev->full_name); continue; } /* Ensure registration has ended */ reg_state.in_registration = 0; e_dev->kdc->kdc_unit = e_dev->unit; } else { /* Announce unattached device */ printf("%s0:%d <%s=0x%x> unknown device\n", mainboard_drv.name, e_dev->ioconf.slot, e_dev->full_name, e_dev->id); } } } struct eisa_device * eisa_match_dev(e_dev, match_func) struct eisa_device *e_dev; char* (*match_func)(eisa_id_t); { struct eisa_device_node *e_node = eisa_dev_list; if (e_dev) { /* Start our search from the last successful match */ e_node = ((struct eisa_device_node *)e_dev)->next; } for(; e_node; e_node = e_node->next) { char *result; if (e_node->dev.driver) { /* Already claimed */ continue; } result = (*match_func)(e_node->dev.id); if (result) { free(e_node->dev.full_name, M_DEVBUF); e_node->dev.full_name = result; return (&(e_node->dev)); } } return NULL; } /* Interrupt and I/O space registration facitlities */ void eisa_reg_start(e_dev) struct eisa_device *e_dev; { /* * Announce the device. */ char *string; reg_state.in_registration = 1; reg_state.num_interrupts = 0; reg_state.num_ioaddrs = 0; reg_state.num_maddrs = 0; reg_state.column = 0; string = malloc(strlen(e_dev->full_name) + sizeof(" <>") + /*NULL*/1, M_TEMP, M_NOWAIT); if(!string) { printf("eisa0: cannot malloc device description string\n"); return; } sprintf(string, " <%s>", e_dev->full_name); eisa_reg_print(e_dev, string, /*separator=*/NULL); free(string, M_TEMP); } /* * Output registration information mindfull of screen wrap. * Output an optional character separator before the string * if the line does not wrap. */ void eisa_reg_print(e_dev, string, separator) struct eisa_device *e_dev; char *string; char *separator; { int len = strlen(string); if(separator) len++; if(reg_state.column + len > MAX_COL) { printf("\n"); reg_state.column = 0; } else if(separator) { printf("%c", *separator); reg_state.column++; } if(reg_state.column == 0) reg_state.column += printf("%s%ld:%s", e_dev->driver->name, e_dev->unit, string); else reg_state.column += printf("%s", string); } /* Interrupt and I/O space registration facitlities */ void eisa_reg_end(e_dev) struct eisa_device *e_dev; { if( reg_state.in_registration ) { /* * The device should have called eisa_registerdev() * during its probe. So hopefully we can use the kdc * to weed out ISA/VL devices that use EISA id registers. */ char string[25]; if (e_dev->kdc && (e_dev->kdc->kdc_parent == &kdc_isa0)) { sprintf(string, " on isa"); } else { sprintf(string, " on %s0 slot %d", mainboard_drv.name, e_dev->ioconf.slot); } eisa_reg_print(e_dev, string, NULL); printf("\n"); reg_state.in_registration = 0; } else printf("eisa_reg_end called outside of a " "registration session\n"); } int eisa_add_intr(e_dev, irq) struct eisa_device *e_dev; int irq; { e_dev->ioconf.irq |= 1ul << irq; return 0; } int eisa_reg_intr(e_dev, irq, func, arg, maskptr, shared) struct eisa_device *e_dev; int irq; void (*func)(void *); void *arg; u_int *maskptr; int shared; { int result; int s; char string[25]; char separator = ','; #if NOT_YET /* * Punt on conflict detection for the moment. * I want to develop a generic routine to do * this for all device types. */ int checkthese = CC_IRQ; if (haveseen_dev(dev, checkthese)) return 1; #endif if (reg_state.in_registration) { s = splhigh(); /* * This should really go to a routine that can optionally * handle shared interrupts. */ result = register_intr(irq, /* isa irq */ 0, /* deviced?? */ 0, /* flags? */ (inthand2_t*) func, /* handler */ maskptr, /* mask pointer */ (int)arg); /* handler arg */ if (result) { printf ("\neisa_reg_int: result=%d\n", result); splx(s); return (result); }; update_intr_masks(); splx(s); } else return EPERM; e_dev->ioconf.irq |= 1ul << irq; sprintf(string, " irq %d", irq); eisa_reg_print(e_dev, string, reg_state.num_interrupts ? &separator : NULL); reg_state.num_interrupts++; return (0); } int eisa_release_intr(e_dev, irq, func) struct eisa_device *e_dev; int irq; void (*func)(void *); { int result; int s; if (!(e_dev->ioconf.irq & (1ul << irq))) { printf("%s%ld: Attempted to release an interrupt (%d) " "it doesn't own\n", e_dev->driver->name, e_dev->unit, irq); return (-1); } s = splhigh(); INTRDIS ((1ul<ioconf.irq & (1ul << irq))) { printf("%s%ld: Attempted to enable an interrupt (%d) " "it doesn't own\n", e_dev->driver->name, e_dev->unit, irq); return (-1); } s = splhigh(); INTREN((1ul << irq)); splx(s); return 0; } static int eisa_add_resvaddr(e_dev, head, base, size, flags) struct eisa_device *e_dev; struct resvlist *head; u_long base; u_long size; int flags; { resvaddr_t *reservation; reservation = (resvaddr_t *)malloc(sizeof(resvaddr_t), M_DEVBUF, M_NOWAIT); if(!reservation) return (ENOMEM); reservation->addr = base; reservation->size = size; reservation->flags = flags; if (!head->lh_first) { LIST_INSERT_HEAD(head, reservation, links); } else { resvaddr_t *node; for(node = head->lh_first; node; node = node->links.le_next) { if (node->addr > reservation->addr) { /* * List is sorted in increasing * address order. */ LIST_INSERT_BEFORE(node, reservation, links); break; } if (node->addr == reservation->addr) { /* * If the entry we want to add * matches any already in here, * fail. */ free(reservation, M_DEVBUF); return (EEXIST); } if (!node->links.le_next) { LIST_INSERT_AFTER(node, reservation, links); break; } } } return (0); } int eisa_add_mspace(e_dev, mbase, msize, flags) struct eisa_device *e_dev; u_long mbase; u_long msize; int flags; { return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.maddrs), mbase, msize, flags); } int eisa_add_iospace(e_dev, iobase, iosize, flags) struct eisa_device *e_dev; u_long iobase; u_long iosize; int flags; { return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), iobase, iosize, flags); } static int eisa_reg_resvaddr(e_dev, head, resvaddr, reg_count) struct eisa_device *e_dev; struct resvlist *head; resvaddr_t *resvaddr; int *reg_count; { if (reg_state.in_registration) { resvaddr_t *node; /* * Ensure that this resvaddr is actually in the devices' * reservation list. */ for(node = head->lh_first; node; node = node->links.le_next) { if (node == resvaddr) { char buf[35]; char separator = ','; char *string = buf; if (*reg_count == 0) { /* First time */ string += sprintf(string, " at"); } if (node->size == 1 || (node->flags & RESVADDR_BITMASK)) sprintf(string, " 0x%lx", node->addr); else sprintf(string, " 0x%lx-0x%lx", node->addr, node->addr + node->size - 1); eisa_reg_print(e_dev, buf, *reg_count ? &separator : NULL); (*reg_count)++; e_dev->kdc->kdc_datalen += sizeof(resvaddr_t); return (0); } } return (ENOENT); } return EPERM; } int eisa_reg_mspace(e_dev, resvaddr) struct eisa_device *e_dev; resvaddr_t *resvaddr; { #ifdef NOT_YET /* * Punt on conflict detection for the moment. * I want to develop a generic routine to do * this for all device types. */ int checkthese = CC_MADDR; if (haveseen_dev(dev, checkthese)) return -1; #endif return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.maddrs), resvaddr, &(reg_state.num_maddrs))); } int eisa_reg_iospace(e_dev, resvaddr) struct eisa_device *e_dev; resvaddr_t *resvaddr; { #ifdef NOT_YET /* * Punt on conflict detection for the moment. * I want to develop a generic routine to do * this for all device types. */ int checkthese = CC_IOADDR; if (haveseen_dev(dev, checkthese)) return -1; #endif return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), resvaddr, &(reg_state.num_ioaddrs))); } int eisa_registerdev(e_dev, driver, kdc_template) struct eisa_device *e_dev; struct eisa_driver *driver; struct kern_devconf *kdc_template; { e_dev->driver = driver; /* Driver now owns this device */ e_dev->kdc = (struct kern_devconf *)malloc(sizeof(struct kern_devconf), M_DEVBUF, M_NOWAIT); if (!e_dev->kdc) { printf("WARNING: eisa_registerdev unable to malloc! " "Device kdc will not be registerd\n"); return 1; } bcopy(kdc_template, e_dev->kdc, sizeof(*kdc_template)); e_dev->kdc->kdc_description = e_dev->full_name; e_dev->kdc->kdc_parentdata = e_dev; dev_attach(e_dev->kdc); return (0); } int eisa_generic_externalize(struct kern_devconf *kdc, struct sysctl_req *req) { struct eisa_device *e_dev; resvaddr_t *node; void *buf; /* Temporary externalizing buffer */ void *bufp; /* Current offset in the buffer */ void *offset; /* Offset relative to target address space */ void *ioa_prev; /* Prev Node entries relative to target address space */ void *ma_prev; /* Prev Node entries relative to target address space */ int retval; - offset = req->oldptr + req->oldidx; + offset = (char *)req->oldptr + req->oldidx; buf = malloc(kdc->kdc_datalen, M_TEMP, M_NOWAIT); if (!buf) return 0; bufp = buf; bcopy(kdc->kdc_eisa, bufp, sizeof(struct eisa_device)); e_dev = bufp; /* Calculate initial prev nodes */ - ioa_prev = offset + ((void *)&(e_dev->ioconf.ioaddrs.lh_first) - - (void *)e_dev); - ma_prev = offset + ((void *)&(e_dev->ioconf.maddrs.lh_first) - - (void *)e_dev); + ioa_prev = (char *)offset + ((char *)&(e_dev->ioconf.ioaddrs.lh_first) + - (char *)e_dev); + ma_prev = (char *)offset + ((char *)&(e_dev->ioconf.maddrs.lh_first) + - (char *)e_dev); - offset += sizeof(*e_dev); - bufp += sizeof(*e_dev); + offset = (char *)offset + sizeof(*e_dev); + bufp = (char *)bufp + sizeof(*e_dev); if (e_dev->ioconf.ioaddrs.lh_first) { node = e_dev->ioconf.ioaddrs.lh_first; e_dev->ioconf.ioaddrs.lh_first = offset; for(;node;node = node->links.le_next) { resvaddr_t *out_node; bcopy(node, bufp, sizeof(resvaddr_t)); out_node = (resvaddr_t *)bufp; - bufp += sizeof(resvaddr_t); - offset += sizeof(resvaddr_t); + bufp = (char *)bufp + sizeof(resvaddr_t); + offset = (char *)offset + sizeof(resvaddr_t); out_node->links.le_prev = ioa_prev; - ioa_prev += sizeof(resvaddr_t); + ioa_prev = (char *)ioa_prev + sizeof(resvaddr_t); if (out_node->links.le_next) out_node->links.le_next = offset; } } if (e_dev->ioconf.maddrs.lh_first) { node = e_dev->ioconf.maddrs.lh_first; e_dev->ioconf.maddrs.lh_first = offset; for(;node;node = node->links.le_next) { resvaddr_t *out_node; bcopy(node, bufp, sizeof(resvaddr_t)); out_node = (resvaddr_t *)bufp; - bufp += sizeof(resvaddr_t); - offset += sizeof(resvaddr_t); + bufp = (char *)bufp + sizeof(resvaddr_t); + offset = (char *)offset + sizeof(resvaddr_t); out_node->links.le_prev = ma_prev; - ma_prev += sizeof(resvaddr_t); + ma_prev = (char *)ma_prev + sizeof(resvaddr_t); if (out_node->links.le_next) out_node->links.le_next = offset; } } retval = SYSCTL_OUT(req, buf, kdc->kdc_datalen); free(buf, M_TEMP); return retval; } Index: head/sys/i386/isa/if_cx.c =================================================================== --- head/sys/i386/isa/if_cx.c (revision 17970) +++ head/sys/i386/isa/if_cx.c (revision 17971) @@ -1,848 +1,848 @@ /* * Cronyx-Sigma adapter driver for FreeBSD. * Supports PPP/HDLC and Cisco/HDLC protocol in synchronous mode, * and asyncronous channels with full modem control. * Keepalive protocol implemented in both Cisco and PPP modes. * * Copyright (C) 1994 Cronyx Ltd. * Author: Serge Vakulenko, * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * Version 1.9, Wed Oct 4 18:58:15 MSK 1995 */ #undef DEBUG #include "cx.h" #include "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #include #endif #include #ifdef DEVFS extern struct cdevsw cx_cdevsw; #include #endif /*DEVFS*/ #include #define watchdog_func_t void(*)(struct ifnet *) #define start_func_t void(*)(struct ifnet*) #include #include #include /* XXX exported. */ void cxswitch (cx_chan_t *c, cx_soft_opt_t new); static int cxprobe __P((struct isa_device *id)); static int cxattach __P((struct isa_device *id)); static void cxput __P((cx_chan_t *c, char b)); static void cxsend __P((cx_chan_t *c)); static void cxrinth __P((cx_chan_t *c)); static int cxtinth __P((cx_chan_t *c)); #ifdef DEBUG # define print(s) printf s #else # define print(s) {/*void*/} #endif #define TXTIMEOUT 10 /* transmit timeout in seconds */ #define DMABUFSZ (6*256) /* buffer size */ #define PPP_HEADER_LEN 4 /* size of PPP header */ /* * Under BSDI it's possible to use general p2p protocol scheme, * as well as our own one. Switching is done via IFF_ALTPHYS flag. * Our ifnet pointer holds the buffer large enough to contain * any of sppp and p2p structures. */ #define IFSTRUCTSZ (sizeof (struct sppp)) #define IFNETSZ (sizeof (struct ifnet)) static int cxsioctl (struct ifnet *ifp, int cmd, caddr_t data); static void cxstart (struct ifnet *ifp); static void cxwatchdog (struct ifnet *ifp); static void cxinput (cx_chan_t *c, void *buf, unsigned len); extern int cxrinta (cx_chan_t *c); extern void cxtinta (cx_chan_t *c); extern void cxmint (cx_chan_t *c); extern void cxtimeout (caddr_t a); static void cxdown (cx_chan_t *c); static void cxup (cx_chan_t *c); cx_board_t cxboard [NCX]; /* adapter state structures */ cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */ static unsigned short irq_valid_values [] = { 3, 5, 7, 10, 11, 12, 15, 0 }; static unsigned short drq_valid_values [] = { 5, 6, 7, 0 }; static unsigned short port_valid_values [] = { 0x240, 0x260, 0x280, 0x300, 0x320, 0x380, 0x3a0, 0, }; static char cxdescription [80]; struct kern_devconf kdc_cx [NCX] = { { 0, 0, 0, "cx", 0, { MDDT_ISA, 0, "net" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, 0, DC_IDLE, cxdescription, DC_CLS_SERIAL } }; /* * Check that the value is contained in the list of correct values. */ static int valid (unsigned short value, unsigned short *list) { while (*list) if (value == *list++) return (1); return (0); } /* * Print the mbuf chain, for debug purposes only. */ static void printmbuf (struct mbuf *m) { printf ("mbuf:"); for (; m; m=m->m_next) { if (m->m_flags & M_PKTHDR) printf (" HDR %d:", m->m_pkthdr.len); if (m->m_flags & M_EXT) printf (" EXT:"); printf (" %d", m->m_len); } printf ("\n"); } /* * Make an mbuf from data. */ static struct mbuf *makembuf (void *buf, unsigned len) { struct mbuf *m, *o, *p; MGETHDR (m, M_DONTWAIT, MT_DATA); if (! m) return (0); if (len >= MINCLSIZE) MCLGET (m, M_DONTWAIT); m->m_pkthdr.len = len; m->m_len = 0; p = m; while (len) { unsigned n = M_TRAILINGSPACE (p); if (n > len) n = len; if (! n) { /* Allocate new mbuf. */ o = p; MGET (p, M_DONTWAIT, MT_DATA); if (! p) { m_freem (m); return (0); } if (len >= MINCLSIZE) MCLGET (p, M_DONTWAIT); p->m_len = 0; o->m_next = p; n = M_TRAILINGSPACE (p); if (n > len) n = len; } bcopy (buf, mtod (p, caddr_t) + p->m_len, n); p->m_len += n; - buf += n; + buf = (char *)buf + n; len -= n; } return (m); } /* * Test the presence of the adapter on the given i/o port. */ static int cxprobe (struct isa_device *id) { int unit = id->id_unit; int iobase = id->id_iobase; int irq = id->id_irq; int drq = id->id_drq; int irqnum; irqnum = ffs (irq) - 1; print (("cx%d: probe iobase=0x%x irq=%d drq=%d\n", unit, iobase, irqnum, drq)); if (! valid (irqnum, irq_valid_values)) { printf ("cx%d: Incorrect IRQ: %d\n", unit, irqnum); return (0); } if (! valid (iobase, port_valid_values)) { printf ("cx%d: Incorrect port address: 0x%x\n", unit, iobase); return (0); } if (! valid (drq, drq_valid_values)) { printf ("cx%d: Incorrect DMA channel: %d\n", unit, drq); return (0); } if (! cx_probe_board (iobase)) return (0); return (1); } /* * The adapter is present, initialize the driver structures. */ #ifdef DEVFS static void *cx_devfs_token; #endif static int cxattach (struct isa_device *id) { int unit = id->id_unit; int iobase = id->id_iobase; int irq = id->id_irq; int drq = id->id_drq; cx_board_t *b = cxboard + unit; int i; /* Initialize the board structure. */ cx_init (b, unit, iobase, ffs(irq)-1, drq); for (i=0; ichan + i; int u = b->num*NCHAN + i; cxchan[u] = c; if (c->type == T_NONE) continue; /* Allocate the buffer memory. */ c->arbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT); c->brbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT); c->atbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT); c->btbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT); /* All buffers should be located in lower 16M of memory! */ if (!c->arbuf || !c->brbuf || !c->atbuf || !c->btbuf) { printf ("cx%d.%d: No memory for channel buffers\n", c->board->num, c->num); c->type = T_NONE; } switch (c->type) { case T_SYNC_RS232: case T_SYNC_V35: case T_SYNC_RS449: case T_UNIV_RS232: case T_UNIV_RS449: case T_UNIV_V35: c->ifp = malloc (IFSTRUCTSZ, M_DEVBUF, M_NOWAIT); if (! c->ifp) { printf ("cx%d.%d: No memory for ifnet buffer\n", c->board->num, c->num); c->type = T_NONE; continue; } bzero (c->ifp, IFSTRUCTSZ); c->master = c->ifp; c->ifp->if_softc = c; c->ifp->if_unit = u; c->ifp->if_name = "cx"; c->ifp->if_mtu = PP_MTU; c->ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; c->ifp->if_ioctl = cxsioctl; c->ifp->if_start = (start_func_t) cxstart; c->ifp->if_watchdog = (watchdog_func_t) cxwatchdog; /* Init routine is never called by upper level? */ sppp_attach (c->ifp); if_attach (c->ifp); #if NBPFILTER > 0 /* If BPF is in the kernel, call the attach for it. */ bpfattach (c->ifp, DLT_PPP, PPP_HEADER_LEN); #endif } } /* Reset the adapter. */ cx_setup_board (b); /* Activate the timeout routine. */ if (unit == 0) timeout ((timeout_func_t) cxtimeout, 0, hz*5); if (unit != 0) kdc_cx[unit] = kdc_cx[0]; kdc_cx[unit].kdc_unit = unit; kdc_cx[unit].kdc_isa = id; sprintf (cxdescription, "Cronyx-Sigma-%s sync/async serial adapter", b->name); dev_attach (&kdc_cx[unit]); printf ("cx%d: \n", unit, b->name); #ifdef DEVFS cx_devfs_token = devfs_add_devswf(&cx_cdevsw, 0, DV_CHR, 0, 0, 0600, "cx"); #endif return (1); } struct isa_driver cxdriver = { cxprobe, cxattach, "cx" }; /* * Process an ioctl request. */ static int cxsioctl (struct ifnet *ifp, int cmd, caddr_t data) { cx_chan_t *q, *c = ifp->if_softc; int error, s, was_up, should_be_up; /* * No socket ioctls while the channel is in async mode. */ if (c->type==T_NONE || c->mode==M_ASYNC) return (EINVAL); /* * Socket ioctls on slave subchannels are not allowed. */ if (c->master != c->ifp) return (EBUSY); was_up = (ifp->if_flags & IFF_RUNNING) != 0; error = sppp_ioctl (ifp, cmd, data); if (error) return (error); print (("cxioctl (%d.%d, ", c->board->num, c->num)); switch (cmd) { default: print (("0x%x)\n", cmd)); return (0); case SIOCADDMULTI: print (("SIOCADDMULTI)\n")); return (0); case SIOCDELMULTI: print (("SIOCDELMULTI)\n")); return (0); case SIOCSIFFLAGS: print (("SIOCSIFFLAGS)\n")); break; case SIOCSIFADDR: print (("SIOCSIFADDR)\n")); break; } /* We get here only in case of SIFFLAGS or SIFADDR. */ s = splimp (); should_be_up = (ifp->if_flags & IFF_RUNNING) != 0; if (!was_up && should_be_up) { /* Interface goes up -- start it. */ cxup (c); /* Start all slave subchannels. */ for (q=c->slaveq; q; q=q->slaveq) cxup (q); cxstart (c->ifp); } else if (was_up && !should_be_up) { /* Interface is going down -- stop it. */ cxdown (c); /* Stop all slave subchannels. */ for (q=c->slaveq; q; q=q->slaveq) cxdown (q); /* Flush the interface output queue */ if (! c->sopt.ext) sppp_flush (c->ifp); } splx (s); return (0); } /* * Stop the interface. Called on splimp(). */ static void cxdown (cx_chan_t *c) { unsigned short port = c->chip->port; print (("cx%d.%d: cxdown\n", c->board->num, c->num)); /* The interface is down, stop it */ c->ifp->if_flags &= ~IFF_OACTIVE; /* Reset the channel (for sync modes only) */ outb (CAR(port), c->num & 3); outb (STCR(port), STC_ABORTTX | STC_SNDSPC); cx_setup_chan (c); } /* * Start the interface. Called on splimp(). */ static void cxup (cx_chan_t *c) { unsigned short port = c->chip->port; /* The interface is up, start it */ print (("cx%d.%d: cxup\n", c->board->num, c->num)); kdc_cx[c->board->num].kdc_state = DC_BUSY; /* Initialize channel, enable receiver and transmitter */ cx_cmd (port, CCR_INITCH | CCR_ENRX | CCR_ENTX); /* Repeat the command, to avoid the rev.H bug */ cx_cmd (port, CCR_INITCH | CCR_ENRX | CCR_ENTX); /* Start receiver */ outw (ARBCNT(port), DMABUFSZ); outb (ARBSTS(port), BSTS_OWN24); outw (BRBCNT(port), DMABUFSZ); outb (BRBSTS(port), BSTS_OWN24); /* Raise DTR and RTS */ cx_chan_dtr (c, 1); cx_chan_rts (c, 1); /* Enable interrupts */ outb (IER(port), IER_RXD | IER_TXD); } /* * Fill transmitter buffer with data. */ static void cxput (cx_chan_t *c, char b) { struct mbuf *m; unsigned char *buf; unsigned short port = c->chip->port, len, cnt_port, sts_port; /* Choose the buffer. */ if (b == 'A') { buf = c->atbuf; cnt_port = ATBCNT(port); sts_port = ATBSTS(port); } else { buf = c->btbuf; cnt_port = BTBCNT(port); sts_port = BTBSTS(port); } /* Is it busy? */ if (inb (sts_port) & BSTS_OWN24) { if (c->ifp->if_flags & IFF_DEBUG) print (("cx%d.%d: tbuf %c already busy, bsts=%b\n", c->board->num, c->num, b, inb (sts_port), BSTS_BITS)); goto ret; } /* Get the packet to send. */ m = sppp_dequeue (c->master); if (! m) return; len = m->m_pkthdr.len; /* Count the transmitted bytes to the subchannel, not the master. */ c->master->if_obytes -= len + 3; c->ifp->if_obytes += len + 3; c->stat->obytes += len + 3; if (len >= DMABUFSZ) { printf ("cx%d.%d: too long packet: %d bytes: ", c->board->num, c->num, len); printmbuf (m); m_freem (m); return; } m_copydata (m, 0, len, buf); #if NBPFILTER > 0 if (c->ifp->if_bpf) bpf_mtap (c->ifp, m); #endif m_freem (m); /* Start transmitter. */ outw (cnt_port, len); outb (sts_port, BSTS_EOFR | BSTS_INTR | BSTS_OWN24); if (c->ifp->if_flags & IFF_DEBUG) print (("cx%d.%d: enqueue %d bytes to %c\n", c->board->num, c->num, len, buf==c->atbuf ? 'A' : 'B')); ret: c->ifp->if_flags |= IFF_OACTIVE; } /* * Start output on the (slave) interface. Get another datagram to send * off of the interface queue, and copy it to the interface * before starting the output. */ static void cxsend (cx_chan_t *c) { unsigned short port = c->chip->port; if (c->ifp->if_flags & IFF_DEBUG) print (("cx%d.%d: cxsend\n", c->board->num, c->num)); /* No output if the interface is down. */ if (! (c->ifp->if_flags & IFF_RUNNING)) return; /* Set the current channel number. */ outb (CAR(port), c->num & 3); /* Determine the buffer order. */ if (inb (DMABSTS(port)) & DMABSTS_NTBUF) { cxput (c, 'B'); cxput (c, 'A'); } else { cxput (c, 'A'); cxput (c, 'B'); } /* Set up transmit timeout. */ if (c->master->if_flags & IFF_OACTIVE) c->master->if_timer = TXTIMEOUT; /* * Enable TXMPTY interrupt, * to catch the case when the second buffer is empty. */ if ((inb (ATBSTS(port)) & BSTS_OWN24) && (inb (BTBSTS(port)) & BSTS_OWN24)) { outb (IER(port), IER_RXD | IER_TXD | IER_TXMPTY); } else outb (IER(port), IER_RXD | IER_TXD); } /* * Start output on the (master) interface and all slave interfaces. * Always called on splimp(). */ static void cxstart (struct ifnet *ifp) { cx_chan_t *q, *c = ifp->if_softc; if (c->ifp->if_flags & IFF_DEBUG) print (("cx%d.%d: cxstart\n", c->board->num, c->num)); /* Start the master subchannel. */ cxsend (c); /* Start all slave subchannels. */ if (c->slaveq && ! sppp_isempty (c->master)) for (q=c->slaveq; q; q=q->slaveq) if ((q->ifp->if_flags & IFF_RUNNING) && ! (q->ifp->if_flags & IFF_OACTIVE)) cxsend (q); } /* * Handle transmit timeouts. * Recover after lost transmit interrupts. * Always called on splimp(). */ static void cxwatchdog (struct ifnet *ifp) { cx_chan_t *q, *c = ifp->if_softc; if (! (ifp->if_flags & IFF_RUNNING)) return; if (ifp->if_flags & IFF_DEBUG) printf ("cx%d.%d: device timeout\n", c->board->num, c->num); cxdown (c); for (q=c->slaveq; q; q=q->slaveq) cxdown (q); cxup (c); for (q=c->slaveq; q; q=q->slaveq) cxup (q); cxstart (ifp); } /* * Handle receive interrupts, including receive errors and * receive timeout interrupt. */ static void cxrinth (cx_chan_t *c) { unsigned short port = c->chip->port; unsigned short len, risr = inw (RISR(port)); /* Receive errors. */ if (risr & (RIS_BUSERR | RIS_OVERRUN | RISH_CRCERR | RISH_RXABORT)) { if (c->ifp->if_flags & IFF_DEBUG) printf ("cx%d.%d: receive error, risr=%b\n", c->board->num, c->num, risr, RISH_BITS); ++c->ifp->if_ierrors; ++c->stat->ierrs; if (risr & RIS_OVERRUN) ++c->ifp->if_collisions; } else if (risr & RIS_EOBUF) { if (c->ifp->if_flags & IFF_DEBUG) print (("cx%d.%d: hdlc receive interrupt, risr=%b, arbsts=%b, brbsts=%b\n", c->board->num, c->num, risr, RISH_BITS, inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS)); ++c->stat->ipkts; /* Handle received data. */ len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port)); c->stat->ibytes += len; if (len > DMABUFSZ) { /* Fatal error: actual DMA transfer size * exceeds our buffer size. It could be caused * by incorrectly programmed DMA register or * hardware fault. Possibly, should panic here. */ printf ("cx%d.%d: panic! DMA buffer overflow: %d bytes\n", c->board->num, c->num, len); ++c->ifp->if_ierrors; } else if (! (risr & RIS_EOFR)) { /* The received frame does not fit in the DMA buffer. * It could be caused by serial lie noise, * or if the peer has too big MTU. */ if (c->ifp->if_flags & IFF_DEBUG) printf ("cx%d.%d: received frame length exceeds MTU, risr=%b\n", c->board->num, c->num, risr, RISH_BITS); ++c->ifp->if_ierrors; } else { /* Valid frame received. */ if (c->ifp->if_flags & IFF_DEBUG) print (("cx%d.%d: hdlc received %d bytes\n", c->board->num, c->num, len)); cxinput (c, (risr & RIS_BB) ? c->brbuf : c->arbuf, len); ++c->ifp->if_ipackets; } } else if (c->ifp->if_flags & IFF_DEBUG) { print (("cx%d.%d: unknown hdlc receive interrupt, risr=%b\n", c->board->num, c->num, risr, RISH_BITS)); ++c->stat->ierrs; } /* Restart receiver. */ if (! (inb (ARBSTS(port)) & BSTS_OWN24)) { outw (ARBCNT(port), DMABUFSZ); outb (ARBSTS(port), BSTS_OWN24); } if (! (inb (BRBSTS(port)) & BSTS_OWN24)) { outw (BRBCNT(port), DMABUFSZ); outb (BRBSTS(port), BSTS_OWN24); } } /* * Handle transmit interrupt. */ static int cxtinth (cx_chan_t *c) { unsigned short port = c->chip->port; unsigned char tisr = inb (TISR(port)); unsigned char teoir = 0; c->ifp->if_flags &= ~IFF_OACTIVE; if (c->ifp == c->master) c->ifp->if_timer = 0; if (tisr & (TIS_BUSERR | TIS_UNDERRUN)) { /* if (c->ifp->if_flags & IFF_DEBUG) */ print (("cx%d.%d: transmit error, tisr=%b, atbsts=%b, btbsts=%b\n", c->board->num, c->num, tisr, TIS_BITS, inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS)); ++c->ifp->if_oerrors; ++c->stat->oerrs; /* Terminate the failed buffer. */ /* teoir = TEOI_TERMBUFF; */ } else if (c->ifp->if_flags & IFF_DEBUG) print (("cx%d.%d: hdlc transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n", c->board->num, c->num, tisr, TIS_BITS, inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS)); if (tisr & TIS_EOFR) { ++c->ifp->if_opackets; ++c->stat->opkts; } /* Start output on the (sub-) channel. */ cxsend (c); return (teoir); } void cxintr (int bnum) { cx_board_t *b = cxboard + bnum; while (! (inw (BSR(b->port)) & BSR_NOINTR)) { /* Acknowledge the interrupt to enter the interrupt context. */ /* Read the local interrupt vector register. */ unsigned char livr = inb (IACK(b->port, BRD_INTR_LEVEL)); cx_chan_t *c = b->chan + (livr>>2 & 0xf); unsigned short port = c->chip->port; unsigned short eoiport = REOIR(port); unsigned char eoi = 0; if (c->type == T_NONE) { printf ("cx%d.%d: unexpected interrupt, livr=0x%x\n", c->board->num, c->num, livr); continue; /* incorrect channel number? */ } /* print (("cx%d.%d: interrupt, livr=0x%x\n", c->board->num, c->num, livr)); */ /* Clear RTS to stop receiver data flow while we are busy * processing the interrupt, thus avoiding underruns. */ if (! c->sopt.norts) { outb (MSVR_RTS(port), 0); c->rts = 0; } switch (livr & 3) { case LIV_EXCEP: /* receive exception */ case LIV_RXDATA: /* receive interrupt */ ++c->stat->rintr; switch (c->mode) { case M_ASYNC: eoi = cxrinta (c); break; case M_HDLC: cxrinth (c); break; default:; /* No bisync and X.21 yet */ } break; case LIV_TXDATA: /* transmit interrupt */ ++c->stat->tintr; eoiport = TEOIR(port); switch (c->mode) { case M_ASYNC: cxtinta (c); break; case M_HDLC: eoi = cxtinth (c); break; default:; /* No bisync and X.21 yet */ } break; case LIV_MODEM: /* modem/timer interrupt */ ++c->stat->mintr; eoiport = MEOIR(port); cxmint (c); break; } /* Raise RTS for this channel if and only if * both receive buffers are empty. */ if (! c->sopt.norts && (inb (CSR(port)) & CSRA_RXEN) && (inb (ARBSTS(port)) & BSTS_OWN24) && (inb (BRBSTS(port)) & BSTS_OWN24)) { outb (MSVR_RTS(port), MSV_RTS); c->rts = 1; } /* Exit from interrupt context. */ outb (eoiport, eoi); /* Master channel - start output on all idle subchannels. */ if (c->master == c->ifp && c->slaveq && (livr & 3) == LIV_TXDATA && c->mode == M_HDLC && ! sppp_isempty (c->ifp)) { cx_chan_t *q; for (q=c->slaveq; q; q=q->slaveq) if ((q->ifp->if_flags & IFF_RUNNING) && ! (q->ifp->if_flags & IFF_OACTIVE)) cxsend (q); } } } /* * Process the received packet. */ static void cxinput (cx_chan_t *c, void *buf, unsigned len) { /* Make an mbuf. */ struct mbuf *m = makembuf (buf, len); if (! m) { if (c->ifp->if_flags & IFF_DEBUG) printf ("cx%d.%d: no memory for packet\n", c->board->num, c->num); ++c->ifp->if_iqdrops; return; } m->m_pkthdr.rcvif = c->master; #ifdef DEBUG if (c->ifp->if_flags & IFF_DEBUG) printmbuf (m); #endif #if NBPFILTER > 0 /* * Check if there's a BPF listener on this interface. * If so, hand off the raw packet to bpf. */ if (c->ifp->if_bpf) bpf_tap (c->ifp, buf, len); #endif /* Count the received bytes to the subchannel, not the master. */ c->master->if_ibytes -= len + 3; c->ifp->if_ibytes += len + 3; sppp_input (c->master, m); } void cxswitch (cx_chan_t *c, cx_soft_opt_t new) { new.ext = 0; if (! new.ext) { struct sppp *sp = (struct sppp*) c->ifp; if (new.cisco) sp->pp_flags |= PP_CISCO; else sp->pp_flags &= ~PP_CISCO; if (new.keepalive) sp->pp_flags |= PP_KEEPALIVE; else sp->pp_flags &= ~PP_KEEPALIVE; } c->sopt = new; } Index: head/sys/i386/isa/istallion.c =================================================================== --- head/sys/i386/isa/istallion.c (revision 17970) +++ head/sys/i386/isa/istallion.c (revision 17971) @@ -1,3878 +1,3878 @@ /*****************************************************************************/ /* * istallion.c -- stallion intelligent multiport serial driver. * * Copyright (c) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Greg Ungerer. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. * - * $Id: istallion.c,v 1.3 1996/06/12 04:26:35 gpalmer Exp $ + * $Id: istallion.c,v 1.4 1996/06/18 01:22:25 bde Exp $ */ /*****************************************************************************/ #define TTYDEFCHARS 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*****************************************************************************/ /* * Define the version level of the kernel - so we can compile in the * appropriate bits of code. By default this will compile for a 2.1 * level kernel. */ #define VFREEBSD 220 #if VFREEBSD >= 220 #define STATIC static #else #define STATIC #endif /*****************************************************************************/ /* * Define different board types. Not all of the following board types * are supported by this driver. But I will use the standard "assigned" * board numbers. Currently supported boards are abbreviated as: * ECP = EasyConnection 8/64, ONB = ONboard, BBY = Brumby and * STAL = Stallion. */ #define BRD_UNKNOWN 0 #define BRD_STALLION 1 #define BRD_BRUMBY4 2 #define BRD_ONBOARD2 3 #define BRD_ONBOARD 4 #define BRD_BRUMBY8 5 #define BRD_BRUMBY16 6 #define BRD_ONBOARDE 7 #define BRD_ONBOARD32 9 #define BRD_ONBOARD2_32 10 #define BRD_ONBOARDRS 11 #define BRD_EASYIO 20 #define BRD_ECH 21 #define BRD_ECHMC 22 #define BRD_ECP 23 #define BRD_ECPE 24 #define BRD_ECPMC 25 #define BRD_ECHPCI 26 #define BRD_BRUMBY BRD_BRUMBY4 /*****************************************************************************/ /* * Define important driver limitations. */ #define STL_MAXBRDS 8 #define STL_MAXPANELS 4 #define STL_PORTSPERPANEL 16 #define STL_PORTSPERBRD 64 #define STL_MAXCHANS STL_PORTSPERBRD /* * Define the important minor number break down bits. These have been * chosen to be "compatable" with the standard sio driver minor numbers. * Extra high bits are used to distinguish between boards and also for * really high port numbers (> 32). */ #define STL_CALLOUTDEV 0x80 #define STL_CTRLLOCK 0x40 #define STL_CTRLINIT 0x20 #define STL_CTRLDEV (STL_CTRLLOCK | STL_CTRLINIT) #define STL_MEMDEV 0x07000000 #define STL_DEFSPEED 9600 #define STL_DEFCFLAG (CS8 | CREAD | HUPCL) /*****************************************************************************/ /* * Define our local driver identity first. Set up stuff to deal with * all the local structures required by a serial tty driver. */ static char stli_drvname[] = "stli"; static char const stli_longdrvname[] = "Stallion Multiport Serial Driver"; static char const stli_drvversion[] = "0.0.5"; static int stli_nrbrds = 0; static int stli_doingtimeout = 0; static char *__file__ = /*__FILE__*/ "istallion.c"; /* * Define some macros to use to class define boards. */ #define BRD_ISA 0x1 #define BRD_EISA 0x2 #define BRD_MCA 0x4 #define BRD_PCI 0x8 static unsigned char stli_stliprobed[STL_MAXBRDS]; /*****************************************************************************/ /* * Define a set of structures to hold all the board/panel/port info * for our ports. These will be dynamically allocated as required at * driver initialization time. */ /* * Port and board structures to hold status info about each object. * The board structure contains pointers to structures for each port * connected to it. Panels are not distinguished here, since * communication with the slave board will always be on a per port * basis. */ typedef struct { struct tty tty; int portnr; int panelnr; int brdnr; int ioaddr; int callout; int devnr; int dtrwait; int dotimestamp; int waitopens; int hotchar; int rc; int argsize; void *argp; unsigned int state; unsigned int sigs; struct termios initintios; struct termios initouttios; struct termios lockintios; struct termios lockouttios; struct timeval timestamp; asysigs_t asig; unsigned long addr; unsigned long rxlost; unsigned long rxoffset; unsigned long txoffset; unsigned long pflag; unsigned int rxsize; unsigned int txsize; unsigned char reqidx; unsigned char reqbit; unsigned char portidx; unsigned char portbit; } stliport_t; /* * Use a structure of function pointers to do board level operations. * These include, enable/disable, paging shared memory, interrupting, etc. */ typedef struct stlibrd { int brdnr; int brdtype; int unitid; int state; int nrpanels; int nrports; int nrdevs; unsigned int iobase; unsigned long paddr; void *vaddr; int memsize; int pagesize; int hostoffset; int slaveoffset; int bitsize; int confbits; void (*init)(struct stlibrd *brdp); void (*enable)(struct stlibrd *brdp); void (*reenable)(struct stlibrd *brdp); void (*disable)(struct stlibrd *brdp); void (*intr)(struct stlibrd *brdp); void (*reset)(struct stlibrd *brdp); char *(*getmemptr)(struct stlibrd *brdp, unsigned long offset, int line); int panels[STL_MAXPANELS]; int panelids[STL_MAXPANELS]; stliport_t *ports[STL_PORTSPERBRD]; } stlibrd_t; static stlibrd_t *stli_brds[STL_MAXBRDS]; static int stli_shared = 0; /* * Keep a local char buffer for processing chars into the LD. We * do this to avoid copying from the boards shared memory one char * at a time. */ static int stli_rxtmplen; static stliport_t *stli_rxtmpport; static char stli_rxtmpbuf[TTYHOG]; /* * Define global stats structures. Not used often, and can be re-used * for each stats call. */ static comstats_t stli_comstats; static combrd_t stli_brdstats; static asystats_t stli_cdkstats; /* * Per board state flags. Used with the state field of the board struct. * Not really much here... All we need to do is keep track of whether * the board has been detected, and whether it is actully running a slave * or not. */ #define BST_FOUND 0x1 #define BST_STARTED 0x2 /* * Define the set of port state flags. These are marked for internal * state purposes only, usually to do with the state of communications * with the slave. They need to be updated atomically. */ #define ST_INITIALIZING 0x1 #define ST_INITIALIZED 0x2 #define ST_OPENING 0x4 #define ST_CLOSING 0x8 #define ST_CMDING 0x10 #define ST_RXING 0x20 #define ST_TXBUSY 0x40 #define ST_DOFLUSHRX 0x80 #define ST_DOFLUSHTX 0x100 #define ST_DOSIGS 0x200 #define ST_GETSIGS 0x400 #define ST_DTRWAIT 0x800 /* * Define an array of board names as printable strings. Handy for * referencing boards when printing trace and stuff. */ static char *stli_brdnames[] = { "Unknown", "Stallion", "Brumby", "ONboard-MC", "ONboard", "Brumby", "Brumby", "ONboard-EI", (char *) NULL, "ONboard", "ONboard-MC", "ONboard-MC", (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, "EasyIO", "EC8/32-AT", "EC8/32-MC", "EC8/64-AT", "EC8/64-EI", "EC8/64-MC", "EC8/32-PCI", }; /*****************************************************************************/ /* * Hardware configuration info for ECP boards. These defines apply * to the directly accessable io ports of the ECP. There is a set of * defines for each ECP board type, ISA, EISA and MCA. */ #define ECP_IOSIZE 4 #define ECP_MEMSIZE (128 * 1024) #define ECP_ATPAGESIZE (4 * 1024) #define ECP_EIPAGESIZE (64 * 1024) #define ECP_MCPAGESIZE (4 * 1024) #define STL_EISAID 0x8c4e /* * Important defines for the ISA class of ECP board. */ #define ECP_ATIREG 0 #define ECP_ATCONFR 1 #define ECP_ATMEMAR 2 #define ECP_ATMEMPR 3 #define ECP_ATSTOP 0x1 #define ECP_ATINTENAB 0x10 #define ECP_ATENABLE 0x20 #define ECP_ATDISABLE 0x00 #define ECP_ATADDRMASK 0x3f000 #define ECP_ATADDRSHFT 12 /* * Important defines for the EISA class of ECP board. */ #define ECP_EIIREG 0 #define ECP_EIMEMARL 1 #define ECP_EICONFR 2 #define ECP_EIMEMARH 3 #define ECP_EIENABLE 0x1 #define ECP_EIDISABLE 0x0 #define ECP_EISTOP 0x4 #define ECP_EIEDGE 0x00 #define ECP_EILEVEL 0x80 #define ECP_EIADDRMASKL 0x00ff0000 #define ECP_EIADDRSHFTL 16 #define ECP_EIADDRMASKH 0xff000000 #define ECP_EIADDRSHFTH 24 #define ECP_EIBRDENAB 0xc84 #define ECP_EISAID 0x4 /* * Important defines for the Micro-channel class of ECP board. * (It has a lot in common with the ISA boards.) */ #define ECP_MCIREG 0 #define ECP_MCCONFR 1 #define ECP_MCSTOP 0x20 #define ECP_MCENABLE 0x80 #define ECP_MCDISABLE 0x00 /* * Hardware configuration info for ONboard and Brumby boards. These * defines apply to the directly accessable io ports of these boards. */ #define ONB_IOSIZE 16 #define ONB_MEMSIZE (64 * 1024) #define ONB_ATPAGESIZE (64 * 1024) #define ONB_MCPAGESIZE (64 * 1024) #define ONB_EIMEMSIZE (128 * 1024) #define ONB_EIPAGESIZE (64 * 1024) /* * Important defines for the ISA class of ONboard board. */ #define ONB_ATIREG 0 #define ONB_ATMEMAR 1 #define ONB_ATCONFR 2 #define ONB_ATSTOP 0x4 #define ONB_ATENABLE 0x01 #define ONB_ATDISABLE 0x00 #define ONB_ATADDRMASK 0xff0000 #define ONB_ATADDRSHFT 16 #define ONB_HIMEMENAB 0x02 /* * Important defines for the EISA class of ONboard board. */ #define ONB_EIIREG 0 #define ONB_EIMEMARL 1 #define ONB_EICONFR 2 #define ONB_EIMEMARH 3 #define ONB_EIENABLE 0x1 #define ONB_EIDISABLE 0x0 #define ONB_EISTOP 0x4 #define ONB_EIEDGE 0x00 #define ONB_EILEVEL 0x80 #define ONB_EIADDRMASKL 0x00ff0000 #define ONB_EIADDRSHFTL 16 #define ONB_EIADDRMASKH 0xff000000 #define ONB_EIADDRSHFTH 24 #define ONB_EIBRDENAB 0xc84 #define ONB_EISAID 0x1 /* * Important defines for the Brumby boards. They are pretty simple, * there is not much that is programmably configurable. */ #define BBY_IOSIZE 16 #define BBY_MEMSIZE (64 * 1024) #define BBY_PAGESIZE (16 * 1024) #define BBY_ATIREG 0 #define BBY_ATCONFR 1 #define BBY_ATSTOP 0x4 /* * Important defines for the Stallion boards. They are pretty simple, * there is not much that is programmably configurable. */ #define STAL_IOSIZE 16 #define STAL_MEMSIZE (64 * 1024) #define STAL_PAGESIZE (64 * 1024) /* * Define the set of status register values for EasyConnection panels. * The signature will return with the status value for each panel. From * this we can determine what is attached to the board - before we have * actually down loaded any code to it. */ #define ECH_PNLSTATUS 2 #define ECH_PNL16PORT 0x20 #define ECH_PNLIDMASK 0x07 #define ECH_PNLINTRPEND 0x80 /* * Define some macros to do things to the board. Even those these boards * are somewhat related there is often significantly different ways of * doing some operation on it (like enable, paging, reset, etc). So each * board class has a set of functions which do the commonly required * operations. The macros below basically just call these functions, * generally checking for a NULL function - which means that the board * needs nothing done to it to achieve this operation! */ #define EBRDINIT(brdp) \ if (brdp->init != NULL) \ (* brdp->init)(brdp) #define EBRDENABLE(brdp) \ if (brdp->enable != NULL) \ (* brdp->enable)(brdp); #define EBRDDISABLE(brdp) \ if (brdp->disable != NULL) \ (* brdp->disable)(brdp); #define EBRDINTR(brdp) \ if (brdp->intr != NULL) \ (* brdp->intr)(brdp); #define EBRDRESET(brdp) \ if (brdp->reset != NULL) \ (* brdp->reset)(brdp); #define EBRDGETMEMPTR(brdp,offset) \ (* brdp->getmemptr)(brdp, offset, __LINE__) /* * Define the maximal baud rate. */ #define STL_MAXBAUD 230400 /*****************************************************************************/ /* * Define macros to extract a brd and port number from a minor number. * This uses the extended minor number range in the upper 2 bytes of * the device number. This gives us plenty of minor numbers to play * with... */ #define MKDEV2BRD(m) (((m) & 0x00700000) >> 20) #define MKDEV2PORT(m) (((m) & 0x1f) | (((m) & 0x00010000) >> 11)) /* * Define some handy local macros... */ #ifndef MIN #define MIN(a,b) (((a) <= (b)) ? (a) : (b)) #endif /*****************************************************************************/ /* * Declare all those functions in this driver! First up is the set of * externally visible functions. */ int stliprobe(struct isa_device *idp); int stliattach(struct isa_device *idp); STATIC d_open_t stliopen; STATIC d_close_t stliclose; STATIC d_read_t stliread; STATIC d_write_t stliwrite; STATIC d_ioctl_t stliioctl; STATIC d_stop_t stlistop; #if VFREEBSD >= 220 STATIC d_devtotty_t stlidevtotty; #else struct tty *stlidevtotty(dev_t dev); #endif /* * Internal function prototypes. */ static stliport_t *stli_dev2port(dev_t dev); static int stli_chksharemem(void); static int stli_isaprobe(struct isa_device *idp); static int stli_eisaprobe(struct isa_device *idp); static int stli_mcaprobe(struct isa_device *idp); static int stli_brdinit(stlibrd_t *brdp); static int stli_brdattach(stlibrd_t *brdp); static int stli_initecp(stlibrd_t *brdp); static int stli_initonb(stlibrd_t *brdp); static int stli_initports(stlibrd_t *brdp); static int stli_startbrd(stlibrd_t *brdp); static void stli_poll(void *arg); static void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp); static int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp); static void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp); static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts); static long stli_mktiocm(unsigned long sigvalue); static void stli_rxprocess(stlibrd_t *brdp, stliport_t *portp); static void stli_flush(stliport_t *portp, int flag); static void stli_start(struct tty *tp); static int stli_param(struct tty *tp, struct termios *tiosp); static void stli_ttyoptim(stliport_t *portp, struct termios *tiosp); static void stli_dtrwakeup(void *arg); static int stli_initopen(stliport_t *portp); static int stli_shutdownclose(stliport_t *portp); static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait); static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait); static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp); static int stli_memrw(dev_t dev, struct uio *uiop, int flag); static int stli_memioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p); static int stli_getbrdstats(caddr_t data); static int stli_getportstats(stliport_t *portp, caddr_t data); static int stli_clrportstats(stliport_t *portp, caddr_t data); static stliport_t *stli_getport(int brdnr, int panelnr, int portnr); static void stli_ecpinit(stlibrd_t *brdp); static void stli_ecpenable(stlibrd_t *brdp); static void stli_ecpdisable(stlibrd_t *brdp); static void stli_ecpreset(stlibrd_t *brdp); static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_ecpintr(stlibrd_t *brdp); static void stli_ecpeiinit(stlibrd_t *brdp); static void stli_ecpeienable(stlibrd_t *brdp); static void stli_ecpeidisable(stlibrd_t *brdp); static void stli_ecpeireset(stlibrd_t *brdp); static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_ecpmcenable(stlibrd_t *brdp); static void stli_ecpmcdisable(stlibrd_t *brdp); static void stli_ecpmcreset(stlibrd_t *brdp); static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_onbinit(stlibrd_t *brdp); static void stli_onbenable(stlibrd_t *brdp); static void stli_onbdisable(stlibrd_t *brdp); static void stli_onbreset(stlibrd_t *brdp); static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_onbeinit(stlibrd_t *brdp); static void stli_onbeenable(stlibrd_t *brdp); static void stli_onbedisable(stlibrd_t *brdp); static void stli_onbereset(stlibrd_t *brdp); static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_bbyinit(stlibrd_t *brdp); static void stli_bbyreset(stlibrd_t *brdp); static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_stalinit(stlibrd_t *brdp); static void stli_stalreset(stlibrd_t *brdp); static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line); /*****************************************************************************/ /* * Declare the driver isa structure. */ struct isa_driver stlidriver = { stliprobe, stliattach, stli_drvname }; /*****************************************************************************/ #if VFREEBSD >= 220 /* * FreeBSD-2.2+ kernel linkage. */ #define CDEV_MAJOR 75 static struct cdevsw stli_cdevsw = { stliopen, stliclose, stliread, stliwrite, stliioctl, stlistop, noreset, stlidevtotty, ttselect, nommap, NULL, stli_drvname, NULL, -1 }; static stli_devsw_installed = 0; static void stli_drvinit(void *unused) { dev_t dev; if (! stli_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &stli_cdevsw, NULL); stli_devsw_installed = 1; } } SYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,stli_drvinit,NULL) #endif /*****************************************************************************/ static stlibrd_t *stli_brdalloc(void) { stlibrd_t *brdp; brdp = (stlibrd_t *) malloc(sizeof(stlibrd_t), M_TTYS, M_NOWAIT); if (brdp == (stlibrd_t *) NULL) { printf("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t)); return((stlibrd_t *) NULL); } bzero(brdp, sizeof(stlibrd_t)); return(brdp); } /*****************************************************************************/ /* * Find an available internal board number (unit number). The problem * is that the same unit numbers can be assigned to different class * boards - but we only want to maintain one setup board structures. */ static int stli_findfreeunit(void) { int i; for (i = 0; (i < STL_MAXBRDS); i++) if (stli_brds[i] == (stlibrd_t *) NULL) break; return((i >= STL_MAXBRDS) ? -1 : i); } /*****************************************************************************/ /* * Try and determine the ISA board type. Hopefully the board * configuration entry will help us out, using the flags field. * If not, we may ne be able to determine the board type... */ static int stli_isaprobe(struct isa_device *idp) { int btype; #if DEBUG printf("stli_isaprobe(idp=%x): unit=%d iobase=%x flags=%x\n", (int) idp, idp->id_unit, idp->id_iobase, idp->id_flags); #endif switch (idp->id_flags) { case BRD_STALLION: case BRD_BRUMBY4: case BRD_BRUMBY8: case BRD_BRUMBY16: case BRD_ONBOARD: case BRD_ONBOARD32: case BRD_ECP: btype = idp->id_flags; break; default: btype = 0; break; } return(btype); } /*****************************************************************************/ /* * Probe for an EISA board type. We should be able to read the EISA ID, * that will tell us if a board is present or not... */ static int stli_eisaprobe(struct isa_device *idp) { int btype, eid; #if DEBUG printf("stli_eisaprobe(idp=%x): unit=%d iobase=%x flags=%x\n", (int) idp, idp->id_unit, idp->id_iobase, idp->id_flags); #endif /* * Firstly check if this is an EISA system. Do this by probing for * the system board EISA ID. If this is not an EISA system then * don't bother going any further! */ outb(0xc80, 0xff); if (inb(0xc80) == 0xff) return(0); /* * Try and read the EISA ID from the board at specified address. * If one is present it will tell us the board type as well. */ outb((idp->id_iobase + 0xc80), 0xff); eid = inb(idp->id_iobase + 0xc80); eid |= inb(idp->id_iobase + 0xc81) << 8; if (eid != STL_EISAID) return(0); btype = 0; eid = inb(idp->id_iobase + 0xc82); if (eid == ECP_EISAID) btype = BRD_ECPE; else if (eid == ONB_EISAID) btype = BRD_ONBOARDE; outb((idp->id_iobase + 0xc84), 0x1); return(btype); } /*****************************************************************************/ /* * Probe for an MCA board type. Not really sure how to do this yet, * so for now just use the supplied flag specifier as board type... */ static int stli_mcaprobe(struct isa_device *idp) { int btype; #if DEBUG printf("stli_mcaprobe(idp=%x): unit=%d iobase=%x flags=%x\n", (int) idp, idp->id_unit, idp->id_iobase, idp->id_flags); #endif switch (idp->id_flags) { case BRD_ONBOARD2: case BRD_ONBOARD2_32: case BRD_ONBOARDRS: case BRD_ECHMC: case BRD_ECPMC: btype = idp->id_flags; break; default: btype = 0; break; } return(0); } /*****************************************************************************/ /* * Probe for a board. This is involved, since we need to enable the * shared memory region to see if the board is really there or not... */ int stliprobe(struct isa_device *idp) { stlibrd_t *brdp; int btype, bclass; #if DEBUG printf("stliprobe(idp=%x): unit=%d iobase=%x flags=%x\n", (int) idp, idp->id_unit, idp->id_iobase, idp->id_flags); #endif if (idp->id_unit > STL_MAXBRDS) return(0); /* * First up determine what bus type of board we might be dealing * with. It is easy to separate out the ISA from the EISA and MCA * boards, based on their IO addresses. We may not be able to tell * the EISA and MCA apart on IO address alone... */ bclass = 0; if ((idp->id_iobase > 0) && (idp->id_iobase < 0x400)) { bclass |= BRD_ISA; } else { /* ONboard2 range */ if ((idp->id_iobase >= 0x700) && (idp->id_iobase < 0x900)) bclass |= BRD_MCA; /* EC-MCA ranges */ if ((idp->id_iobase >= 0x7000) && (idp->id_iobase < 0x7400)) bclass |= BRD_MCA; if ((idp->id_iobase >= 0x8000) && (idp->id_iobase < 0xc000)) bclass |= BRD_MCA; /* EISA board range */ if ((idp->id_iobase & ~0xf000) == 0) bclass |= BRD_EISA; } if ((bclass == 0) || (idp->id_iobase == 0)) return(0); /* * Based on the board bus type, try and figure out what it might be... */ btype = 0; if (bclass & BRD_ISA) btype = stli_isaprobe(idp); if ((btype == 0) && (bclass & BRD_EISA)) btype = stli_eisaprobe(idp); if ((btype == 0) && (bclass & BRD_MCA)) btype = stli_mcaprobe(idp); if (btype == 0) return(0); /* * Go ahead and try probing for the shared memory region now. * This way we will really know if the board is here... */ if ((brdp = stli_brdalloc()) == (stlibrd_t *) NULL) return(0); brdp->brdnr = stli_findfreeunit(); brdp->brdtype = btype; brdp->unitid = idp->id_unit; brdp->iobase = idp->id_iobase; brdp->vaddr = idp->id_maddr; brdp->paddr = vtophys(idp->id_maddr); #if DEBUG printf("%s(%d): btype=%x unit=%d brd=%d io=%x mem=%x(%x)\n", __file__, __LINE__, btype, brdp->unitid, brdp->brdnr, brdp->iobase, brdp->paddr, brdp->vaddr); #endif stli_stliprobed[idp->id_unit] = brdp->brdnr; stli_brdinit(brdp); if ((brdp->state & BST_FOUND) == 0) { stli_brds[brdp->brdnr] = (stlibrd_t *) NULL; return(0); } stli_nrbrds++; return(1); } /*****************************************************************************/ /* * Allocate resources for and initialize a board. */ int stliattach(struct isa_device *idp) { stlibrd_t *brdp; int brdnr; #if DEBUG printf("stliattach(idp=%x): unit=%d iobase=%x\n", idp, idp->id_unit, idp->id_iobase); #endif brdnr = stli_stliprobed[idp->id_unit]; brdp = stli_brds[brdnr]; if (brdp == (stlibrd_t *) NULL) return(0); if (brdp->state & BST_FOUND) stli_brdattach(brdp); return(1); } /*****************************************************************************/ STATIC int stliopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp; stliport_t *portp; int error, callout, x; #if DEBUG printf("stliopen(dev=%x,flag=%x,mode=%x,p=%x)\n", (int) dev, flag, mode, (int) p); #endif /* * Firstly check if the supplied device number is a valid device. */ if (dev & STL_MEMDEV) return(0); portp = stli_dev2port(dev); if (portp == (stliport_t *) NULL) return(ENXIO); tp = &portp->tty; callout = minor(dev) & STL_CALLOUTDEV; error = 0; x = spltty(); stliopen_restart: /* * Wait here for the DTR drop timeout period to expire. */ while (portp->state & ST_DTRWAIT) { error = tsleep(&portp->dtrwait, (TTIPRI | PCATCH), "stlidtr", 0); if (error) goto stliopen_end; } /* * If the port is in its raw hardware initialization phase, then * hold up here 'till it is done. */ while (portp->state & (ST_INITIALIZING | ST_CLOSING)) { error = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (error) goto stliopen_end; } /* * We have a valid device, so now we check if it is already open. * If not then initialize the port hardware and set up the tty * struct as required. */ if ((tp->t_state & TS_ISOPEN) == 0) { tp->t_oproc = stli_start; tp->t_param = stli_param; tp->t_dev = dev; tp->t_termios = callout ? portp->initouttios : portp->initintios; stli_initopen(portp); wakeup(&portp->state); ttsetwater(tp); if ((portp->sigs & TIOCM_CD) || callout) (*linesw[tp->t_line].l_modem)(tp, 1); } else { if (callout) { if (portp->callout == 0) { error = EBUSY; goto stliopen_end; } } else { if (portp->callout != 0) { if (flag & O_NONBLOCK) { error = EBUSY; goto stliopen_end; } error = tsleep(&portp->callout, (TTIPRI | PCATCH), "stlicall", 0); if (error) goto stliopen_end; goto stliopen_restart; } } if ((tp->t_state & TS_XCLUDE) && (p->p_ucred->cr_uid != 0)) { error = EBUSY; goto stliopen_end; } } /* * If this port is not the callout device and we do not have carrier * then we need to sleep, waiting for it to be asserted. */ if (((tp->t_state & TS_CARR_ON) == 0) && !callout && ((tp->t_cflag & CLOCAL) == 0) && ((flag & O_NONBLOCK) == 0)) { portp->waitopens++; error = tsleep(TSA_CARR_ON(tp), (TTIPRI | PCATCH), "stlidcd",0); portp->waitopens--; if (error) goto stliopen_end; goto stliopen_restart; } /* * Open the line discipline. */ error = (*linesw[tp->t_line].l_open)(dev, tp); stli_ttyoptim(portp, &tp->t_termios); if ((tp->t_state & TS_ISOPEN) && callout) portp->callout = 1; /* * If for any reason we get to here and the port is not actually * open then close of the physical hardware - no point leaving it * active when the open failed... */ stliopen_end: splx(x); if (((tp->t_state & TS_ISOPEN) == 0) && (portp->waitopens == 0)) stli_shutdownclose(portp); return(error); } /*****************************************************************************/ STATIC int stliclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp; stliport_t *portp; int x; #if DEBUG printf("stliclose(dev=%x,flag=%x,mode=%x,p=%x)\n", dev, flag, mode, p); #endif if (dev & STL_MEMDEV) return(0); portp = stli_dev2port(dev); if (portp == (stliport_t *) NULL) return(ENXIO); tp = &portp->tty; x = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); stli_ttyoptim(portp, &tp->t_termios); stli_shutdownclose(portp); ttyclose(tp); splx(x); return(0); } /*****************************************************************************/ STATIC int stliread(dev_t dev, struct uio *uiop, int flag) { stliport_t *portp; #if DEBUG printf("stliread(dev=%x,uiop=%x,flag=%x)\n", dev, uiop, flag); #endif if (dev & STL_MEMDEV) return(stli_memrw(dev, uiop, flag)); portp = stli_dev2port(dev); if (portp == (stliport_t *) NULL) return(ENODEV); return((*linesw[portp->tty.t_line].l_read)(&portp->tty, uiop, flag)); } /*****************************************************************************/ #if VFREEBSD >= 220 STATIC void stlistop(struct tty *tp, int rw) { #if DEBUG printf("stlistop(tp=%x,rw=%x)\n", (int) tp, rw); #endif stli_flush((stliport_t *) tp, rw); } #else STATIC int stlistop(struct tty *tp, int rw) { #if DEBUG printf("stlistop(tp=%x,rw=%x)\n", (int) tp, rw); #endif stli_flush((stliport_t *) tp, rw); return(0); } #endif /*****************************************************************************/ STATIC struct tty *stlidevtotty(dev_t dev) { #if DEBUG printf("stlidevtotty(dev=%x)\n", dev); #endif return((struct tty *) stli_dev2port(dev)); } /*****************************************************************************/ STATIC int stliwrite(dev_t dev, struct uio *uiop, int flag) { stliport_t *portp; #if DEBUG printf("stliwrite(dev=%x,uiop=%x,flag=%x)\n", dev, uiop, flag); #endif if (dev & STL_MEMDEV) return(stli_memrw(dev, uiop, flag)); portp = stli_dev2port(dev); if (portp == (stliport_t *) NULL) return(ENODEV); return((*linesw[portp->tty.t_line].l_write)(&portp->tty, uiop, flag)); } /*****************************************************************************/ STATIC int stliioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { struct termios *newtios, *localtios; struct tty *tp; stlibrd_t *brdp; stliport_t *portp; long arg; int error, i, x; #if DEBUG printf("stliioctl(dev=%x,cmd=%x,data=%x,flag=%x,p=%x)\n", dev, cmd, data, flag, p); #endif dev = minor(dev); if (dev & STL_MEMDEV) return(stli_memioctl(dev, cmd, data, flag, p)); portp = stli_dev2port(dev); if (portp == (stliport_t *) NULL) return(ENODEV); if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL) return(ENODEV); tp = &portp->tty; error = 0; /* * First up handle ioctls on the control devices. */ if (dev & STL_CTRLDEV) { if ((dev & STL_CTRLDEV) == STL_CTRLINIT) localtios = (dev & STL_CALLOUTDEV) ? &portp->initouttios : &portp->initintios; else if ((dev & STL_CTRLDEV) == STL_CTRLLOCK) localtios = (dev & STL_CALLOUTDEV) ? &portp->lockouttios : &portp->lockintios; else return(ENODEV); switch (cmd) { case TIOCSETA: if ((error = suser(p->p_ucred, &p->p_acflag)) == 0) *localtios = *((struct termios *) data); break; case TIOCGETA: *((struct termios *) data) = *localtios; break; case TIOCGETD: *((int *) data) = TTYDISC; break; case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); break; default: error = ENOTTY; break; } return(error); } /* * Deal with 4.3 compatability issues if we have too... */ #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (1) { struct termios tios; int oldcmd; tios = tp->t_termios; oldcmd = cmd; if ((error = ttsetcompat(tp, &cmd, data, &tios))) return(error); if (cmd != oldcmd) data = (caddr_t) &tios; } #endif /* * Carry out some pre-cmd processing work first... * Hmmm, not so sure we want this, disable for now... */ if ((cmd == TIOCSETA) || (cmd == TIOCSETAW) || (cmd == TIOCSETAF)) { newtios = (struct termios *) data; localtios = (dev & STL_CALLOUTDEV) ? &portp->lockouttios : &portp->lockintios; newtios->c_iflag = (tp->t_iflag & localtios->c_iflag) | (newtios->c_iflag & ~localtios->c_iflag); newtios->c_oflag = (tp->t_oflag & localtios->c_oflag) | (newtios->c_oflag & ~localtios->c_oflag); newtios->c_cflag = (tp->t_cflag & localtios->c_cflag) | (newtios->c_cflag & ~localtios->c_cflag); newtios->c_lflag = (tp->t_lflag & localtios->c_lflag) | (newtios->c_lflag & ~localtios->c_lflag); for (i = 0; (i < NCCS); i++) { if (localtios->c_cc[i] != 0) newtios->c_cc[i] = tp->t_cc[i]; } if (localtios->c_ispeed != 0) newtios->c_ispeed = tp->t_ispeed; if (localtios->c_ospeed != 0) newtios->c_ospeed = tp->t_ospeed; } /* * Call the line discipline and the common command processing to * process this command (if they can). */ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return(error); x = spltty(); error = ttioctl(tp, cmd, data, flag); stli_ttyoptim(portp, &tp->t_termios); if (error >= 0) { splx(x); return(error); } error = 0; /* * Process local commands here. These are all commands that only we * can take care of (they all rely on actually doing something special * to the actual hardware). */ switch (cmd) { case TIOCSBRK: arg = BREAKON; error = stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(unsigned long), 0); break; case TIOCCBRK: arg = BREAKOFF; error = stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(unsigned long), 0); break; case TIOCSDTR: stli_mkasysigs(&portp->asig, 1, -1); error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); break; case TIOCCDTR: stli_mkasysigs(&portp->asig, 0, -1); error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); break; case TIOCMSET: i = *((int *) data); stli_mkasysigs(&portp->asig, ((i & TIOCM_DTR) ? 1 : 0), ((i & TIOCM_RTS) ? 1 : 0)); error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); break; case TIOCMBIS: i = *((int *) data); stli_mkasysigs(&portp->asig, ((i & TIOCM_DTR) ? 1 : -1), ((i & TIOCM_RTS) ? 1 : -1)); error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); break; case TIOCMBIC: i = *((int *) data); stli_mkasysigs(&portp->asig, ((i & TIOCM_DTR) ? 0 : -1), ((i & TIOCM_RTS) ? 0 : -1)); error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); break; case TIOCMGET: if ((error = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) break; portp->sigs = stli_mktiocm(portp->asig.sigvalue); *((int *) data) = (portp->sigs | TIOCM_LE); break; case TIOCMSDTRWAIT: if ((error = suser(p->p_ucred, &p->p_acflag)) == 0) portp->dtrwait = *((int *) data) * hz / 100; break; case TIOCMGDTRWAIT: *((int *) data) = portp->dtrwait * 100 / hz; break; case TIOCTIMESTAMP: portp->dotimestamp = 1; *((struct timeval *) data) = portp->timestamp; break; default: error = ENOTTY; break; } splx(x); return(error); } /*****************************************************************************/ /* * Convert the specified minor device number into a port struct * pointer. Return NULL if the device number is not a valid port. */ STATIC stliport_t *stli_dev2port(dev_t dev) { stlibrd_t *brdp; brdp = stli_brds[MKDEV2BRD(dev)]; if (brdp == (stlibrd_t *) NULL) return((stliport_t *) NULL); if ((brdp->state & BST_STARTED) == 0) return((stliport_t *) NULL); return(brdp->ports[MKDEV2PORT(dev)]); } /*****************************************************************************/ /* * Carry out first open operations on a port. This involves a number of * commands to be sent to the slave. We need to open the port, set the * notification events, set the initial port settings, get and set the * initial signal values. We sleep and wait in between each one. But * this still all happens pretty quickly. */ static int stli_initopen(stliport_t *portp) { stlibrd_t *brdp; asynotify_t nt; asyport_t aport; int rc; #if DEBUG printf("stli_initopen(portp=%x)\n", (int) portp); #endif if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL) return(ENXIO); if (portp->state & ST_INITIALIZED) return(0); portp->state |= ST_INITIALIZED; if ((rc = stli_rawopen(brdp, portp, 0, 1)) < 0) return(rc); bzero(&nt, sizeof(asynotify_t)); nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK); nt.signal = SG_DCD; if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, sizeof(asynotify_t), 0)) < 0) return(rc); stli_mkasyport(portp, &aport, &portp->tty.t_termios); if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0) return(rc); portp->state |= ST_GETSIGS; if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) return(rc); if (portp->state & ST_GETSIGS) { portp->sigs = stli_mktiocm(portp->asig.sigvalue); portp->state &= ~ST_GETSIGS; } stli_mkasysigs(&portp->asig, 1, 1); if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0) return(rc); return(0); } /*****************************************************************************/ /* * Shutdown the hardware of a port. */ static int stli_shutdownclose(stliport_t *portp) { stlibrd_t *brdp; struct tty *tp; #if DEBUG printf("stli_shutdownclose(portp=%x): brdnr=%d panelnr=%d portnr=%d\n", portp, portp->brdnr, portp->panelnr, portp->portnr); #endif if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL) return(ENXIO); tp = &portp->tty; stli_rawclose(brdp, portp, 0, 0); stli_flush(portp, (FWRITE | FREAD)); if (tp->t_cflag & HUPCL) { disable_intr(); stli_mkasysigs(&portp->asig, 0, 0); if (portp->state & ST_CMDING) { portp->state |= ST_DOSIGS; } else { stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); } enable_intr(); if (portp->dtrwait != 0) { portp->state |= ST_DTRWAIT; timeout(stli_dtrwakeup, portp, portp->dtrwait); } } portp->callout = 0; portp->state &= ~ST_INITIALIZED; wakeup(&portp->callout); wakeup(TSA_CARR_ON(tp)); return(0); } /*****************************************************************************/ /* * Clear the DTR waiting flag, and wake up any sleepers waiting for * DTR wait period to finish. */ static void stli_dtrwakeup(void *arg) { stliport_t *portp; portp = (stliport_t *) arg; portp->state &= ~ST_DTRWAIT; wakeup(&portp->dtrwait); } /*****************************************************************************/ /* * Send an open message to the slave. This will sleep waiting for the * acknowledgement, so must have user context. We need to co-ordinate * with close events here, since we don't want open and close events * to overlap. */ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait) { volatile cdkhdr_t *hdrp; volatile cdkctrl_t *cp; volatile unsigned char *bits; int rc; #if DEBUG printf("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait); #endif disable_intr(); /* * Slave is already closing this port. This can happen if a hangup * occurs on this port. So we must wait until it is complete. The * order of opens and closes may not be preserved across shared * memory, so we must wait until it is complete. */ while (portp->state & ST_CLOSING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { enable_intr(); return(rc); } } /* * Everything is ready now, so write the open message into shared * memory. Once the message is in set the service bits to say that * this port wants service. */ EBRDENABLE(brdp); cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; cp->openarg = arg; cp->open = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); if (wait == 0) { enable_intr(); return(0); } /* * Slave is in action, so now we must wait for the open acknowledgment * to come back. */ rc = 0; portp->state |= ST_OPENING; while (portp->state & ST_OPENING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { enable_intr(); return(rc); } } enable_intr(); if ((rc == 0) && (portp->rc != 0)) rc = EIO; return(rc); } /*****************************************************************************/ /* * Send a close message to the slave. Normally this will sleep waiting * for the acknowledgement, but if wait parameter is 0 it will not. If * wait is true then must have user context (to sleep). */ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait) { volatile cdkhdr_t *hdrp; volatile cdkctrl_t *cp; volatile unsigned char *bits; int rc; #if DEBUG printf("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait); #endif disable_intr(); /* * Slave is already closing this port. This can happen if a hangup * occurs on this port. */ if (wait) { while (portp->state & ST_CLOSING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { enable_intr(); return(rc); } } } /* * Write the close command into shared memory. */ EBRDENABLE(brdp); cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; cp->closearg = arg; cp->close = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); portp->state |= ST_CLOSING; if (wait == 0) { enable_intr(); return(0); } /* * Slave is in action, so now we must wait for the open acknowledgment * to come back. */ rc = 0; while (portp->state & ST_CLOSING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { enable_intr(); return(rc); } } enable_intr(); if ((rc == 0) && (portp->rc != 0)) rc = EIO; return(rc); } /*****************************************************************************/ /* * Send a command to the slave and wait for the response. This must * have user context (it sleeps). This routine is generic in that it * can send any type of command. Its purpose is to wait for that command * to complete (as opposed to initiating the command then returning). */ static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback) { int rc; #if DEBUG printf("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d," "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback); #endif disable_intr(); while (portp->state & ST_CMDING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { enable_intr(); return(rc); } } stli_sendcmd(brdp, portp, cmd, arg, size, copyback); while (portp->state & ST_CMDING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { enable_intr(); return(rc); } } enable_intr(); if (portp->rc != 0) return(EIO); return(0); } /*****************************************************************************/ /* * Start (or continue) the transfer of TX data on this port. If the * port is not currently busy then load up the interrupt ring queue * buffer and kick of the transmitter. If the port is running low on * TX data then refill the ring queue. This routine is also used to * activate input flow control! */ static void stli_start(struct tty *tp) { volatile cdkasy_t *ap; volatile cdkhdr_t *hdrp; volatile unsigned char *bits; unsigned char *shbuf; stliport_t *portp; stlibrd_t *brdp; unsigned int len, stlen, head, tail, size; int count, x; portp = (stliport_t *) tp; #if DEBUG printf("stli_start(tp=%x): brdnr=%d portnr=%d\n", (int) tp, portp->brdnr, portp->portnr); #endif x = spltty(); #if VFREEBSD == 205 /* * Check if the output cooked clist buffers are near empty, wake up * the line discipline to fill it up. */ if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(&tp->t_outq); } selwakeup(&tp->t_wsel); } #endif if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { splx(x); return; } /* * Copy data from the clists into the interrupt ring queue. This will * require at most 2 copys... What we do is calculate how many chars * can fit into the ring queue, and how many can fit in 1 copy. If after * the first copy there is still more room then do the second copy. */ if (tp->t_outq.c_cc != 0) { brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) { splx(x); return; } disable_intr(); EBRDENABLE(brdp); ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); head = (unsigned int) ap->txq.head; tail = (unsigned int) ap->txq.tail; if (tail != ((unsigned int) ap->txq.tail)) tail = (unsigned int) ap->txq.tail; size = portp->txsize; if (head >= tail) { len = size - (head - tail) - 1; stlen = size - head; } else { len = tail - head - 1; stlen = len; } count = 0; shbuf = (char *) EBRDGETMEMPTR(brdp, portp->txoffset); if (len > 0) { stlen = MIN(len, stlen); count = q_to_b(&tp->t_outq, (shbuf + head), stlen); len -= count; head += count; if (head >= size) { head = 0; if (len > 0) { stlen = q_to_b(&tp->t_outq, shbuf, len); head += stlen; count += stlen; } } } ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); ap->txq.head = head; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; *bits |= portp->portbit; portp->state |= ST_TXBUSY; tp->t_state |= TS_BUSY; EBRDDISABLE(brdp); enable_intr(); } #if VFREEBSD != 205 /* * Do any writer wakeups. */ ttwwakeup(tp); #endif splx(x); } /*****************************************************************************/ /* * Send a new port configuration to the slave. */ static int stli_param(struct tty *tp, struct termios *tiosp) { stlibrd_t *brdp; stliport_t *portp; asyport_t aport; int x, rc; portp = (stliport_t *) tp; if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL) return(ENXIO); x = spltty(); stli_mkasyport(portp, &aport, tiosp); /* can we sleep here? */ rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0); stli_ttyoptim(portp, tiosp); splx(x); return(rc); } /*****************************************************************************/ /* * Flush characters from the lower buffer. We may not have user context * so we cannot sleep waiting for it to complete. Also we need to check * if there is chars for this port in the TX cook buffer, and flush them * as well. */ static void stli_flush(stliport_t *portp, int flag) { stlibrd_t *brdp; unsigned long ftype; #if DEBUG printf("stli_flush(portp=%x,flag=%x)\n", (int) portp, flag); #endif if (portp == (stliport_t *) NULL) return; if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds)) return; brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) return; disable_intr(); if (portp->state & ST_CMDING) { portp->state |= (flag & FWRITE) ? ST_DOFLUSHTX : 0; portp->state |= (flag & FREAD) ? ST_DOFLUSHRX : 0; } else { ftype = (flag & FWRITE) ? FLUSHTX : 0; ftype |= (flag & FREAD) ? FLUSHRX : 0; portp->state &= ~(ST_DOFLUSHTX | ST_DOFLUSHRX); stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(unsigned long), 0); } if ((flag & FREAD) && (stli_rxtmpport == portp)) stli_rxtmplen = 0; enable_intr(); } /*****************************************************************************/ /* * Generic send command routine. This will send a message to the slave, * of the specified type with the specified argument. Must be very * carefull of data that will be copied out from shared memory - * containing command results. The command completion is all done from * a poll routine that does not have user coontext. Therefore you cannot * copy back directly into user space, or to the kernel stack of a * process. This routine does not sleep, so can be called from anywhere, * and must be called with interrupt locks set. */ static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback) { volatile cdkhdr_t *hdrp; volatile cdkctrl_t *cp; volatile unsigned char *bits; #if DEBUG printf("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d," "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback); #endif if (portp->state & ST_CMDING) { printf("STALLION: command already busy, cmd=%x!\n", (int) cmd); return; } EBRDENABLE(brdp); cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; if (size > 0) { bcopy(arg, (void *) &(cp->args[0]), size); if (copyback) { portp->argp = arg; portp->argsize = size; } } cp->status = 0; cp->cmd = cmd; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; *bits |= portp->portbit; portp->state |= ST_CMDING; EBRDDISABLE(brdp); } /*****************************************************************************/ /* * Read data from shared memory. This assumes that the shared memory * is enabled and that interrupts are off. Basically we just empty out * the shared memory buffer into the tty buffer. Must be carefull to * handle the case where we fill up the tty buffer, but still have * more chars to unload. */ static void stli_rxprocess(stlibrd_t *brdp, stliport_t *portp) { volatile cdkasyrq_t *rp; volatile char *shbuf; struct tty *tp; unsigned int head, tail, size; unsigned int len, stlen, i; int ch; #if DEBUG printf("stli_rxprocess(brdp=%x,portp=%d)\n", (int) brdp, (int) portp); #endif tp = &portp->tty; if ((tp->t_state & TS_ISOPEN) == 0) { stli_flush(portp, FREAD); return; } if (tp->t_state & TS_TBLOCK) return; rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq; head = (unsigned int) rp->head; if (head != ((unsigned int) rp->head)) head = (unsigned int) rp->head; tail = (unsigned int) rp->tail; size = portp->rxsize; if (head >= tail) { len = head - tail; stlen = len; } else { len = size - (tail - head); stlen = size - tail; } if (len == 0) return; shbuf = (volatile char *) EBRDGETMEMPTR(brdp, portp->rxoffset); /* * If we can bypass normal LD processing then just copy direct * from board shared memory into the tty buffers. */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (((tp->t_rawq.c_cc + len) >= TTYHOG) && ((tp->t_cflag & CRTS_IFLOW) || (tp->t_iflag & IXOFF)) && ((tp->t_state & TS_TBLOCK) == 0)) { ch = TTYHOG - tp->t_rawq.c_cc - 1; len = (ch > 0) ? ch : 0; stlen = MIN(stlen, len); tp->t_state |= TS_TBLOCK; } i = b_to_q((char *) (shbuf + tail), stlen, &tp->t_rawq); tail += stlen; len -= stlen; if (tail >= size) { tail = 0; i += b_to_q((char *) shbuf, len, &tp->t_rawq); tail += len; } portp->rxlost += i; ttwakeup(tp); rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq; rp->tail = tail; } else { /* * Copy the data from board shared memory into a local * memory buffer. Then feed them from here into the LD. * We don't want to go into board shared memory one char * at a time, it is too slow... */ if (len > TTYHOG) { len = TTYHOG - 1; stlen = min(len, stlen); } stli_rxtmpport = portp; stli_rxtmplen = len; bcopy((char *) (shbuf + tail), &stli_rxtmpbuf[0], stlen); len -= stlen; if (len > 0) bcopy((char *) shbuf, &stli_rxtmpbuf[stlen], len); for (i = 0; (i < stli_rxtmplen); i++) { ch = (unsigned char) stli_rxtmpbuf[i]; (*linesw[tp->t_line].l_rint)(ch, tp); } EBRDENABLE(brdp); rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq; if (stli_rxtmplen == 0) { head = (unsigned int) rp->head; if (head != ((unsigned int) rp->head)) head = (unsigned int) rp->head; tail = head; } else { tail += i; if (tail >= size) tail -= size; } rp->tail = tail; stli_rxtmpport = (stliport_t *) NULL; stli_rxtmplen = 0; } portp->state |= ST_RXING; } /*****************************************************************************/ /* * Set up and carry out any delayed commands. There is only a small set * of slave commands that can be done "off-level". So it is not too * difficult to deal with them as a special case here. */ static inline void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp) { int cmd; if (portp->state & ST_DOSIGS) { if ((portp->state & ST_DOFLUSHTX) && (portp->state & ST_DOFLUSHRX)) cmd = A_SETSIGNALSF; else if (portp->state & ST_DOFLUSHTX) cmd = A_SETSIGNALSFTX; else if (portp->state & ST_DOFLUSHRX) cmd = A_SETSIGNALSFRX; else cmd = A_SETSIGNALS; portp->state &= ~(ST_DOFLUSHTX | ST_DOFLUSHRX | ST_DOSIGS); bcopy((void *) &portp->asig, (void *) &(cp->args[0]), sizeof(asysigs_t)); cp->status = 0; cp->cmd = cmd; portp->state |= ST_CMDING; } else if ((portp->state & ST_DOFLUSHTX) || (portp->state & ST_DOFLUSHRX)) { cmd = ((portp->state & ST_DOFLUSHTX) ? FLUSHTX : 0); cmd |= ((portp->state & ST_DOFLUSHRX) ? FLUSHRX : 0); portp->state &= ~(ST_DOFLUSHTX | ST_DOFLUSHRX); bcopy((void *) &cmd, (void *) &(cp->args[0]), sizeof(int)); cp->status = 0; cp->cmd = A_FLUSH; portp->state |= ST_CMDING; } } /*****************************************************************************/ /* * Host command service checking. This handles commands or messages * coming from the slave to the host. Must have board shared memory * enabled and interrupts off when called. Notice that by servicing the * read data last we don't need to change the shared memory pointer * during processing (which is a slow IO operation). * Return value indicates if this port is still awaiting actions from * the slave (like open, command, or even TX data being sent). If 0 * then port is still busy, otherwise the port request bit flag is * returned. */ static inline int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp) { volatile cdkasy_t *ap; volatile cdkctrl_t *cp; asynotify_t nt; unsigned long oldsigs; unsigned int head, tail; int rc, donerx; #if DEBUG printf("stli_hostcmd(brdp=%x,portp=%x)\n", (int) brdp, (int) portp); #endif ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); cp = &ap->ctrl; /* * Check if we are waiting for an open completion message. */ if (portp->state & ST_OPENING) { rc = (int) cp->openarg; if ((cp->open == 0) && (rc != 0)) { if (rc > 0) rc--; cp->openarg = 0; portp->rc = rc; portp->state &= ~ST_OPENING; wakeup(&portp->state); } } /* * Check if we are waiting for a close completion message. */ if (portp->state & ST_CLOSING) { rc = (int) cp->closearg; if ((cp->close == 0) && (rc != 0)) { if (rc > 0) rc--; cp->closearg = 0; portp->rc = rc; portp->state &= ~ST_CLOSING; wakeup(&portp->state); } } /* * Check if we are waiting for a command completion message. We may * need to copy out the command results associated with this command. */ if (portp->state & ST_CMDING) { rc = cp->status; if ((cp->cmd == 0) && (rc != 0)) { if (rc > 0) rc--; if (portp->argp != (void *) NULL) { bcopy((void *) &(cp->args[0]), portp->argp, portp->argsize); portp->argp = (void *) NULL; } cp->status = 0; portp->rc = rc; portp->state &= ~ST_CMDING; stli_dodelaycmd(portp, cp); wakeup(&portp->state); } } /* * Check for any notification messages ready. This includes lots of * different types of events - RX chars ready, RX break received, * TX data low or empty in the slave, modem signals changed state. * Must be extremely carefull if we call to the LD, it may call * other routines of ours that will disable the memory... * Something else we need to be carefull of is race conditions on * marking the TX as empty... */ donerx = 0; if (ap->notify) { struct tty *tp; nt = ap->changed; ap->notify = 0; tp = &portp->tty; if (nt.signal & SG_DCD) { oldsigs = portp->sigs; portp->sigs = stli_mktiocm(nt.sigvalue); portp->state &= ~ST_GETSIGS; (*linesw[tp->t_line].l_modem)(tp, (portp->sigs & TIOCM_CD)); EBRDENABLE(brdp); } if (nt.data & DT_RXBUSY) { donerx++; stli_rxprocess(brdp, portp); } if (nt.data & DT_RXBREAK) { (*linesw[tp->t_line].l_rint)(TTY_BI, tp); EBRDENABLE(brdp); } if (nt.data & DT_TXEMPTY) { ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); head = (unsigned int) ap->txq.head; tail = (unsigned int) ap->txq.tail; if (tail != ((unsigned int) ap->txq.tail)) tail = (unsigned int) ap->txq.tail; head = (head >= tail) ? (head - tail) : portp->txsize - (tail - head); if (head == 0) { portp->state &= ~ST_TXBUSY; tp->t_state &= ~TS_BUSY; } } if (nt.data & (DT_TXEMPTY | DT_TXLOW)) { (*linesw[tp->t_line].l_start)(tp); EBRDENABLE(brdp); } } /* * It might seem odd that we are checking for more RX chars here. * But, we need to handle the case where the tty buffer was previously * filled, but we had more characters to pass up. The slave will not * send any more RX notify messages until the RX buffer has been emptied. * But it will leave the service bits on (since the buffer is not empty). * So from here we can try to process more RX chars. */ if ((!donerx) && (portp->state & ST_RXING)) { portp->state &= ~ST_RXING; stli_rxprocess(brdp, portp); } return((portp->state & (ST_OPENING | ST_CLOSING | ST_CMDING | ST_TXBUSY | ST_RXING)) ? 0 : 1); } /*****************************************************************************/ /* * Service all ports on a particular board. Assumes that the boards * shared memory is enabled, and that the page pointer is pointed * at the cdk header structure. */ static inline void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp) { stliport_t *portp; unsigned char hostbits[(STL_MAXCHANS / 8) + 1]; unsigned char slavebits[(STL_MAXCHANS / 8) + 1]; unsigned char *slavep; int bitpos, bitat, bitsize; int channr, nrdevs, slavebitchange; bitsize = brdp->bitsize; nrdevs = brdp->nrdevs; /* * Check if slave wants any service. Basically we try to do as * little work as possible here. There are 2 levels of service * bits. So if there is nothing to do we bail early. We check * 8 service bits at a time in the inner loop, so we can bypass * the lot if none of them want service. */ bcopy((((unsigned char *) hdrp) + brdp->hostoffset), &hostbits[0], bitsize); bzero(&slavebits[0], bitsize); slavebitchange = 0; for (bitpos = 0; (bitpos < bitsize); bitpos++) { if (hostbits[bitpos] == 0) continue; channr = bitpos * 8; bitat = 0x1; for (; (channr < nrdevs); channr++, bitat <<=1) { if (hostbits[bitpos] & bitat) { portp = brdp->ports[(channr - 1)]; if (stli_hostcmd(brdp, portp)) { slavebitchange++; slavebits[bitpos] |= bitat; } } } } /* * If any of the ports are no longer busy then update them in the * slave request bits. We need to do this after, since a host port * service may initiate more slave requests... */ if (slavebitchange) { hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); slavep = ((unsigned char *) hdrp) + brdp->slaveoffset; for (bitpos = 0; (bitpos < bitsize); bitpos++) { if (slavebits[bitpos]) slavep[bitpos] &= ~slavebits[bitpos]; } } } /*****************************************************************************/ /* * Driver poll routine. This routine polls the boards in use and passes * messages back up to host when neccesary. This is actually very * CPU efficient, since we will always have the kernel poll clock, it * adds only a few cycles when idle (since board service can be * determined very easily), but when loaded generates no interrupts * (with their expensive associated context change). */ static void stli_poll(void *arg) { volatile cdkhdr_t *hdrp; stlibrd_t *brdp; int brdnr; disable_intr(); /* * Check each board and do any servicing required. */ for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) { brdp = stli_brds[brdnr]; if (brdp == (stlibrd_t *) NULL) continue; if ((brdp->state & BST_STARTED) == 0) continue; EBRDENABLE(brdp); hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); if (hdrp->hostreq) stli_brdpoll(brdp, hdrp); EBRDDISABLE(brdp); } enable_intr(); timeout(stli_poll, 0, 1); } /*****************************************************************************/ /* * Translate the termios settings into the port setting structure of * the slave. */ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp) { #if DEBUG printf("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", (int) portp, (int) pp, (int) tiosp); #endif bzero(pp, sizeof(asyport_t)); /* * Start of by setting the baud, char size, parity and stop bit info. */ if (tiosp->c_ispeed == 0) tiosp->c_ispeed = tiosp->c_ospeed; if ((tiosp->c_ospeed < 0) || (tiosp->c_ospeed > STL_MAXBAUD)) tiosp->c_ospeed = STL_MAXBAUD; pp->baudout = tiosp->c_ospeed; pp->baudin = pp->baudout; switch (tiosp->c_cflag & CSIZE) { case CS5: pp->csize = 5; break; case CS6: pp->csize = 6; break; case CS7: pp->csize = 7; break; default: pp->csize = 8; break; } if (tiosp->c_cflag & CSTOPB) pp->stopbs = PT_STOP2; else pp->stopbs = PT_STOP1; if (tiosp->c_cflag & PARENB) { if (tiosp->c_cflag & PARODD) pp->parity = PT_ODDPARITY; else pp->parity = PT_EVENPARITY; } else { pp->parity = PT_NOPARITY; } if (tiosp->c_iflag & ISTRIP) pp->iflag |= FI_ISTRIP; /* * Set up any flow control options enabled. */ if (tiosp->c_iflag & IXON) { pp->flow |= F_IXON; if (tiosp->c_iflag & IXANY) pp->flow |= F_IXANY; } if (tiosp->c_iflag & IXOFF) pp->flow |= F_IXOFF; if (tiosp->c_cflag & CCTS_OFLOW) pp->flow |= F_CTSFLOW; if (tiosp->c_cflag & CRTS_IFLOW) pp->flow |= F_RTSFLOW; pp->startin = tiosp->c_cc[VSTART]; pp->stopin = tiosp->c_cc[VSTOP]; pp->startout = tiosp->c_cc[VSTART]; pp->stopout = tiosp->c_cc[VSTOP]; /* * Set up the RX char marking mask with those RX error types we must * catch. We can get the slave to help us out a little here, it will * ignore parity errors and breaks for us, and mark parity errors in * the data stream. */ if (tiosp->c_iflag & IGNPAR) pp->iflag |= FI_IGNRXERRS; if (tiosp->c_iflag & IGNBRK) pp->iflag |= FI_IGNBREAK; if (tiosp->c_iflag & (INPCK | PARMRK)) pp->iflag |= FI_1MARKRXERRS; /* * Transfer any persistent flags into the asyport structure. */ pp->pflag = portp->pflag; } /*****************************************************************************/ /* * Construct a slave signals structure for setting the DTR and RTS * signals as specified. */ static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts) { #if DEBUG printf("stli_mkasysigs(sp=%x,dtr=%d,rts=%d)\n", (int) sp, dtr, rts); #endif bzero(sp, sizeof(asysigs_t)); if (dtr >= 0) { sp->signal |= SG_DTR; sp->sigvalue |= ((dtr > 0) ? SG_DTR : 0); } if (rts >= 0) { sp->signal |= SG_RTS; sp->sigvalue |= ((rts > 0) ? SG_RTS : 0); } } /*****************************************************************************/ /* * Convert the signals returned from the slave into a local TIOCM type * signals value. We keep them localy in TIOCM format. */ static long stli_mktiocm(unsigned long sigvalue) { long tiocm; #if DEBUG printf("stli_mktiocm(sigvalue=%x)\n", (int) sigvalue); #endif tiocm = 0; tiocm |= ((sigvalue & SG_DCD) ? TIOCM_CD : 0); tiocm |= ((sigvalue & SG_CTS) ? TIOCM_CTS : 0); tiocm |= ((sigvalue & SG_RI) ? TIOCM_RI : 0); tiocm |= ((sigvalue & SG_DSR) ? TIOCM_DSR : 0); tiocm |= ((sigvalue & SG_DTR) ? TIOCM_DTR : 0); tiocm |= ((sigvalue & SG_RTS) ? TIOCM_RTS : 0); return(tiocm); } /*****************************************************************************/ /* * Enable l_rint processing bypass mode if tty modes allow it. */ static void stli_ttyoptim(stliport_t *portp, struct termios *tiosp) { struct tty *tp; tp = &portp->tty; if (((tiosp->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR)) == 0) && (((tiosp->c_iflag & BRKINT) == 0) || (tiosp->c_iflag & IGNBRK)) && (((tiosp->c_iflag & PARMRK) == 0) || ((tiosp->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))) && ((tiosp->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) ==0) && (linesw[tp->t_line].l_rint == ttyinput)) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; if (tp->t_line == SLIPDISC) portp->hotchar = 0xc0; else if (tp->t_line == PPPDISC) portp->hotchar = 0x7e; else portp->hotchar = 0; } /*****************************************************************************/ /* * All panels and ports actually attached have been worked out. All * we need to do here is set up the appropriate per port data structures. */ static int stli_initports(stlibrd_t *brdp) { stliport_t *portp; int i, panelnr, panelport; #if DEBUG printf("stli_initports(brdp=%x)\n", (int) brdp); #endif for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) { portp = (stliport_t *) malloc(sizeof(stliport_t), M_TTYS, M_NOWAIT); if (portp == (stliport_t *) NULL) { printf("STALLION: failed to allocate port structure\n"); continue; } bzero(portp, sizeof(stliport_t)); portp->portnr = i; portp->brdnr = brdp->brdnr; portp->panelnr = panelnr; portp->initintios.c_ispeed = STL_DEFSPEED; portp->initintios.c_ospeed = STL_DEFSPEED; portp->initintios.c_cflag = STL_DEFCFLAG; portp->initintios.c_iflag = 0; portp->initintios.c_oflag = 0; portp->initintios.c_lflag = 0; bcopy(&ttydefchars[0], &portp->initintios.c_cc[0], sizeof(portp->initintios.c_cc)); portp->initouttios = portp->initintios; portp->dtrwait = 3 * hz; panelport++; if (panelport >= brdp->panels[panelnr]) { panelport = 0; panelnr++; } brdp->ports[i] = portp; } return(0); } /*****************************************************************************/ /* * All the following routines are board specific hardware operations. */ static void stli_ecpinit(stlibrd_t *brdp) { unsigned long memconf; #if DEBUG printf("stli_ecpinit(brdp=%d)\n", (int) brdp); #endif outb((brdp->iobase + ECP_ATCONFR), ECP_ATSTOP); DELAY(10); outb((brdp->iobase + ECP_ATCONFR), ECP_ATDISABLE); DELAY(100); memconf = (brdp->paddr & ECP_ATADDRMASK) >> ECP_ATADDRSHFT; outb((brdp->iobase + ECP_ATMEMAR), memconf); } /*****************************************************************************/ static void stli_ecpenable(stlibrd_t *brdp) { #if DEBUG printf("stli_ecpenable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ECP_ATCONFR), ECP_ATENABLE); } /*****************************************************************************/ static void stli_ecpdisable(stlibrd_t *brdp) { #if DEBUG printf("stli_ecpdisable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ECP_ATCONFR), ECP_ATDISABLE); } /*****************************************************************************/ static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; unsigned char val; #if DEBUG printf("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { - ptr = brdp->vaddr + (offset % ECP_ATPAGESIZE); + ptr = (char *) brdp->vaddr + (offset % ECP_ATPAGESIZE); val = (unsigned char) (offset / ECP_ATPAGESIZE); } outb((brdp->iobase + ECP_ATMEMPR), val); return(ptr); } /*****************************************************************************/ static void stli_ecpreset(stlibrd_t *brdp) { #if DEBUG printf("stli_ecpreset(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ECP_ATCONFR), ECP_ATSTOP); DELAY(10); outb((brdp->iobase + ECP_ATCONFR), ECP_ATDISABLE); DELAY(500); } /*****************************************************************************/ static void stli_ecpintr(stlibrd_t *brdp) { #if DEBUG printf("stli_ecpintr(brdp=%x)\n", (int) brdp); #endif outb(brdp->iobase, 0x1); } /*****************************************************************************/ /* * The following set of functions act on ECP EISA boards. */ static void stli_ecpeiinit(stlibrd_t *brdp) { unsigned long memconf; #if DEBUG printf("stli_ecpeiinit(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ECP_EIBRDENAB), 0x1); outb((brdp->iobase + ECP_EICONFR), ECP_EISTOP); DELAY(10); outb((brdp->iobase + ECP_EICONFR), ECP_EIDISABLE); DELAY(500); memconf = (brdp->paddr & ECP_EIADDRMASKL) >> ECP_EIADDRSHFTL; outb((brdp->iobase + ECP_EIMEMARL), memconf); memconf = (brdp->paddr & ECP_EIADDRMASKH) >> ECP_EIADDRSHFTH; outb((brdp->iobase + ECP_EIMEMARH), memconf); } /*****************************************************************************/ static void stli_ecpeienable(stlibrd_t *brdp) { outb((brdp->iobase + ECP_EICONFR), ECP_EIENABLE); } /*****************************************************************************/ static void stli_ecpeidisable(stlibrd_t *brdp) { outb((brdp->iobase + ECP_EICONFR), ECP_EIDISABLE); } /*****************************************************************************/ static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; unsigned char val; #if DEBUG printf("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { - ptr = brdp->vaddr + (offset % ECP_EIPAGESIZE); + ptr = (char *) brdp->vaddr + (offset % ECP_EIPAGESIZE); if (offset < ECP_EIPAGESIZE) val = ECP_EIENABLE; else val = ECP_EIENABLE | 0x40; } outb((brdp->iobase + ECP_EICONFR), val); return(ptr); } /*****************************************************************************/ static void stli_ecpeireset(stlibrd_t *brdp) { outb((brdp->iobase + ECP_EICONFR), ECP_EISTOP); DELAY(10); outb((brdp->iobase + ECP_EICONFR), ECP_EIDISABLE); DELAY(500); } /*****************************************************************************/ /* * The following set of functions act on ECP MCA boards. */ static void stli_ecpmcenable(stlibrd_t *brdp) { outb((brdp->iobase + ECP_MCCONFR), ECP_MCENABLE); } /*****************************************************************************/ static void stli_ecpmcdisable(stlibrd_t *brdp) { outb((brdp->iobase + ECP_MCCONFR), ECP_MCDISABLE); } /*****************************************************************************/ static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; unsigned char val; if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { - ptr = brdp->vaddr + (offset % ECP_MCPAGESIZE); + ptr = (char *) brdp->vaddr + (offset % ECP_MCPAGESIZE); val = ((unsigned char) (offset / ECP_MCPAGESIZE)) | ECP_MCENABLE; } outb((brdp->iobase + ECP_MCCONFR), val); return(ptr); } /*****************************************************************************/ static void stli_ecpmcreset(stlibrd_t *brdp) { outb((brdp->iobase + ECP_MCCONFR), ECP_MCSTOP); DELAY(10); outb((brdp->iobase + ECP_MCCONFR), ECP_MCDISABLE); DELAY(500); } /*****************************************************************************/ /* * The following routines act on ONboards. */ static void stli_onbinit(stlibrd_t *brdp) { unsigned long memconf; int i; #if DEBUG printf("stli_onbinit(brdp=%d)\n", (int) brdp); #endif outb((brdp->iobase + ONB_ATCONFR), ONB_ATSTOP); DELAY(10); outb((brdp->iobase + ONB_ATCONFR), ONB_ATDISABLE); for (i = 0; (i < 1000); i++) DELAY(1000); memconf = (brdp->paddr & ONB_ATADDRMASK) >> ONB_ATADDRSHFT; outb((brdp->iobase + ONB_ATMEMAR), memconf); outb(brdp->iobase, 0x1); DELAY(1000); } /*****************************************************************************/ static void stli_onbenable(stlibrd_t *brdp) { #if DEBUG printf("stli_onbenable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_ATCONFR), (ONB_ATENABLE | brdp->confbits)); } /*****************************************************************************/ static void stli_onbdisable(stlibrd_t *brdp) { #if DEBUG printf("stli_onbdisable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_ATCONFR), (ONB_ATDISABLE | brdp->confbits)); } /*****************************************************************************/ static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; #if DEBUG printf("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; } else { - ptr = brdp->vaddr + (offset % ONB_ATPAGESIZE); + ptr = (char *) brdp->vaddr + (offset % ONB_ATPAGESIZE); } return(ptr); } /*****************************************************************************/ static void stli_onbreset(stlibrd_t *brdp) { int i; #if DEBUG printf("stli_onbreset(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_ATCONFR), ONB_ATSTOP); DELAY(10); outb((brdp->iobase + ONB_ATCONFR), ONB_ATDISABLE); for (i = 0; (i < 1000); i++) DELAY(1000); } /*****************************************************************************/ /* * The following routines act on ONboard EISA. */ static void stli_onbeinit(stlibrd_t *brdp) { unsigned long memconf; int i; #if DEBUG printf("stli_onbeinit(brdp=%d)\n", (int) brdp); #endif outb((brdp->iobase + ONB_EIBRDENAB), 0x1); outb((brdp->iobase + ONB_EICONFR), ONB_EISTOP); DELAY(10); outb((brdp->iobase + ONB_EICONFR), ONB_EIDISABLE); for (i = 0; (i < 1000); i++) DELAY(1000); memconf = (brdp->paddr & ONB_EIADDRMASKL) >> ONB_EIADDRSHFTL; outb((brdp->iobase + ONB_EIMEMARL), memconf); memconf = (brdp->paddr & ONB_EIADDRMASKH) >> ONB_EIADDRSHFTH; outb((brdp->iobase + ONB_EIMEMARH), memconf); outb(brdp->iobase, 0x1); DELAY(1000); } /*****************************************************************************/ static void stli_onbeenable(stlibrd_t *brdp) { #if DEBUG printf("stli_onbeenable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_EICONFR), ONB_EIENABLE); } /*****************************************************************************/ static void stli_onbedisable(stlibrd_t *brdp) { #if DEBUG printf("stli_onbedisable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_EICONFR), ONB_EIDISABLE); } /*****************************************************************************/ static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; unsigned char val; #if DEBUG printf("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { - ptr = brdp->vaddr + (offset % ONB_EIPAGESIZE); + ptr = (char *) brdp->vaddr + (offset % ONB_EIPAGESIZE); if (offset < ONB_EIPAGESIZE) val = ONB_EIENABLE; else val = ONB_EIENABLE | 0x40; } outb((brdp->iobase + ONB_EICONFR), val); return(ptr); } /*****************************************************************************/ static void stli_onbereset(stlibrd_t *brdp) { int i; #if DEBUG printf("stli_onbereset(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_EICONFR), ONB_EISTOP); DELAY(10); outb((brdp->iobase + ONB_EICONFR), ONB_EIDISABLE); for (i = 0; (i < 1000); i++) DELAY(1000); } /*****************************************************************************/ /* * The following routines act on Brumby boards. */ static void stli_bbyinit(stlibrd_t *brdp) { int i; #if DEBUG printf("stli_bbyinit(brdp=%d)\n", (int) brdp); #endif outb((brdp->iobase + BBY_ATCONFR), BBY_ATSTOP); DELAY(10); outb((brdp->iobase + BBY_ATCONFR), 0); for (i = 0; (i < 1000); i++) DELAY(1000); outb(brdp->iobase, 0x1); DELAY(1000); } /*****************************************************************************/ static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; unsigned char val; #if DEBUG printf("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { - ptr = brdp->vaddr + (offset % BBY_PAGESIZE); + ptr = (char *) brdp->vaddr + (offset % BBY_PAGESIZE); val = (unsigned char) (offset / BBY_PAGESIZE); } outb((brdp->iobase + BBY_ATCONFR), val); return(ptr); } /*****************************************************************************/ static void stli_bbyreset(stlibrd_t *brdp) { int i; #if DEBUG printf("stli_bbyreset(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + BBY_ATCONFR), BBY_ATSTOP); DELAY(10); outb((brdp->iobase + BBY_ATCONFR), 0); for (i = 0; (i < 1000); i++) DELAY(1000); } /*****************************************************************************/ /* * The following routines act on original old Stallion boards. */ static void stli_stalinit(stlibrd_t *brdp) { int i; #if DEBUG printf("stli_stalinit(brdp=%d)\n", (int) brdp); #endif outb(brdp->iobase, 0x1); for (i = 0; (i < 1000); i++) DELAY(1000); } /*****************************************************************************/ static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; #if DEBUG printf("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; } else { - ptr = brdp->vaddr + (offset % STAL_PAGESIZE); + ptr = (char *) brdp->vaddr + (offset % STAL_PAGESIZE); } return(ptr); } /*****************************************************************************/ static void stli_stalreset(stlibrd_t *brdp) { volatile unsigned long *vecp; int i; #if DEBUG printf("stli_stalreset(brdp=%x)\n", (int) brdp); #endif - vecp = (volatile unsigned long *) (brdp->vaddr + 0x30); + vecp = (volatile unsigned long *) ((char *) brdp->vaddr + 0x30); *vecp = 0xffff0000; outb(brdp->iobase, 0); for (i = 0; (i < 1000); i++) DELAY(1000); } /*****************************************************************************/ /* * Try to find an ECP board and initialize it. This handles only ECP * board types. */ static int stli_initecp(stlibrd_t *brdp) { cdkecpsig_t sig; cdkecpsig_t *sigsp; unsigned int status, nxtid; int panelnr; #if DEBUG printf("stli_initecp(brdp=%x)\n", (int) brdp); #endif /* * Do a basic sanity check on the IO and memory addresses. */ if ((brdp->iobase == 0) || (brdp->paddr == 0)) return(EINVAL); /* * Based on the specific board type setup the common vars to access * and enable shared memory. Set all board specific information now * as well. */ switch (brdp->brdtype) { case BRD_ECP: brdp->memsize = ECP_MEMSIZE; brdp->pagesize = ECP_ATPAGESIZE; brdp->init = stli_ecpinit; brdp->enable = stli_ecpenable; brdp->reenable = stli_ecpenable; brdp->disable = stli_ecpdisable; brdp->getmemptr = stli_ecpgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpreset; break; case BRD_ECPE: brdp->memsize = ECP_MEMSIZE; brdp->pagesize = ECP_EIPAGESIZE; brdp->init = stli_ecpeiinit; brdp->enable = stli_ecpeienable; brdp->reenable = stli_ecpeienable; brdp->disable = stli_ecpeidisable; brdp->getmemptr = stli_ecpeigetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpeireset; break; case BRD_ECPMC: brdp->memsize = ECP_MEMSIZE; brdp->pagesize = ECP_MCPAGESIZE; brdp->init = NULL; brdp->enable = stli_ecpmcenable; brdp->reenable = stli_ecpmcenable; brdp->disable = stli_ecpmcdisable; brdp->getmemptr = stli_ecpmcgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpmcreset; break; default: return(EINVAL); } /* * The per-board operations structure is all setup, so now lets go * and get the board operational. Firstly initialize board configuration * registers. */ EBRDINIT(brdp); /* * Now that all specific code is set up, enable the shared memory and * look for the a signature area that will tell us exactly what board * this is, and what it is connected to it. */ EBRDENABLE(brdp); sigsp = (cdkecpsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR); bcopy(sigsp, &sig, sizeof(cdkecpsig_t)); EBRDDISABLE(brdp); #if 0 printf("%s(%d): sig-> magic=%x rom=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n", __file__, __LINE__, (int) sig.magic, sig.romver, sig.panelid[0], (int) sig.panelid[1], (int) sig.panelid[2], (int) sig.panelid[3], (int) sig.panelid[4], (int) sig.panelid[5], (int) sig.panelid[6], (int) sig.panelid[7]); #endif if (sig.magic != ECP_MAGIC) return(ENXIO); /* * Scan through the signature looking at the panels connected to the * board. Calculate the total number of ports as we go. */ for (panelnr = 0, nxtid = 0; (panelnr < STL_MAXPANELS); panelnr++) { status = sig.panelid[nxtid]; if ((status & ECH_PNLIDMASK) != nxtid) break; brdp->panelids[panelnr] = status; if (status & ECH_PNL16PORT) { brdp->panels[panelnr] = 16; brdp->nrports += 16; nxtid += 2; } else { brdp->panels[panelnr] = 8; brdp->nrports += 8; nxtid++; } brdp->nrpanels++; } brdp->state |= BST_FOUND; return(0); } /*****************************************************************************/ /* * Try to find an ONboard, Brumby or Stallion board and initialize it. * This handles only these board types. */ static int stli_initonb(stlibrd_t *brdp) { cdkonbsig_t sig; cdkonbsig_t *sigsp; int i; #if DEBUG printf("stli_initonb(brdp=%x)\n", (int) brdp); #endif /* * Do a basic sanity check on the IO and memory addresses. */ if ((brdp->iobase == 0) || (brdp->paddr == 0)) return(EINVAL); /* * Based on the specific board type setup the common vars to access * and enable shared memory. Set all board specific information now * as well. */ switch (brdp->brdtype) { case BRD_ONBOARD: case BRD_ONBOARD32: case BRD_ONBOARD2: case BRD_ONBOARD2_32: case BRD_ONBOARDRS: brdp->memsize = ONB_MEMSIZE; brdp->pagesize = ONB_ATPAGESIZE; brdp->init = stli_onbinit; brdp->enable = stli_onbenable; brdp->reenable = stli_onbenable; brdp->disable = stli_onbdisable; brdp->getmemptr = stli_onbgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_onbreset; brdp->confbits = (brdp->paddr > 0x100000) ? ONB_HIMEMENAB : 0; break; case BRD_ONBOARDE: brdp->memsize = ONB_EIMEMSIZE; brdp->pagesize = ONB_EIPAGESIZE; brdp->init = stli_onbeinit; brdp->enable = stli_onbeenable; brdp->reenable = stli_onbeenable; brdp->disable = stli_onbedisable; brdp->getmemptr = stli_onbegetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_onbereset; break; case BRD_BRUMBY4: case BRD_BRUMBY8: case BRD_BRUMBY16: brdp->memsize = BBY_MEMSIZE; brdp->pagesize = BBY_PAGESIZE; brdp->init = stli_bbyinit; brdp->enable = NULL; brdp->reenable = NULL; brdp->disable = NULL; brdp->getmemptr = stli_bbygetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_bbyreset; break; case BRD_STALLION: brdp->memsize = STAL_MEMSIZE; brdp->pagesize = STAL_PAGESIZE; brdp->init = stli_stalinit; brdp->enable = NULL; brdp->reenable = NULL; brdp->disable = NULL; brdp->getmemptr = stli_stalgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_stalreset; break; default: return(EINVAL); } /* * The per-board operations structure is all setup, so now lets go * and get the board operational. Firstly initialize board configuration * registers. */ EBRDINIT(brdp); /* * Now that all specific code is set up, enable the shared memory and * look for the a signature area that will tell us exactly what board * this is, and how many ports. */ EBRDENABLE(brdp); sigsp = (cdkonbsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR); bcopy(sigsp, &sig, sizeof(cdkonbsig_t)); EBRDDISABLE(brdp); #if 0 printf("%s(%d): sig-> magic=%x:%x:%x:%x romver=%x amask=%x:%x:%x\n", __file__, __LINE__, sig.magic0, sig.magic1, sig.magic2, sig.magic3, sig.romver, sig.amask0, sig.amask1, sig.amask2); #endif if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) || (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3)) return(ENXIO); /* * Scan through the signature alive mask and calculate how many ports * there are on this board. */ brdp->nrpanels = 1; if (sig.amask1) { brdp->nrports = 32; } else { for (i = 0; (i < 16); i++) { if (((sig.amask0 << i) & 0x8000) == 0) break; } brdp->nrports = i; } brdp->panels[0] = brdp->nrports; brdp->state |= BST_FOUND; return(0); } /*****************************************************************************/ /* * Start up a running board. This routine is only called after the * code has been down loaded to the board and is operational. It will * read in the memory map, and get the show on the road... */ static int stli_startbrd(stlibrd_t *brdp) { volatile cdkhdr_t *hdrp; volatile cdkmem_t *memp; volatile cdkasy_t *ap; stliport_t *portp; int portnr, nrdevs, i, rc; #if DEBUG printf("stli_startbrd(brdp=%x)\n", (int) brdp); #endif rc = 0; disable_intr(); EBRDENABLE(brdp); hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); nrdevs = hdrp->nrdevs; #if 0 printf("%s(%d): CDK version %d.%d.%d --> nrdevs=%d memp=%x hostp=%x " "slavep=%x\n", __file__, __LINE__, hdrp->ver_release, hdrp->ver_modification, hdrp->ver_fix, nrdevs, (int) hdrp->memp, (int) hdrp->hostp, (int) hdrp->slavep); #endif if (nrdevs < (brdp->nrports + 1)) { printf("STALLION: slave failed to allocate memory for all " "devices, devices=%d\n", nrdevs); brdp->nrports = nrdevs - 1; } brdp->nrdevs = nrdevs; brdp->hostoffset = hdrp->hostp - CDK_CDKADDR; brdp->slaveoffset = hdrp->slavep - CDK_CDKADDR; brdp->bitsize = (nrdevs + 7) / 8; memp = (volatile cdkmem_t *) hdrp->memp; if (((unsigned long) memp) > brdp->memsize) { printf("STALLION: corrupted shared memory region?\n"); rc = EIO; goto stli_donestartup; } memp = (volatile cdkmem_t *) EBRDGETMEMPTR(brdp, (unsigned long) memp); if (memp->dtype != TYP_ASYNCTRL) { printf("STALLION: no slave control device found\n"); rc = EIO; goto stli_donestartup; } memp++; /* * Cycle through memory allocation of each port. We are guaranteed to * have all ports inside the first page of slave window, so no need to * change pages while reading memory map. */ for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++, memp++) { if (memp->dtype != TYP_ASYNC) break; portp = brdp->ports[portnr]; if (portp == (stliport_t *) NULL) break; portp->devnr = i; portp->addr = memp->offset; portp->reqidx = (unsigned char) (i * 8 / nrdevs); portp->reqbit = (unsigned char) (0x1 << portp->reqidx); portp->portidx = (unsigned char) (i / 8); portp->portbit = (unsigned char) (0x1 << (i % 8)); } hdrp->slavereq = 0xff; /* * For each port setup a local copy of the RX and TX buffer offsets * and sizes. We do this separate from the above, because we need to * move the shared memory page... */ for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++) { portp = brdp->ports[portnr]; if (portp == (stliport_t *) NULL) break; if (portp->addr == 0) break; ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); if (ap != (volatile cdkasy_t *) NULL) { portp->rxsize = ap->rxq.size; portp->txsize = ap->txq.size; portp->rxoffset = ap->rxq.offset; portp->txoffset = ap->txq.offset; } } stli_donestartup: EBRDDISABLE(brdp); enable_intr(); if (rc == 0) brdp->state |= BST_STARTED; if (stli_doingtimeout == 0) { timeout(stli_poll, 0, 1); stli_doingtimeout++; } return(rc); } /*****************************************************************************/ /* * Probe and initialize the specified board. */ static int stli_brdinit(stlibrd_t *brdp) { #if DEBUG printf("stli_brdinit(brdp=%x)\n", (int) brdp); #endif stli_brds[brdp->brdnr] = brdp; switch (brdp->brdtype) { case BRD_ECP: case BRD_ECPE: case BRD_ECPMC: stli_initecp(brdp); break; case BRD_ONBOARD: case BRD_ONBOARDE: case BRD_ONBOARD2: case BRD_ONBOARD32: case BRD_ONBOARD2_32: case BRD_ONBOARDRS: case BRD_BRUMBY4: case BRD_BRUMBY8: case BRD_BRUMBY16: case BRD_STALLION: stli_initonb(brdp); break; case BRD_EASYIO: case BRD_ECH: case BRD_ECHMC: case BRD_ECHPCI: printf("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]); return(ENODEV); default: printf("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); return(ENODEV); } return(0); } /*****************************************************************************/ /* * Finish off the remaining initialization for a board. */ static int stli_brdattach(stlibrd_t *brdp) { #if DEBUG printf("stli_brdattach(brdp=%x)\n", (int) brdp); #endif #if 0 if ((brdp->state & BST_FOUND) == 0) { printf("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->paddr); return(ENXIO); } #endif stli_initports(brdp); printf("stli%d: %s (driver version %s), unit=%d nrpanels=%d " "nrports=%d\n", brdp->unitid, stli_brdnames[brdp->brdtype], stli_drvversion, brdp->brdnr, brdp->nrpanels, brdp->nrports); return(0); } /*****************************************************************************/ /* * Check for possible shared memory sharing between boards. * FIX: need to start this optimization somewhere... */ static int stli_chksharemem() { stlibrd_t *brdp, *nxtbrdp; int i, j; #if DEBUG printf("stli_chksharemem()\n"); #endif /* * All found boards are initialized. Now for a little optimization, if * no boards are sharing the "shared memory" regions then we can just * leave them all enabled. This is in fact the usual case. */ stli_shared = 0; if (stli_nrbrds > 1) { for (i = 0; (i < stli_nrbrds); i++) { brdp = stli_brds[i]; if (brdp == (stlibrd_t *) NULL) continue; for (j = i + 1; (j < stli_nrbrds); j++) { nxtbrdp = stli_brds[j]; if (nxtbrdp == (stlibrd_t *) NULL) continue; if ((brdp->paddr >= nxtbrdp->paddr) && (brdp->paddr <= (nxtbrdp->paddr + nxtbrdp->memsize - 1))) { stli_shared++; break; } } } } if (stli_shared == 0) { for (i = 0; (i < stli_nrbrds); i++) { brdp = stli_brds[i]; if (brdp == (stlibrd_t *) NULL) continue; if (brdp->state & BST_FOUND) { EBRDENABLE(brdp); brdp->enable = NULL; brdp->disable = NULL; } } } return(0); } /*****************************************************************************/ /* * Return the board stats structure to user app. */ static int stli_getbrdstats(caddr_t data) { stlibrd_t *brdp; int i; #if DEBUG printf("stli_getbrdstats(data=%x)\n", data); #endif stli_brdstats = *((combrd_t *) data); if (stli_brdstats.brd >= STL_MAXBRDS) return(-ENODEV); brdp = stli_brds[stli_brdstats.brd]; if (brdp == (stlibrd_t *) NULL) return(-ENODEV); bzero(&stli_brdstats, sizeof(combrd_t)); stli_brdstats.brd = brdp->brdnr; stli_brdstats.type = brdp->brdtype; stli_brdstats.hwid = 0; stli_brdstats.state = brdp->state; stli_brdstats.ioaddr = brdp->iobase; stli_brdstats.memaddr = brdp->paddr; stli_brdstats.nrpanels = brdp->nrpanels; stli_brdstats.nrports = brdp->nrports; for (i = 0; (i < brdp->nrpanels); i++) { stli_brdstats.panels[i].panel = i; stli_brdstats.panels[i].hwid = brdp->panelids[i]; stli_brdstats.panels[i].nrports = brdp->panels[i]; } *((combrd_t *) data) = stli_brdstats; return(0); } /*****************************************************************************/ /* * Resolve the referenced port number into a port struct pointer. */ static stliport_t *stli_getport(int brdnr, int panelnr, int portnr) { stlibrd_t *brdp; int i; if ((brdnr < 0) || (brdnr >= STL_MAXBRDS)) return((stliport_t *) NULL); brdp = stli_brds[brdnr]; if (brdp == (stlibrd_t *) NULL) return((stliport_t *) NULL); for (i = 0; (i < panelnr); i++) portnr += brdp->panels[i]; if ((portnr < 0) || (portnr >= brdp->nrports)) return((stliport_t *) NULL); return(brdp->ports[portnr]); } /*****************************************************************************/ /* * Return the port stats structure to user app. A NULL port struct * pointer passed in means that we need to find out from the app * what port to get stats for (used through board control device). */ static int stli_getportstats(stliport_t *portp, caddr_t data) { stlibrd_t *brdp; int rc; if (portp == (stliport_t *) NULL) { stli_comstats = *((comstats_t *) data); portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port); if (portp == (stliport_t *) NULL) return(-ENODEV); } brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) return(-ENODEV); if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, &stli_cdkstats, sizeof(asystats_t), 1)) < 0) return(rc); stli_comstats.brd = portp->brdnr; stli_comstats.panel = portp->panelnr; stli_comstats.port = portp->portnr; stli_comstats.state = portp->state; /*stli_comstats.flags = portp->flags;*/ stli_comstats.ttystate = portp->tty.t_state; stli_comstats.cflags = portp->tty.t_cflag; stli_comstats.iflags = portp->tty.t_iflag; stli_comstats.oflags = portp->tty.t_oflag; stli_comstats.lflags = portp->tty.t_lflag; stli_comstats.txtotal = stli_cdkstats.txchars; stli_comstats.rxtotal = stli_cdkstats.rxchars + stli_cdkstats.ringover; stli_comstats.txbuffered = stli_cdkstats.txringq; stli_comstats.rxbuffered = stli_cdkstats.rxringq; stli_comstats.rxoverrun = stli_cdkstats.overruns; stli_comstats.rxparity = stli_cdkstats.parity; stli_comstats.rxframing = stli_cdkstats.framing; stli_comstats.rxlost = stli_cdkstats.ringover + portp->rxlost; stli_comstats.rxbreaks = stli_cdkstats.rxbreaks; stli_comstats.txbreaks = stli_cdkstats.txbreaks; stli_comstats.txxon = stli_cdkstats.txstart; stli_comstats.txxoff = stli_cdkstats.txstop; stli_comstats.rxxon = stli_cdkstats.rxstart; stli_comstats.rxxoff = stli_cdkstats.rxstop; stli_comstats.rxrtsoff = stli_cdkstats.rtscnt / 2; stli_comstats.rxrtson = stli_cdkstats.rtscnt - stli_comstats.rxrtsoff; stli_comstats.modem = stli_cdkstats.dcdcnt; stli_comstats.hwid = stli_cdkstats.hwid; stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals); *((comstats_t *) data) = stli_comstats;; return(0); } /*****************************************************************************/ /* * Clear the port stats structure. We also return it zeroed out... */ static int stli_clrportstats(stliport_t *portp, caddr_t data) { stlibrd_t *brdp; int rc; if (portp == (stliport_t *) NULL) { stli_comstats = *((comstats_t *) data); portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port); if (portp == (stliport_t *) NULL) return(-ENODEV); } brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) return(-ENODEV); if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, 0, 0, 0)) < 0) return(rc); portp->rxlost = 0; bzero(&stli_comstats, sizeof(comstats_t)); stli_comstats.brd = portp->brdnr; stli_comstats.panel = portp->panelnr; stli_comstats.port = portp->portnr; *((comstats_t *) data) = stli_comstats;; return(0); } /*****************************************************************************/ /* * Code to handle an "staliomem" read and write operations. This device * is the contents of the board shared memory. It is used for down * loading the slave image (and debugging :-) */ STATIC int stli_memrw(dev_t dev, struct uio *uiop, int flag) { stlibrd_t *brdp; void *memptr; int brdnr, size, n, error; #if DEBUG printf("stli_memrw(dev=%x,uiop=%x,flag=%x)\n", (int) dev, (int) uiop, flag); #endif brdnr = dev & 0x7; brdp = stli_brds[brdnr]; if (brdp == (stlibrd_t *) NULL) return(ENODEV); if (brdp->state == 0) return(ENODEV); if (uiop->uio_offset >= brdp->memsize) return(0); error = 0; size = brdp->memsize - uiop->uio_offset; disable_intr(); EBRDENABLE(brdp); while (size > 0) { memptr = (void *) EBRDGETMEMPTR(brdp, uiop->uio_offset); n = MIN(size, (brdp->pagesize - (((unsigned long) uiop->uio_offset) % brdp->pagesize))); error = uiomove(memptr, n, uiop); if ((uiop->uio_resid == 0) || error) break; } EBRDDISABLE(brdp); enable_intr(); return(error); } /*****************************************************************************/ /* * The "staliomem" device is also required to do some special operations * on the board. We need to be able to send an interrupt to the board, * reset it, and start/stop it. */ static int stli_memioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { stlibrd_t *brdp; int brdnr, rc; #if DEBUG printf("stli_memioctl(dev=%x,cmd=%x,data=%x,flag=%x)\n", (int) dev, cmd, (int) data, flag); #endif brdnr = dev & 0x7; brdp = stli_brds[brdnr]; if (brdp == (stlibrd_t *) NULL) return(ENODEV); if (brdp->state == 0) return(ENODEV); rc = 0; switch (cmd) { case STL_BINTR: EBRDINTR(brdp); break; case STL_BSTART: rc = stli_startbrd(brdp); break; case STL_BSTOP: brdp->state &= ~BST_STARTED; break; case STL_BRESET: brdp->state &= ~BST_STARTED; EBRDRESET(brdp); if (stli_shared == 0) { if (brdp->reenable != NULL) (* brdp->reenable)(brdp); } break; case COM_GETPORTSTATS: rc = stli_getportstats((stliport_t *) NULL, data); break; case COM_CLRPORTSTATS: rc = stli_clrportstats((stliport_t *) NULL, data); break; case COM_GETBRDSTATS: rc = stli_getbrdstats(data); break; default: rc = ENOTTY; break; } return(rc); } /*****************************************************************************/ Index: head/sys/i386/isa/wt.c =================================================================== --- head/sys/i386/isa/wt.c (revision 17970) +++ head/sys/i386/isa/wt.c (revision 17971) @@ -1,1023 +1,1023 @@ /* * Streamer tape driver for 386bsd and FreeBSD. * Supports Archive and Wangtek compatible QIC-02/QIC-36 boards. * * Copyright (C) 1993 by: * Sergey Ryzhkov * Serge Vakulenko * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * This driver is derived from the old 386bsd Wangtek streamer tape driver, * made by Robert Baron at CMU, based on Intel sources. * Authors thank Robert Baron, CMU and Intel and retain here * the original CMU copyright notice. * * Version 1.3, Thu Nov 11 12:09:13 MSK 1993 - * $Id: wt.c,v 1.32 1996/04/08 19:40:57 smpatel Exp $ + * $Id: wt.c,v 1.33 1996/07/23 21:51:50 phk Exp $ * */ /* * Code for MTERASE added by John Lind (john@starfire.mn.org) 95/09/02. * This was very easy due to the excellent structure and clear coding * of the original driver. */ /* * Copyright (c) 1989 Carnegie-Mellon University. * All rights reserved. * * Authors: Robert Baron * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include "wt.h" #if NWT > 0 #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include /* * Uncomment this to enable internal device tracing. */ #define TRACE(s) /* printf s */ #define WTPRI (PZERO+10) /* sleep priority */ /* * Wangtek controller ports */ #define WT_CTLPORT(base) ((base)+0) /* control, write only */ #define WT_STATPORT(base) ((base)+0) /* status, read only */ #define WT_CMDPORT(base) ((base)+1) /* command, write only */ #define WT_DATAPORT(base) ((base)+1) /* data, read only */ #define WT_NPORT 2 /* 2 i/o ports */ /* status port bits */ #define WT_BUSY 0x01 /* not ready bit define */ #define WT_NOEXCEP 0x02 /* no exception bit define */ #define WT_RESETMASK 0x07 /* to check after reset */ #define WT_RESETVAL 0x05 /* state after reset */ /* control port bits */ #define WT_ONLINE 0x01 /* device selected */ #define WT_RESET 0x02 /* reset command */ #define WT_REQUEST 0x04 /* request command */ #define WT_IEN 0x08 /* enable dma */ /* * Archive controller ports */ #define AV_DATAPORT(base) ((base)+0) /* data, read only */ #define AV_CMDPORT(base) ((base)+0) /* command, write only */ #define AV_STATPORT(base) ((base)+1) /* status, read only */ #define AV_CTLPORT(base) ((base)+1) /* control, write only */ #define AV_SDMAPORT(base) ((base)+2) /* start dma */ #define AV_RDMAPORT(base) ((base)+3) /* reset dma */ #define AV_NPORT 4 /* 4 i/o ports */ /* status port bits */ #define AV_BUSY 0x40 /* not ready bit define */ #define AV_NOEXCEP 0x20 /* no exception bit define */ #define AV_RESETMASK 0xf8 /* to check after reset */ #define AV_RESETVAL 0x50 /* state after reset */ /* control port bits */ #define AV_RESET 0x80 /* reset command */ #define AV_REQUEST 0x40 /* request command */ #define AV_IEN 0x20 /* enable interrupts */ enum wttype { UNKNOWN = 0, /* unknown type, driver disabled */ ARCHIVE, /* Archive Viper SC499, SC402 etc */ WANGTEK, /* Wangtek */ }; typedef struct { unsigned short err; /* code for error encountered */ unsigned short ercnt; /* number of error blocks */ unsigned short urcnt; /* number of underruns */ } wtstatus_t; typedef struct { enum wttype type; /* type of controller */ unsigned unit; /* unit number */ unsigned port; /* base i/o port */ unsigned chan; /* dma channel number, 1..3 */ unsigned flags; /* state of tape drive */ unsigned dens; /* tape density */ int bsize; /* tape block size */ void *buf; /* internal i/o buffer */ void *dmavaddr; /* virtual address of dma i/o buffer */ unsigned dmatotal; /* size of i/o buffer */ unsigned dmaflags; /* i/o direction, B_READ or B_WRITE */ unsigned dmacount; /* resulting length of dma i/o */ wtstatus_t error; /* status of controller */ unsigned short DATAPORT, CMDPORT, STATPORT, CTLPORT, SDMAPORT, RDMAPORT; unsigned char BUSY, NOEXCEP, RESETMASK, RESETVAL; unsigned char ONLINE, RESET, REQUEST, IEN; #ifdef DEVFS void *devfs_token; void *devfs_token_r; #endif } wtinfo_t; static wtinfo_t wttab[NWT]; /* tape info by unit number */ static int wtwait (wtinfo_t *t, int catch, char *msg); static int wtcmd (wtinfo_t *t, int cmd); static int wtstart (wtinfo_t *t, unsigned mode, void *vaddr, unsigned len); static void wtdma (wtinfo_t *t); static timeout_t wtimer; static void wtclock (wtinfo_t *t); static int wtreset (wtinfo_t *t); static int wtsense (wtinfo_t *t, int verb, int ignor); static int wtstatus (wtinfo_t *t); static void wtrewind (wtinfo_t *t); static int wtreadfm (wtinfo_t *t); static int wtwritefm (wtinfo_t *t); static int wtpoll (wtinfo_t *t, int mask, int bits); static struct kern_devconf kdc_wt[NWT] = { { 0, 0, 0, /* filled in by dev_attach */ "wt", 0, { MDDT_ISA, 0, "bio" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, /* state */ "Archive or Wangtek QIC-02/QIC-36 tape controller", DC_CLS_TAPE /* class */ } }; static d_open_t wtopen; static d_close_t wtclose; static d_ioctl_t wtioctl; static d_dump_t wtdump; static d_psize_t wtsize; static d_strategy_t wtstrategy; #define CDEV_MAJOR 10 #define BDEV_MAJOR 3 static struct cdevsw wt_cdevsw; static struct bdevsw wt_bdevsw = { wtopen, wtclose, wtstrategy, wtioctl, /*3*/ wtdump, wtsize, B_TAPE, "wt", &wt_cdevsw, -1 }; static inline void wt_registerdev(struct isa_device *id) { if(id->id_unit) kdc_wt[id->id_unit] = kdc_wt[0]; kdc_wt[id->id_unit].kdc_unit = id->id_unit; kdc_wt[id->id_unit].kdc_parentdata = id; dev_attach(&kdc_wt[id->id_unit]); } /* * Probe for the presence of the device. */ static int wtprobe (struct isa_device *id) { wtinfo_t *t = wttab + id->id_unit; wt_registerdev(id); t->unit = id->id_unit; t->chan = id->id_drq; t->port = id->id_iobase; if (t->chan<1 || t->chan>3) { printf ("wt%d: Bad drq=%d, should be 1..3\n", t->unit, t->chan); return (0); } /* Try Wangtek. */ t->type = WANGTEK; t->CTLPORT = WT_CTLPORT (t->port); t->STATPORT = WT_STATPORT (t->port); t->CMDPORT = WT_CMDPORT (t->port); t->DATAPORT = WT_DATAPORT (t->port); t->SDMAPORT = 0; t->RDMAPORT = 0; t->BUSY = WT_BUSY; t->NOEXCEP = WT_NOEXCEP; t->RESETMASK = WT_RESETMASK; t->RESETVAL = WT_RESETVAL; t->ONLINE = WT_ONLINE; t->RESET = WT_RESET; t->REQUEST = WT_REQUEST; t->IEN = WT_IEN; if (wtreset (t)) return (WT_NPORT); /* Try Archive. */ t->type = ARCHIVE; t->CTLPORT = AV_CTLPORT (t->port); t->STATPORT = AV_STATPORT (t->port); t->CMDPORT = AV_CMDPORT (t->port); t->DATAPORT = AV_DATAPORT (t->port); t->SDMAPORT = AV_SDMAPORT (t->port); t->RDMAPORT = AV_RDMAPORT (t->port); t->BUSY = AV_BUSY; t->NOEXCEP = AV_NOEXCEP; t->RESETMASK = AV_RESETMASK; t->RESETVAL = AV_RESETVAL; t->ONLINE = 0; t->RESET = AV_RESET; t->REQUEST = AV_REQUEST; t->IEN = AV_IEN; if (wtreset (t)) return (AV_NPORT); /* Tape controller not found. */ t->type = UNKNOWN; return (0); } /* * Device is found, configure it. */ static int wtattach (struct isa_device *id) { wtinfo_t *t = wttab + id->id_unit; if (t->type == ARCHIVE) { printf ("wt%d: type \n", t->unit); outb (t->RDMAPORT, 0); /* reset dma */ } else printf ("wt%d: type \n", t->unit); t->flags = TPSTART; /* tape is rewound */ t->dens = -1; /* unknown density */ kdc_wt[id->id_unit].kdc_state = DC_IDLE; isa_dmainit(t->chan, 1024); #ifdef DEVFS t->devfs_token_r = devfs_add_devswf(&wt_cdevsw, id->id_unit, DV_CHR, 0, 0, 0600, "rwt%d", id->id_unit); t->devfs_token = devfs_add_devswf(&wt_bdevsw, id->id_unit, DV_BLK, 0, 0, 0600, "wt%d", id->id_unit); #endif return (1); } struct isa_driver wtdriver = { wtprobe, wtattach, "wt", }; int wtdump (dev_t dev) { /* Not implemented */ return (EINVAL); } int wtsize (dev_t dev) { /* Not implemented */ return (-1); } /* * Open routine, called on every device open. */ int wtopen (dev_t dev, int flag, int fmt, struct proc *p) { int u = minor (dev) & T_UNIT; wtinfo_t *t = wttab + u; int error; if (u >= NWT || t->type == UNKNOWN) return (ENXIO); /* Check that device is not in use */ if (t->flags & TPINUSE) return (EBUSY); /* If the tape is in rewound state, check the status and set density. */ if (t->flags & TPSTART) { /* If rewind is going on, wait */ if (error = wtwait (t, PCATCH, "wtrew")) return (error); /* Check the controller status */ if (! wtsense (t, 0, (flag & FWRITE) ? 0 : TP_WRP)) { /* Bad status, reset the controller */ if (! wtreset (t)) return (EIO); if (! wtsense (t, 1, (flag & FWRITE) ? 0 : TP_WRP)) return (EIO); } /* Set up tape density. */ if (t->dens != (minor (dev) & WT_DENSEL)) { int d = 0; switch (minor (dev) & WT_DENSEL) { case WT_DENSDFLT: default: break; /* default density */ case WT_QIC11: d = QIC_FMT11; break; /* minor 010 */ case WT_QIC24: d = QIC_FMT24; break; /* minor 020 */ case WT_QIC120: d = QIC_FMT120; break; /* minor 030 */ case WT_QIC150: d = QIC_FMT150; break; /* minor 040 */ case WT_QIC300: d = QIC_FMT300; break; /* minor 050 */ case WT_QIC600: d = QIC_FMT600; break; /* minor 060 */ } if (d) { /* Change tape density. */ if (! wtcmd (t, d)) return (EIO); if (! wtsense (t, 1, TP_WRP | TP_ILL)) return (EIO); /* Check the status of the controller. */ if (t->error.err & TP_ILL) { printf ("wt%d: invalid tape density\n", t->unit); return (ENODEV); } } t->dens = minor (dev) & WT_DENSEL; } t->flags &= ~TPSTART; } else if (t->dens != (minor (dev) & WT_DENSEL)) return (ENXIO); t->bsize = (minor (dev) & WT_BSIZE) ? 1024 : 512; t->buf = malloc (t->bsize, M_TEMP, M_WAITOK); if (! t->buf) return (EAGAIN); if (isa_dma_acquire(t->chan)) return(EBUSY); t->flags = TPINUSE; kdc_wt[u].kdc_state = DC_BUSY; if (flag & FREAD) t->flags |= TPREAD; if (flag & FWRITE) t->flags |= TPWRITE; return (0); } /* * Close routine, called on last device close. */ int wtclose (dev_t dev, int flags, int fmt, struct proc *p) { int u = minor (dev) & T_UNIT; wtinfo_t *t = wttab + u; if (u >= NWT || t->type == UNKNOWN) return (ENXIO); /* If rewind is pending, do nothing */ if (t->flags & TPREW) goto done; /* If seek forward is pending and no rewind on close, do nothing */ if (t->flags & TPRMARK) { if (minor (dev) & T_NOREWIND) goto done; /* If read file mark is going on, wait */ wtwait (t, 0, "wtrfm"); } if (t->flags & TPWANY) /* Tape was written. Write file mark. */ wtwritefm (t); if (! (minor (dev) & T_NOREWIND)) { /* Rewind tape to beginning of tape. */ /* Don't wait until rewind, though. */ wtrewind (t); goto done; } if ((t->flags & TPRANY) && ! (t->flags & (TPVOL | TPWANY))) /* Space forward to after next file mark if no writing done. */ /* Don't wait for completion. */ wtreadfm (t); done: t->flags &= TPREW | TPRMARK | TPSTART | TPTIMER; kdc_wt[u].kdc_state = DC_IDLE; free (t->buf, M_TEMP); isa_dma_release(t->chan); return (0); } /* * Ioctl routine. Compatible with BSD ioctls. * There are two possible ioctls: * ioctl (int fd, MTIOCGET, struct mtget *buf) -- get status * ioctl (int fd, MTIOCTOP, struct mtop *buf) -- do BSD-like op */ int wtioctl (dev_t dev, int cmd, caddr_t arg, int flags, struct proc *p) { int u = minor (dev) & T_UNIT; wtinfo_t *t = wttab + u; int error, count, op; if (u >= NWT || t->type == UNKNOWN) return (ENXIO); switch (cmd) { default: return (EINVAL); case MTIOCIEOT: /* ignore EOT errors */ case MTIOCEEOT: /* enable EOT errors */ return (0); case MTIOCGET: ((struct mtget*)arg)->mt_type = t->type == ARCHIVE ? MT_ISVIPER1 : 0x11; ((struct mtget*)arg)->mt_dsreg = t->flags; /* status */ ((struct mtget*)arg)->mt_erreg = t->error.err; /* errors */ ((struct mtget*)arg)->mt_resid = 0; ((struct mtget*)arg)->mt_fileno = 0; /* file */ ((struct mtget*)arg)->mt_blkno = 0; /* block */ return (0); case MTIOCTOP: break; } switch ((short) ((struct mtop*)arg)->mt_op) { default: case MTFSR: /* forward space record */ case MTBSR: /* backward space record */ case MTBSF: /* backward space file */ break; case MTNOP: /* no operation, sets status only */ case MTCACHE: /* enable controller cache */ case MTNOCACHE: /* disable controller cache */ return (0); case MTREW: /* rewind */ case MTOFFL: /* rewind and put the drive offline */ if (t->flags & TPREW) /* rewind is running */ return (0); if (error = wtwait (t, PCATCH, "wtorew")) return (error); wtrewind (t); return (0); case MTFSF: /* forward space file */ for (count=((struct mtop*)arg)->mt_count; count>0; --count) { if (error = wtwait (t, PCATCH, "wtorfm")) return (error); if (error = wtreadfm (t)) return (error); } return (0); case MTWEOF: /* write an end-of-file record */ if (! (t->flags & TPWRITE) || (t->flags & TPWP)) return (EACCES); if (error = wtwait (t, PCATCH, "wtowfm")) return (error); if (error = wtwritefm (t)) return (error); return (0); case MTRETENS: /* re-tension tape */ if (error = wtwait (t, PCATCH, "wtretens")) return (error); op = QIC_RETENS; goto erase_retens; case MTERASE: /* erase to EOM */ if (! (t->flags & TPWRITE) || (t->flags & TPWP)) return (EACCES); if (error = wtwait (t, PCATCH, "wterase")) return (error); op = QIC_ERASE; erase_retens: /* ERASE and RETENS operations work like REWIND. */ /* Simulate the rewind operation here. */ t->flags &= ~(TPRO | TPWO | TPVOL); if (! wtcmd (t, op)) return (EIO); t->flags |= TPSTART | TPREW; t->flags |= TPWANY; wtclock (t); return (0); } return (EINVAL); } /* * Strategy routine. */ void wtstrategy (struct buf *bp) { int u = minor (bp->b_dev) & T_UNIT; wtinfo_t *t = wttab + u; int s; bp->b_resid = bp->b_bcount; if (u >= NWT || t->type == UNKNOWN) goto errxit; /* at file marks and end of tape, we just return '0 bytes available' */ if (t->flags & TPVOL) goto xit; if (bp->b_flags & B_READ) { /* Check read access and no previous write to this tape. */ if (! (t->flags & TPREAD) || (t->flags & TPWANY)) goto errxit; /* For now, we assume that all data will be copied out */ /* If read command outstanding, just skip down */ if (! (t->flags & TPRO)) { if (! wtsense (t, 1, TP_WRP)) /* clear status */ goto errxit; if (! wtcmd (t, QIC_RDDATA)) { /* sed read mode */ wtsense (t, 1, TP_WRP); goto errxit; } t->flags |= TPRO | TPRANY; } } else { /* Check write access and write protection. */ /* No previous read from this tape allowed. */ if (! (t->flags & TPWRITE) || (t->flags & (TPWP | TPRANY))) goto errxit; /* If write command outstanding, just skip down */ if (! (t->flags & TPWO)) { if (! wtsense (t, 1, 0)) /* clear status */ goto errxit; if (! wtcmd (t, QIC_WRTDATA)) { /* set write mode */ wtsense (t, 1, 0); goto errxit; } t->flags |= TPWO | TPWANY; } } if (! bp->b_bcount) goto xit; t->flags &= ~TPEXCEP; s = splbio (); if (wtstart (t, bp->b_flags, bp->b_un.b_addr, bp->b_bcount)) { wtwait (t, 0, (bp->b_flags & B_READ) ? "wtread" : "wtwrite"); bp->b_resid -= t->dmacount; } splx (s); if (t->flags & TPEXCEP) { errxit: bp->b_flags |= B_ERROR; bp->b_error = EIO; } xit: biodone (bp); return; } /* * Interrupt routine. */ void wtintr (int u) { wtinfo_t *t = wttab + u; unsigned char s; if (u >= NWT || t->type == UNKNOWN) { TRACE (("wtintr() -- device not configured\n")); return; } s = inb (t->STATPORT); /* get status */ TRACE (("wtintr() status=0x%x -- ", s)); if ((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP)) { TRACE (("busy\n")); return; /* device is busy */ } /* * Check if rewind finished. */ if (t->flags & TPREW) { TRACE (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ? "rewind busy?\n" : "rewind finished\n")); t->flags &= ~TPREW; /* Rewind finished. */ wtsense (t, 1, TP_WRP); wakeup ((caddr_t)t); return; } /* * Check if writing/reading of file mark finished. */ if (t->flags & (TPRMARK | TPWMARK)) { TRACE (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ? "marker r/w busy?\n" : "marker r/w finished\n")); if (! (s & t->NOEXCEP)) /* operation failed */ wtsense (t, 1, (t->flags & TPRMARK) ? TP_WRP : 0); t->flags &= ~(TPRMARK | TPWMARK); /* operation finished */ wakeup ((caddr_t)t); return; } /* * Do we started any i/o? If no, just return. */ if (! (t->flags & TPACTIVE)) { TRACE (("unexpected interrupt\n")); return; } t->flags &= ~TPACTIVE; t->dmacount += t->bsize; /* increment counter */ /* * Clean up dma. */ if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < t->bsize) { /* If reading short block, copy the internal buffer * to the user memory. */ isa_dmadone (t->dmaflags, t->buf, t->bsize, t->chan); bcopy (t->buf, t->dmavaddr, t->dmatotal - t->dmacount); } else isa_dmadone (t->dmaflags, t->dmavaddr, t->bsize, t->chan); /* * On exception, check for end of file and end of volume. */ if (! (s & t->NOEXCEP)) { TRACE (("i/o exception\n")); wtsense (t, 1, (t->dmaflags & B_READ) ? TP_WRP : 0); if (t->error.err & (TP_EOM | TP_FIL)) t->flags |= TPVOL; /* end of file */ else t->flags |= TPEXCEP; /* i/o error */ wakeup ((caddr_t)t); return; } if (t->dmacount < t->dmatotal) { /* continue i/o */ - t->dmavaddr += t->bsize; + t->dmavaddr = (char *)t->dmavaddr + t->bsize; wtdma (t); TRACE (("continue i/o, %d\n", t->dmacount)); return; } if (t->dmacount > t->dmatotal) /* short last block */ t->dmacount = t->dmatotal; wakeup ((caddr_t)t); /* wake up user level */ TRACE (("i/o finished, %d\n", t->dmacount)); } /* start the rewind operation */ static void wtrewind (wtinfo_t *t) { int rwmode = (t->flags & (TPRO | TPWO)); t->flags &= ~(TPRO | TPWO | TPVOL); /* * Wangtek strictly follows QIC-02 standard: * clearing ONLINE in read/write modes causes rewind. * REWIND command is not allowed in read/write mode * and gives `illegal command' error. */ if (t->type==WANGTEK && rwmode) { outb (t->CTLPORT, 0); } else if (! wtcmd (t, QIC_REWIND)) return; t->flags |= TPSTART | TPREW; wtclock (t); } /* start the `read marker' operation */ static int wtreadfm (wtinfo_t *t) { t->flags &= ~(TPRO | TPWO | TPVOL); if (! wtcmd (t, QIC_READFM)) { wtsense (t, 1, TP_WRP); return (EIO); } t->flags |= TPRMARK | TPRANY; wtclock (t); /* Don't wait for completion here. */ return (0); } /* write marker to the tape */ static int wtwritefm (wtinfo_t *t) { tsleep ((caddr_t)wtwritefm, WTPRI, "wtwfm", hz); /* timeout: 1 second */ t->flags &= ~(TPRO | TPWO); if (! wtcmd (t, QIC_WRITEFM)) { wtsense (t, 1, 0); return (EIO); } t->flags |= TPWMARK | TPWANY; wtclock (t); return (wtwait (t, 0, "wtwfm")); } /* while controller status & mask == bits continue waiting */ static int wtpoll (wtinfo_t *t, int mask, int bits) { int s, i; /* Poll status port, waiting for specified bits. */ for (i=0; i<1000; ++i) { /* up to 1 msec */ s = inb (t->STATPORT); if ((s & mask) != bits) return (s); DELAY (1); } for (i=0; i<100; ++i) { /* up to 10 msec */ s = inb (t->STATPORT); if ((s & mask) != bits) return (s); DELAY (100); } for (;;) { /* forever */ s = inb (t->STATPORT); if ((s & mask) != bits) return (s); tsleep ((caddr_t)wtpoll, WTPRI, "wtpoll", 1); /* timeout: 1 tick */ } } /* execute QIC command */ static int wtcmd (wtinfo_t *t, int cmd) { int s, x; TRACE (("wtcmd() cmd=0x%x\n", cmd)); x = splbio(); s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */ if (! (s & t->NOEXCEP)) { /* error */ splx(x); return (0); } outb (t->CMDPORT, cmd); /* output the command */ outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */ outb (t->CTLPORT, t->IEN | t->ONLINE); /* reset request */ wtpoll (t, t->BUSY, 0); /* wait for not ready */ splx(x); return (1); } /* wait for the end of i/o, seeking marker or rewind operation */ static int wtwait (wtinfo_t *t, int catch, char *msg) { int error; TRACE (("wtwait() `%s'\n", msg)); while (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)) if (error = tsleep ((caddr_t)t, WTPRI | catch, msg, 0)) return (error); return (0); } /* initialize dma for the i/o operation */ static void wtdma (wtinfo_t *t) { t->flags |= TPACTIVE; wtclock (t); if (t->type == ARCHIVE) outb (t->SDMAPORT, 0); /* set dma */ if ((t->dmaflags & B_READ) && (t->dmatotal - t->dmacount) < t->bsize) /* Reading short block. Do it through the internal buffer. */ isa_dmastart (t->dmaflags, t->buf, t->bsize, t->chan); else isa_dmastart (t->dmaflags, t->dmavaddr, t->bsize, t->chan); } /* start i/o operation */ static int wtstart (wtinfo_t *t, unsigned flags, void *vaddr, unsigned len) { int s, x; TRACE (("wtstart()\n")); x = splbio(); s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */ if (! (s & t->NOEXCEP)) { t->flags |= TPEXCEP; /* error */ splx(x); return (0); } t->flags &= ~TPEXCEP; /* clear exception flag */ t->dmavaddr = vaddr; t->dmatotal = len; t->dmacount = 0; t->dmaflags = flags; wtdma (t); splx(x); return (1); } /* start timer */ static void wtclock (wtinfo_t *t) { if (! (t->flags & TPTIMER)) { t->flags |= TPTIMER; /* Some controllers seem to lose dma interrupts too often. * To make the tape stream we need 1 tick timeout. */ timeout (wtimer, (caddr_t)t, (t->flags & TPACTIVE) ? 1 : hz); } } /* * Simulate an interrupt periodically while i/o is going. * This is necessary in case interrupts get eaten due to * multiple devices on a single IRQ line. */ static void wtimer (void *xt) { wtinfo_t *t = (wtinfo_t *)xt; int s; t->flags &= ~TPTIMER; if (! (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK))) return; /* If i/o going, simulate interrupt. */ s = splbio (); if ((inb (t->STATPORT) & (t->BUSY | t->NOEXCEP)) != (t->BUSY | t->NOEXCEP)) { TRACE (("wtimer() -- ")); wtintr (t->unit); } splx (s); /* Restart timer if i/o pending. */ if (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)) wtclock (t); } /* reset the controller */ static int wtreset (wtinfo_t *t) { /* Perform QIC-02 and QIC-36 compatible reset sequence. */ /* Thanks to Mikael Hybsch . */ int s, i; outb (t->CTLPORT, t->RESET | t->ONLINE); /* send reset */ DELAY (30); outb (t->CTLPORT, t->ONLINE); /* turn off reset */ DELAY (30); /* Read the controller status. */ s = inb (t->STATPORT); if (s == 0xff) /* no port at this address? */ return (0); /* Wait 3 sec for reset to complete. Needed for QIC-36 boards? */ for (i=0; i<3000; ++i) { if (! (s & t->BUSY) || ! (s & t->NOEXCEP)) break; DELAY (1000); s = inb (t->STATPORT); } return ((s & t->RESETMASK) == t->RESETVAL); } /* get controller status information */ /* return 0 if user i/o request should receive an i/o error code */ static int wtsense (wtinfo_t *t, int verb, int ignor) { char *msg = 0; int err; TRACE (("wtsense() ignor=0x%x\n", ignor)); t->flags &= ~(TPRO | TPWO); if (! wtstatus (t)) return (0); if (! (t->error.err & TP_ST0)) t->error.err &= ~TP_ST0MASK; if (! (t->error.err & TP_ST1)) t->error.err &= ~TP_ST1MASK; t->error.err &= ~ignor; /* ignore certain errors */ err = t->error.err & (TP_FIL | TP_BNL | TP_UDA | TP_EOM | TP_WRP | TP_USL | TP_CNI | TP_MBD | TP_NDT | TP_ILL); if (! err) return (1); if (! verb) return (0); /* lifted from tdriver.c from Wangtek */ if (err & TP_USL) msg = "Drive not online"; else if (err & TP_CNI) msg = "No cartridge"; else if ((err & TP_WRP) && !(t->flags & TPWP)) { msg = "Tape is write protected"; t->flags |= TPWP; } else if (err & TP_FIL) msg = 0 /*"Filemark detected"*/; else if (err & TP_EOM) msg = 0 /*"End of tape"*/; else if (err & TP_BNL) msg = "Block not located"; else if (err & TP_UDA) msg = "Unrecoverable data error"; else if (err & TP_NDT) msg = "No data detected"; else if (err & TP_ILL) msg = "Illegal command"; if (msg) printf ("wt%d: %s\n", t->unit, msg); return (0); } /* get controller status information */ static int wtstatus (wtinfo_t *t) { char *p; int x; x = splbio(); wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */ outb (t->CMDPORT, QIC_RDSTAT); /* send `read status' command */ outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */ outb (t->CTLPORT, t->ONLINE); /* reset request */ wtpoll (t, t->BUSY, 0); /* wait for not ready */ p = (char*) &t->error; while (p < (char*)&t->error + 6) { int s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); if (! (s & t->NOEXCEP)) { /* error */ splx(x); return (0); } *p++ = inb (t->DATAPORT); /* read status byte */ outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ wtpoll (t, t->BUSY, 0); /* wait for not ready */ DELAY(20); outb (t->CTLPORT, t->ONLINE); /* unset request */ } splx(x); return (1); } static wt_devsw_installed = 0; static void wt_drvinit(void *unused) { if( ! wt_devsw_installed ) { bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &wt_bdevsw); wt_devsw_installed = 1; } } SYSINIT(wtdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,wt_drvinit,NULL) #endif /* NWT */ Index: head/sys/kern/kern_devconf.c =================================================================== --- head/sys/kern/kern_devconf.c (revision 17970) +++ head/sys/kern/kern_devconf.c (revision 17971) @@ -1,181 +1,181 @@ /* * Copyright (c) 1994, Garrett A. Wollman. 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. * - * $Id: kern_devconf.c,v 1.14 1996/01/31 18:05:19 gibbs Exp $ + * $Id: kern_devconf.c,v 1.15 1996/06/12 15:10:27 joerg Exp $ */ /* * kern_devconf.c - manage device configuration table * * Garrett A. Wollman, October 1994. */ #include #include #include #include #include #include struct kern_devconf *dc_list = 0; static unsigned dc_lastnum = 0; int dev_attach(struct kern_devconf *kdc) { int s = splclock(); kdc->kdc_next = dc_list; if(kdc->kdc_next) kdc->kdc_next->kdc_rlink = &kdc->kdc_next; kdc->kdc_rlink = &dc_list; kdc->kdc_number = ++dc_lastnum; dc_list = kdc; splx(s); return 0; } int dev_detach(struct kern_devconf *kdc) { int s; s = splclock(); *kdc->kdc_rlink = kdc->kdc_next; if(kdc->kdc_next) kdc->kdc_next->kdc_rlink = kdc->kdc_rlink; splx(s); return 0; } /* * NB: the device must do a dev_detach inside its shutdown routine, if it * succeeds. If no routine is specified, we assume no special treatment is * required. */ int dev_shutdownall(int force) { int rv = 0; struct kern_devconf *kdc = dc_list; while(kdc) { if(!kdc->kdc_shutdown) { dev_detach(kdc); kdc = dc_list; continue; } if(kdc->kdc_shutdown(kdc, force)) { rv++; kdc = kdc->kdc_next; } else { kdc = dc_list; } } return rv; } static void make_devconf(struct kern_devconf *kdc, struct devconf *dc) { strncpy(dc->dc_name, kdc->kdc_name, sizeof dc->dc_name); dc->dc_name[(sizeof dc->dc_name) - 1] = '\0'; dc->dc_unit = kdc->kdc_unit; dc->dc_number = kdc->kdc_number; if(kdc->kdc_parent) { strncpy(dc->dc_pname, kdc->kdc_parent->kdc_name, sizeof dc->dc_pname); dc->dc_pname[(sizeof dc->dc_pname) - 1] = '\0'; dc->dc_punit = kdc->kdc_parent->kdc_unit; dc->dc_pnumber = kdc->kdc_parent->kdc_number; } else { bzero(dc->dc_pname, sizeof dc->dc_pname); dc->dc_punit = -1; dc->dc_pnumber = -1; } MACHDEP_COPYDEV(dc, kdc); dc->dc_state = kdc->kdc_state; dc->dc_class = kdc->kdc_class; dc->dc_datalen = kdc->kdc_datalen; strncpy(dc->dc_descr, kdc->kdc_description, sizeof dc->dc_descr); dc->dc_descr[(sizeof dc->dc_descr) - 1] = '\0'; } static int sysctl_hw_devconfig SYSCTL_HANDLER_ARGS { int *name = (int *) arg1; u_int namelen = arg2; struct kern_devconf *kdc; struct devconf dc; int rv; /* all sysctl names at this level are terminal */ if (namelen != 1) return ENOTDIR; if (name[0] == DEVCONF_NUMBER) return sysctl_handle_int(oidp, 0, dc_lastnum, req); for(kdc = dc_list; kdc; kdc = kdc->kdc_next) { if(kdc->kdc_number == name[0]) break; } if(!kdc) return ENXIO; make_devconf(kdc, &dc); /* * Let the device specific externalization routines * handle the variable length data at the end of the * dc. Since the compiler may optimize alignment and * perform padding, we must do the subtraction to * determine the proper length. (dc_datalen includes the * externalized data so it can't be used) */ - rv = SYSCTL_OUT(req, &dc, (void *)&dc.dc_data - (void *)&dc); + rv = SYSCTL_OUT(req, &dc, (char *)&dc.dc_data - (char *)&dc); if(rv) return rv; if(kdc->kdc_externalize) rv = kdc->kdc_externalize(kdc, req); if(rv) return rv; if(!req->newptr) return 0; if(!kdc->kdc_internalize) return EOPNOTSUPP; rv = kdc->kdc_internalize(kdc, req); return rv; } SYSCTL_NODE(_hw, HW_DEVCONF, devconfig, CTLFLAG_RW, sysctl_hw_devconfig,""); Index: head/sys/kern/kern_sysctl.c =================================================================== --- head/sys/kern/kern_sysctl.c (revision 17970) +++ head/sys/kern/kern_sysctl.c (revision 17971) @@ -1,1075 +1,1076 @@ /*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Karels at Berkeley Software Design, Inc. * * Quite extensively rewritten by Poul-Henning Kamp of the FreeBSD * project, to make these variables more userfriendly. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)kern_sysctl.c 8.4 (Berkeley) 4/14/94 - * $Id: kern_sysctl.c,v 1.63 1996/06/06 17:17:54 phk Exp $ + * $Id: kern_sysctl.c,v 1.64 1996/06/10 16:23:30 nate Exp $ */ #include #include #include #include #include #include #include #include #include #include /* * Locking and stats */ static struct sysctl_lock { int sl_lock; int sl_want; int sl_locked; } memlock; static int sysctl_root SYSCTL_HANDLER_ARGS; extern struct linker_set sysctl_; /* * Initialization of the MIB tree. * * Order by number in each linker_set. */ static int sysctl_order_cmp(const void *a, const void *b) { const struct sysctl_oid **pa, **pb; pa = (const struct sysctl_oid **)a; pb = (const struct sysctl_oid **)b; if (*pa == NULL) return (1); if (*pb == NULL) return (-1); return ((*pa)->oid_number - (*pb)->oid_number); } static void sysctl_order(void *arg) { int j, k; struct linker_set *l = (struct linker_set *) arg; struct sysctl_oid **oidpp; /* First, find the highest oid we have */ j = l->ls_length; oidpp = (struct sysctl_oid **) l->ls_items; for (k = 0; j--; oidpp++) { if ((*oidpp)->oid_arg1 == arg) { *oidpp = 0; continue; } if (*oidpp && (*oidpp)->oid_number > k) k = (*oidpp)->oid_number; } /* Next, replace all OID_AUTO oids with new numbers */ j = l->ls_length; oidpp = (struct sysctl_oid **) l->ls_items; k += 100; for (; j--; oidpp++) if (*oidpp && (*oidpp)->oid_number == OID_AUTO) (*oidpp)->oid_number = k++; /* Finally: sort by oid */ j = l->ls_length; oidpp = (struct sysctl_oid **) l->ls_items; for (; j--; oidpp++) { if (!*oidpp) continue; if (((*oidpp)->oid_kind & CTLTYPE) == CTLTYPE_NODE) if (!(*oidpp)->oid_handler) sysctl_order((*oidpp)->oid_arg1); } qsort(l->ls_items, l->ls_length, sizeof l->ls_items[0], sysctl_order_cmp); } SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_order, &sysctl_); /* * "Staff-functions" * * These functions implement a presently undocumented interface * used by the sysctl program to walk the tree, and get the type * so it can print the value. * This interface is under work and consideration, and should probably * be killed with a big axe by the first person who can find the time. * (be aware though, that the proper interface isn't as obvious as it * may seem, there are various conflicting requirements. * * {0,0} printf the entire MIB-tree. * {0,1,...} return the name of the "..." OID. * {0,2,...} return the next OID. * {0,3} return the OID of the name in "new" * {0,4,...} return the kind & format info for the "..." OID. */ static void sysctl_sysctl_debug_dump_node(struct linker_set *l, int i) { int j, k; struct sysctl_oid **oidpp; j = l->ls_length; oidpp = (struct sysctl_oid **) l->ls_items; for (; j--; oidpp++) { if (!*oidpp) continue; for (k=0; koid_number, (*oidpp)->oid_name); printf("%c%c", (*oidpp)->oid_kind & CTLFLAG_RD ? 'R':' ', (*oidpp)->oid_kind & CTLFLAG_WR ? 'W':' '); if ((*oidpp)->oid_handler) printf(" *Handler"); switch ((*oidpp)->oid_kind & CTLTYPE) { case CTLTYPE_NODE: printf(" Node\n"); if (!(*oidpp)->oid_handler) { sysctl_sysctl_debug_dump_node( (*oidpp)->oid_arg1, i+2); } break; case CTLTYPE_INT: printf(" Int\n"); break; case CTLTYPE_STRING: printf(" String\n"); break; case CTLTYPE_QUAD: printf(" Quad\n"); break; case CTLTYPE_OPAQUE: printf(" Opaque/struct\n"); break; default: printf("\n"); } } } static int sysctl_sysctl_debug SYSCTL_HANDLER_ARGS { sysctl_sysctl_debug_dump_node(&sysctl_, 0); return ENOENT; } SYSCTL_PROC(_sysctl, 0, debug, CTLTYPE_STRING|CTLFLAG_RD, 0, 0, sysctl_sysctl_debug, "-", ""); static int sysctl_sysctl_name SYSCTL_HANDLER_ARGS { int *name = (int *) arg1; u_int namelen = arg2; int i, j, error = 0; struct sysctl_oid **oidpp; struct linker_set *lsp = &sysctl_; char buf[10]; while (namelen) { if (!lsp) { sprintf(buf,"%d",*name); if (req->oldidx) error = SYSCTL_OUT(req, ".", 1); if (!error) error = SYSCTL_OUT(req, buf, strlen(buf)); if (error) return (error); namelen--; name++; continue; } oidpp = (struct sysctl_oid **) lsp->ls_items; j = lsp->ls_length; lsp = 0; for (i = 0; i < j; i++, oidpp++) { if (*oidpp && ((*oidpp)->oid_number != *name)) continue; if (req->oldidx) error = SYSCTL_OUT(req, ".", 1); if (!error) error = SYSCTL_OUT(req, (*oidpp)->oid_name, strlen((*oidpp)->oid_name)); if (error) return (error); namelen--; name++; if (((*oidpp)->oid_kind & CTLTYPE) != CTLTYPE_NODE) break; if ((*oidpp)->oid_handler) break; lsp = (struct linker_set*)(*oidpp)->oid_arg1; break; } } return (SYSCTL_OUT(req, "", 1)); } SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, ""); static int sysctl_sysctl_next_ls (struct linker_set *lsp, int *name, u_int namelen, int *next, int *len, int level, struct sysctl_oid **oidp) { int i, j; struct sysctl_oid **oidpp; oidpp = (struct sysctl_oid **) lsp->ls_items; j = lsp->ls_length; *len = level; for (i = 0; i < j; i++, oidpp++) { if (!*oidpp) continue; *next = (*oidpp)->oid_number; *oidp = *oidpp; if (!namelen) { if (((*oidpp)->oid_kind & CTLTYPE) != CTLTYPE_NODE) return 0; if ((*oidpp)->oid_handler) /* We really should call the handler here...*/ return 0; lsp = (struct linker_set*)(*oidpp)->oid_arg1; if (!sysctl_sysctl_next_ls (lsp, 0, 0, next+1, len, level+1, oidp)) return 0; goto next; } if ((*oidpp)->oid_number < *name) continue; if ((*oidpp)->oid_number > *name) { if (((*oidpp)->oid_kind & CTLTYPE) != CTLTYPE_NODE) return 0; if ((*oidpp)->oid_handler) return 0; lsp = (struct linker_set*)(*oidpp)->oid_arg1; if (!sysctl_sysctl_next_ls (lsp, name+1, namelen-1, next+1, len, level+1, oidp)) return (0); goto next; } if (((*oidpp)->oid_kind & CTLTYPE) != CTLTYPE_NODE) continue; if ((*oidpp)->oid_handler) continue; lsp = (struct linker_set*)(*oidpp)->oid_arg1; if (!sysctl_sysctl_next_ls (lsp, name+1, namelen-1, next+1, len, level+1, oidp)) return (0); next: namelen = 1; *len = level; } return 1; } static int sysctl_sysctl_next SYSCTL_HANDLER_ARGS { int *name = (int *) arg1; u_int namelen = arg2; int i, j, error; struct sysctl_oid *oid; struct linker_set *lsp = &sysctl_; int newoid[CTL_MAXNAME]; i = sysctl_sysctl_next_ls (lsp, name, namelen, newoid, &j, 1, &oid); if (i) return ENOENT; error = SYSCTL_OUT(req, newoid, j * sizeof (int)); return (error); } SYSCTL_NODE(_sysctl, 2, next, CTLFLAG_RD, sysctl_sysctl_next, ""); static int name2oid (char *name, int *oid, int *len, struct sysctl_oid **oidp) { int i, j; struct sysctl_oid **oidpp; struct linker_set *lsp = &sysctl_; char *p; if (!*name) return ENOENT; p = name + strlen(name) - 1 ; if (*p == '.') *p = '\0'; *len = 0; for (p = name; *p && *p != '.'; p++) ; i = *p; if (i == '.') *p = '\0'; j = lsp->ls_length; oidpp = (struct sysctl_oid **) lsp->ls_items; while (j-- && *len < CTL_MAXNAME) { if (!*oidpp) continue; if (strcmp(name, (*oidpp)->oid_name)) { oidpp++; continue; } *oid++ = (*oidpp)->oid_number; (*len)++; if (!i) { if (oidp) *oidp = *oidpp; return (0); } if (((*oidpp)->oid_kind & CTLTYPE) != CTLTYPE_NODE) break; if ((*oidpp)->oid_handler) break; lsp = (struct linker_set*)(*oidpp)->oid_arg1; j = lsp->ls_length; oidpp = (struct sysctl_oid **)lsp->ls_items; name = p+1; for (p = name; *p && *p != '.'; p++) ; i = *p; if (i == '.') *p = '\0'; } return ENOENT; } static int sysctl_sysctl_name2oid SYSCTL_HANDLER_ARGS { char *p; int error, oid[CTL_MAXNAME], len; struct sysctl_oid *op = 0; if (!req->newlen) return ENOENT; p = malloc(req->newlen+1, M_SYSCTL, M_WAITOK); error = SYSCTL_IN(req, p, req->newlen); if (error) { free(p, M_SYSCTL); return (error); } p [req->newlen] = '\0'; error = name2oid(p, oid, &len, &op); free(p, M_SYSCTL); if (error) return (error); error = SYSCTL_OUT(req, oid, len * sizeof *oid); return (error); } SYSCTL_PROC(_sysctl, 3, name2oid, CTLFLAG_RW|CTLFLAG_ANYBODY, 0, 0, sysctl_sysctl_name2oid, "I", ""); static int sysctl_sysctl_oidfmt SYSCTL_HANDLER_ARGS { int *name = (int *) arg1, error; u_int namelen = arg2; int indx, j; struct sysctl_oid **oidpp; struct linker_set *lsp = &sysctl_; j = lsp->ls_length; oidpp = (struct sysctl_oid **) lsp->ls_items; indx = 0; while (j-- && indx < CTL_MAXNAME) { if (*oidpp && ((*oidpp)->oid_number == name[indx])) { indx++; if (((*oidpp)->oid_kind & CTLTYPE) == CTLTYPE_NODE) { if ((*oidpp)->oid_handler) goto found; if (indx == namelen) goto found; lsp = (struct linker_set*)(*oidpp)->oid_arg1; j = lsp->ls_length; oidpp = (struct sysctl_oid **)lsp->ls_items; } else { if (indx != namelen) return EISDIR; goto found; } } else { oidpp++; } } return ENOENT; found: if (!(*oidpp)->oid_fmt) return ENOENT; error = SYSCTL_OUT(req, &(*oidpp)->oid_kind, sizeof((*oidpp)->oid_kind)); if (!error) error = SYSCTL_OUT(req, (*oidpp)->oid_fmt, strlen((*oidpp)->oid_fmt)+1); return (error); } SYSCTL_NODE(_sysctl, 4, oidfmt, CTLFLAG_RD, sysctl_sysctl_oidfmt, ""); /* * Default "handler" functions. */ /* * Handle an integer, signed or unsigned. * Two cases: * a variable: point arg1 at it. * a constant: pass it in arg2. */ int sysctl_handle_int SYSCTL_HANDLER_ARGS { int error = 0; if (arg1) error = SYSCTL_OUT(req, arg1, sizeof(int)); else if (arg2) error = SYSCTL_OUT(req, &arg2, sizeof(int)); if (error || !req->newptr) return (error); if (!arg1) error = EPERM; else error = SYSCTL_IN(req, arg1, sizeof(int)); return (error); } /* * Handle our generic '\0' terminated 'C' string. * Two cases: * a variable string: point arg1 at it, arg2 is max length. * a constant string: point arg1 at it, arg2 is zero. */ int sysctl_handle_string SYSCTL_HANDLER_ARGS { int error=0; error = SYSCTL_OUT(req, arg1, strlen((char *)arg1)+1); if (error || !req->newptr || !arg2) return (error); if ((req->newlen - req->newidx) > arg2) { error = E2BIG; } else { arg2 = (req->newlen - req->newidx); error = SYSCTL_IN(req, arg1, arg2); ((char *)arg1)[arg2] = '\0'; } return (error); } /* * Handle any kind of opaque data. * arg1 points to it, arg2 is the size. */ int sysctl_handle_opaque SYSCTL_HANDLER_ARGS { int error; error = SYSCTL_OUT(req, arg1, arg2); if (error || !req->newptr) return (error); error = SYSCTL_IN(req, arg1, arg2); return (error); } /* * Transfer functions to/from kernel space. * XXX: rather untested at this point */ static int sysctl_old_kernel(struct sysctl_req *req, const void *p, int l) { int i = 0; if (req->oldptr) { i = min(req->oldlen - req->oldidx, l); if (i > 0) - bcopy(p, req->oldptr + req->oldidx, i); + bcopy(p, (char *)req->oldptr + req->oldidx, i); } req->oldidx += l; if (req->oldptr && i != l) return (ENOMEM); return (0); } static int sysctl_new_kernel(struct sysctl_req *req, void *p, int l) { if (!req->newptr) return 0; if (req->newlen - req->newidx < l) return (EINVAL); - bcopy(req->newptr + req->newidx, p, l); + bcopy((char *)req->newptr + req->newidx, p, l); req->newidx += l; return (0); } int kernel_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen, int *retval) { int error = 0; struct sysctl_req req; bzero(&req, sizeof req); req.p = p; if (oldlenp) { req.oldlen = *oldlenp; } if (old) { req.oldptr= old; } if (newlen) { req.newlen = newlen; req.newptr = new; } req.oldfunc = sysctl_old_kernel; req.newfunc = sysctl_new_kernel; req.lock = 1; /* XXX this should probably be done in a general way */ while (memlock.sl_lock) { memlock.sl_want = 1; (void) tsleep((caddr_t)&memlock, PRIBIO+1, "sysctl", 0); memlock.sl_locked++; } memlock.sl_lock = 1; error = sysctl_root(0, name, namelen, &req); if (req.lock == 2) vsunlock(req.oldptr, req.oldlen, B_WRITE); memlock.sl_lock = 0; if (memlock.sl_want) { memlock.sl_want = 0; wakeup((caddr_t)&memlock); } if (error && error != ENOMEM) return (error); if (retval) { if (req.oldptr && req.oldidx > req.oldlen) *retval = req.oldlen; else *retval = req.oldidx; } return (error); } /* * Transfer function to/from user space. */ static int sysctl_old_user(struct sysctl_req *req, const void *p, int l) { int error = 0, i = 0; if (req->lock == 1 && req->oldptr) { vslock(req->oldptr, req->oldlen); req->lock = 2; } if (req->oldptr) { i = min(req->oldlen - req->oldidx, l); if (i > 0) - error = copyout(p, req->oldptr + req->oldidx, i); + error = copyout(p, (char *)req->oldptr + req->oldidx, + i); } req->oldidx += l; if (error) return (error); if (req->oldptr && i < l) return (ENOMEM); return (0); } static int sysctl_new_user(struct sysctl_req *req, void *p, int l) { int error; if (!req->newptr) return 0; if (req->newlen - req->newidx < l) return (EINVAL); - error = copyin(req->newptr + req->newidx, p, l); + error = copyin((char *)req->newptr + req->newidx, p, l); req->newidx += l; return (error); } /* * Traverse our tree, and find the right node, execute whatever it points * at, and return the resulting error code. */ int sysctl_root SYSCTL_HANDLER_ARGS { int *name = (int *) arg1; u_int namelen = arg2; int indx, i, j; struct sysctl_oid **oidpp; struct linker_set *lsp = &sysctl_; j = lsp->ls_length; oidpp = (struct sysctl_oid **) lsp->ls_items; indx = 0; while (j-- && indx < CTL_MAXNAME) { if (*oidpp && ((*oidpp)->oid_number == name[indx])) { indx++; if ((*oidpp)->oid_kind & CTLFLAG_NOLOCK) req->lock = 0; if (((*oidpp)->oid_kind & CTLTYPE) == CTLTYPE_NODE) { if ((*oidpp)->oid_handler) goto found; if (indx == namelen) return ENOENT; lsp = (struct linker_set*)(*oidpp)->oid_arg1; j = lsp->ls_length; oidpp = (struct sysctl_oid **)lsp->ls_items; } else { if (indx != namelen) return EISDIR; goto found; } } else { oidpp++; } } return ENOENT; found: /* If writing isn't allowed */ if (req->newptr && !((*oidpp)->oid_kind & CTLFLAG_WR)) return (EPERM); /* Most likely only root can write */ if (!((*oidpp)->oid_kind & CTLFLAG_ANYBODY) && req->newptr && req->p && (i = suser(req->p->p_ucred, &req->p->p_acflag))) return (i); if (!(*oidpp)->oid_handler) return EINVAL; if (((*oidpp)->oid_kind & CTLTYPE) == CTLTYPE_NODE) { i = ((*oidpp)->oid_handler) (*oidpp, name + indx, namelen - indx, req); } else { i = ((*oidpp)->oid_handler) (*oidpp, (*oidpp)->oid_arg1, (*oidpp)->oid_arg2, req); } return (i); } #ifndef _SYS_SYSPROTO_H_ struct sysctl_args { int *name; u_int namelen; void *old; size_t *oldlenp; void *new; size_t newlen; }; #endif int __sysctl(struct proc *p, struct sysctl_args *uap, int *retval) { int error, i, j, name[CTL_MAXNAME]; if (uap->namelen > CTL_MAXNAME || uap->namelen < 2) return (EINVAL); error = copyin(uap->name, &name, uap->namelen * sizeof(int)); if (error) return (error); error = userland_sysctl(p, name, uap->namelen, uap->old, uap->oldlenp, 0, uap->new, uap->newlen, &j); if (error && error != ENOMEM) return (error); if (uap->oldlenp) { i = copyout(&j, uap->oldlenp, sizeof(j)); if (i) return (i); } return (error); } /* * This is used from various compatibility syscalls too. That's why name * must be in kernel space. */ int userland_sysctl(struct proc *p, int *name, u_int namelen, void *old, size_t *oldlenp, int inkernel, void *new, size_t newlen, int *retval) { int error = 0; struct sysctl_req req, req2; bzero(&req, sizeof req); req.p = p; if (oldlenp) { if (inkernel) { req.oldlen = *oldlenp; } else { error = copyin(oldlenp, &req.oldlen, sizeof(*oldlenp)); if (error) return (error); } } if (old) { if (!useracc(old, req.oldlen, B_WRITE)) return (EFAULT); req.oldptr= old; } if (newlen) { if (!useracc(new, req.newlen, B_READ)) return (EFAULT); req.newlen = newlen; req.newptr = new; } req.oldfunc = sysctl_old_user; req.newfunc = sysctl_new_user; req.lock = 1; /* XXX this should probably be done in a general way */ while (memlock.sl_lock) { memlock.sl_want = 1; (void) tsleep((caddr_t)&memlock, PRIBIO+1, "sysctl", 0); memlock.sl_locked++; } memlock.sl_lock = 1; do { req2 = req; error = sysctl_root(0, name, namelen, &req2); } while (error == EAGAIN); req = req2; if (req.lock == 2) vsunlock(req.oldptr, req.oldlen, B_WRITE); memlock.sl_lock = 0; if (memlock.sl_want) { memlock.sl_want = 0; wakeup((caddr_t)&memlock); } if (error && error != ENOMEM) return (error); if (retval) { if (req.oldptr && req.oldidx > req.oldlen) *retval = req.oldlen; else *retval = req.oldidx; } return (error); } #ifdef COMPAT_43 #include #include #define KINFO_PROC (0<<8) #define KINFO_RT (1<<8) #define KINFO_VNODE (2<<8) #define KINFO_FILE (3<<8) #define KINFO_METER (4<<8) #define KINFO_LOADAVG (5<<8) #define KINFO_CLOCKRATE (6<<8) /* Non-standard BSDI extension - only present on their 4.3 net-2 releases */ #define KINFO_BSDI_SYSINFO (101<<8) /* * XXX this is bloat, but I hope it's better here than on the potentially * limited kernel stack... -Peter */ static struct { int bsdi_machine; /* "i386" on BSD/386 */ /* ^^^ this is an offset to the string, relative to the struct start */ char *pad0; long pad1; long pad2; long pad3; u_long pad4; u_long pad5; u_long pad6; int bsdi_ostype; /* "BSD/386" on BSD/386 */ int bsdi_osrelease; /* "1.1" on BSD/386 */ long pad7; long pad8; char *pad9; long pad10; long pad11; int pad12; long pad13; quad_t pad14; long pad15; struct timeval pad16; /* we dont set this, because BSDI's uname used gethostname() instead */ int bsdi_hostname; /* hostname on BSD/386 */ /* the actual string data is appended here */ } bsdi_si; /* * this data is appended to the end of the bsdi_si structure during copyout. * The "char *" offsets are relative to the base of the bsdi_si struct. * This contains "FreeBSD\02.0-BUILT-nnnnnn\0i386\0", and these strings * should not exceed the length of the buffer here... (or else!! :-) */ static char bsdi_strings[80]; /* It had better be less than this! */ #ifndef _SYS_SYSPROTO_H_ struct getkerninfo_args { int op; char *where; int *size; int arg; }; #endif int ogetkerninfo(struct proc *p, struct getkerninfo_args *uap, int *retval) { int error, name[6]; u_int size; switch (uap->op & 0xff00) { case KINFO_RT: name[0] = CTL_NET; name[1] = PF_ROUTE; name[2] = 0; name[3] = (uap->op & 0xff0000) >> 16; name[4] = uap->op & 0xff; name[5] = uap->arg; error = userland_sysctl(p, name, 6, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_VNODE: name[0] = CTL_KERN; name[1] = KERN_VNODE; error = userland_sysctl(p, name, 2, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_PROC: name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = uap->op & 0xff; name[3] = uap->arg; error = userland_sysctl(p, name, 4, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_FILE: name[0] = CTL_KERN; name[1] = KERN_FILE; error = userland_sysctl(p, name, 2, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_METER: name[0] = CTL_VM; name[1] = VM_METER; error = userland_sysctl(p, name, 2, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_LOADAVG: name[0] = CTL_VM; name[1] = VM_LOADAVG; error = userland_sysctl(p, name, 2, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_CLOCKRATE: name[0] = CTL_KERN; name[1] = KERN_CLOCKRATE; error = userland_sysctl(p, name, 2, uap->where, uap->size, 0, 0, 0, &size); break; case KINFO_BSDI_SYSINFO: { /* * this is pretty crude, but it's just enough for uname() * from BSDI's 1.x libc to work. * * In particular, it doesn't return the same results when * the supplied buffer is too small. BSDI's version apparently * will return the amount copied, and set the *size to how * much was needed. The emulation framework here isn't capable * of that, so we just set both to the amount copied. * BSDI's 2.x product apparently fails with ENOMEM in this * scenario. */ u_int needed; u_int left; char *s; bzero((char *)&bsdi_si, sizeof(bsdi_si)); bzero(bsdi_strings, sizeof(bsdi_strings)); s = bsdi_strings; bsdi_si.bsdi_ostype = (s - bsdi_strings) + sizeof(bsdi_si); strcpy(s, ostype); s += strlen(s) + 1; bsdi_si.bsdi_osrelease = (s - bsdi_strings) + sizeof(bsdi_si); strcpy(s, osrelease); s += strlen(s) + 1; bsdi_si.bsdi_machine = (s - bsdi_strings) + sizeof(bsdi_si); strcpy(s, machine); s += strlen(s) + 1; needed = sizeof(bsdi_si) + (s - bsdi_strings); if (uap->where == NULL) { /* process is asking how much buffer to supply.. */ size = needed; error = 0; break; } /* if too much buffer supplied, trim it down */ if (size > needed) size = needed; /* how much of the buffer is remaining */ left = size; if ((error = copyout((char *)&bsdi_si, uap->where, left)) != 0) break; /* is there any point in continuing? */ if (left > sizeof(bsdi_si)) { left -= sizeof(bsdi_si); error = copyout(&bsdi_strings, uap->where + sizeof(bsdi_si), left); } break; } default: return (EOPNOTSUPP); } if (error) return (error); *retval = size; if (uap->size) error = copyout((caddr_t)&size, (caddr_t)uap->size, sizeof(size)); return (error); } #endif /* COMPAT_43 */ Index: head/sys/kern/sysv_msg.c =================================================================== --- head/sys/kern/sysv_msg.c (revision 17970) +++ head/sys/kern/sysv_msg.c (revision 17971) @@ -1,1034 +1,1034 @@ -/* $Id: sysv_msg.c,v 1.11 1995/12/15 05:00:27 peter Exp $ */ +/* $Id: sysv_msg.c,v 1.12 1996/01/05 16:37:56 wollman Exp $ */ /* * Implementation of SVID messages * * Author: Daniel Boulet * * Copyright 1993 Daniel Boulet and RTMX Inc. * * This system call was implemented by Daniel Boulet under contract from RTMX. * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. */ #include "opt_sysvipc.h" #include #include #include #include #include #include #include static void msginit __P((void *)); SYSINIT(sysv_msg, SI_SUB_SYSV_MSG, SI_ORDER_FIRST, msginit, NULL) #define MSG_DEBUG #undef MSG_DEBUG_OK #ifndef _SYS_SYSPROTO_H_ struct msgctl_args; int msgctl __P((struct proc *p, struct msgctl_args *uap, int *retval)); struct msgget_args; int msgget __P((struct proc *p, struct msgget_args *uap, int *retval)); struct msgsnd_args; int msgsnd __P((struct proc *p, struct msgsnd_args *uap, int *retval)); struct msgrcv_args; int msgrcv __P((struct proc *p, struct msgrcv_args *uap, int *retval)); #endif static void msg_freehdr __P((struct msg *msghdr)); /* XXX casting to (sy_call_t *) is bogus, as usual. */ static sy_call_t *msgcalls[] = { (sy_call_t *)msgctl, (sy_call_t *)msgget, (sy_call_t *)msgsnd, (sy_call_t *)msgrcv }; static int nfree_msgmaps; /* # of free map entries */ static short free_msgmaps; /* head of linked list of free map entries */ static struct msg *free_msghdrs; /* list of free msg headers */ char *msgpool; /* MSGMAX byte long msg buffer pool */ struct msgmap *msgmaps; /* MSGSEG msgmap structures */ struct msg *msghdrs; /* MSGTQL msg headers */ struct msqid_ds *msqids; /* MSGMNI msqid_ds struct's */ void msginit(dummy) void *dummy; { register int i; /* * msginfo.msgssz should be a power of two for efficiency reasons. * It is also pretty silly if msginfo.msgssz is less than 8 * or greater than about 256 so ... */ i = 8; while (i < 1024 && i != msginfo.msgssz) i <<= 1; if (i != msginfo.msgssz) { printf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz, msginfo.msgssz); panic("msginfo.msgssz not a small power of 2"); } if (msginfo.msgseg > 32767) { printf("msginfo.msgseg=%d\n", msginfo.msgseg); panic("msginfo.msgseg > 32767"); } if (msgmaps == NULL) panic("msgmaps is NULL"); for (i = 0; i < msginfo.msgseg; i++) { if (i > 0) msgmaps[i-1].next = i; msgmaps[i].next = -1; /* implies entry is available */ } free_msgmaps = 0; nfree_msgmaps = msginfo.msgseg; if (msghdrs == NULL) panic("msghdrs is NULL"); for (i = 0; i < msginfo.msgtql; i++) { msghdrs[i].msg_type = 0; if (i > 0) msghdrs[i-1].msg_next = &msghdrs[i]; msghdrs[i].msg_next = NULL; } free_msghdrs = &msghdrs[0]; if (msqids == NULL) panic("msqids is NULL"); for (i = 0; i < msginfo.msgmni; i++) { msqids[i].msg_qbytes = 0; /* implies entry is available */ msqids[i].msg_perm.seq = 0; /* reset to a known value */ } } /* * Entry point for all MSG calls */ int msgsys(p, uap, retval) struct proc *p; /* XXX actually varargs. */ struct msgsys_args /* { u_int which; int a2; int a3; int a4; int a5; int a6; } */ *uap; int *retval; { if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0])) return (EINVAL); return ((*msgcalls[uap->which])(p, &uap->a2, retval)); } static void msg_freehdr(msghdr) struct msg *msghdr; { while (msghdr->msg_ts > 0) { short next; if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) panic("msghdr->msg_spot out of range"); next = msgmaps[msghdr->msg_spot].next; msgmaps[msghdr->msg_spot].next = free_msgmaps; free_msgmaps = msghdr->msg_spot; nfree_msgmaps++; msghdr->msg_spot = next; if (msghdr->msg_ts >= msginfo.msgssz) msghdr->msg_ts -= msginfo.msgssz; else msghdr->msg_ts = 0; } if (msghdr->msg_spot != -1) panic("msghdr->msg_spot != -1"); msghdr->msg_next = free_msghdrs; free_msghdrs = msghdr; } #ifndef _SYS_SYSPROTO_H_ struct msgctl_args { int msqid; int cmd; struct msqid_ds *buf; }; #endif int msgctl(p, uap, retval) struct proc *p; register struct msgctl_args *uap; int *retval; { int msqid = uap->msqid; int cmd = uap->cmd; struct msqid_ds *user_msqptr = uap->buf; struct ucred *cred = p->p_ucred; int rval, eval; struct msqid_ds msqbuf; register struct msqid_ds *msqptr; #ifdef MSG_DEBUG_OK printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr); #endif msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { #ifdef MSG_DEBUG_OK printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni); #endif return(EINVAL); } msqptr = &msqids[msqid]; if (msqptr->msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("no such msqid\n"); #endif return(EINVAL); } if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK printf("wrong sequence number\n"); #endif return(EINVAL); } eval = 0; rval = 0; switch (cmd) { case IPC_RMID: { struct msg *msghdr; if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M))) return(eval); /* Free the message headers */ msghdr = msqptr->msg_first; while (msghdr != NULL) { struct msg *msghdr_tmp; /* Free the segments of each message */ msqptr->msg_cbytes -= msghdr->msg_ts; msqptr->msg_qnum--; msghdr_tmp = msghdr; msghdr = msghdr->msg_next; msg_freehdr(msghdr_tmp); } if (msqptr->msg_cbytes != 0) panic("msg_cbytes is screwed up"); if (msqptr->msg_qnum != 0) panic("msg_qnum is screwed up"); msqptr->msg_qbytes = 0; /* Mark it as free */ wakeup((caddr_t)msqptr); } break; case IPC_SET: if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M))) return(eval); if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0) return(eval); if (msqbuf.msg_qbytes > msqptr->msg_qbytes && cred->cr_uid != 0) return(EPERM); if (msqbuf.msg_qbytes > msginfo.msgmnb) { #ifdef MSG_DEBUG_OK printf("can't increase msg_qbytes beyond %d (truncating)\n", msginfo.msgmnb); #endif msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ } if (msqbuf.msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("can't reduce msg_qbytes to 0\n"); #endif return(EINVAL); /* non-standard errno! */ } msqptr->msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ msqptr->msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | (msqbuf.msg_perm.mode & 0777); msqptr->msg_qbytes = msqbuf.msg_qbytes; msqptr->msg_ctime = time.tv_sec; break; case IPC_STAT: if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { #ifdef MSG_DEBUG_OK printf("requester doesn't have read access\n"); #endif return(eval); } eval = copyout((caddr_t)msqptr, user_msqptr, sizeof(struct msqid_ds)); break; default: #ifdef MSG_DEBUG_OK printf("invalid command %d\n", cmd); #endif return(EINVAL); } if (eval == 0) *retval = rval; return(eval); } #ifndef _SYS_SYSPROTO_H_ struct msgget_args { key_t key; int msgflg; }; #endif int msgget(p, uap, retval) struct proc *p; register struct msgget_args *uap; int *retval; { int msqid, eval; int key = uap->key; int msgflg = uap->msgflg; struct ucred *cred = p->p_ucred; register struct msqid_ds *msqptr = NULL; #ifdef MSG_DEBUG_OK printf("msgget(0x%x, 0%o)\n", key, msgflg); #endif if (key != IPC_PRIVATE) { for (msqid = 0; msqid < msginfo.msgmni; msqid++) { msqptr = &msqids[msqid]; if (msqptr->msg_qbytes != 0 && msqptr->msg_perm.key == key) break; } if (msqid < msginfo.msgmni) { #ifdef MSG_DEBUG_OK printf("found public key\n"); #endif if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { #ifdef MSG_DEBUG_OK printf("not exclusive\n"); #endif return(EEXIST); } if ((eval = ipcperm(cred, &msqptr->msg_perm, msgflg & 0700 ))) { #ifdef MSG_DEBUG_OK printf("requester doesn't have 0%o access\n", msgflg & 0700); #endif return(eval); } goto found; } } #ifdef MSG_DEBUG_OK printf("need to allocate the msqid_ds\n"); #endif if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { for (msqid = 0; msqid < msginfo.msgmni; msqid++) { /* * Look for an unallocated and unlocked msqid_ds. * msqid_ds's can be locked by msgsnd or msgrcv while * they are copying the message in/out. We can't * re-use the entry until they release it. */ msqptr = &msqids[msqid]; if (msqptr->msg_qbytes == 0 && (msqptr->msg_perm.mode & MSG_LOCKED) == 0) break; } if (msqid == msginfo.msgmni) { #ifdef MSG_DEBUG_OK printf("no more msqid_ds's available\n"); #endif return(ENOSPC); } #ifdef MSG_DEBUG_OK printf("msqid %d is available\n", msqid); #endif msqptr->msg_perm.key = key; msqptr->msg_perm.cuid = cred->cr_uid; msqptr->msg_perm.uid = cred->cr_uid; msqptr->msg_perm.cgid = cred->cr_gid; msqptr->msg_perm.gid = cred->cr_gid; msqptr->msg_perm.mode = (msgflg & 0777); /* Make sure that the returned msqid is unique */ msqptr->msg_perm.seq++; msqptr->msg_first = NULL; msqptr->msg_last = NULL; msqptr->msg_cbytes = 0; msqptr->msg_qnum = 0; msqptr->msg_qbytes = msginfo.msgmnb; msqptr->msg_lspid = 0; msqptr->msg_lrpid = 0; msqptr->msg_stime = 0; msqptr->msg_rtime = 0; msqptr->msg_ctime = time.tv_sec; } else { #ifdef MSG_DEBUG_OK printf("didn't find it and wasn't asked to create it\n"); #endif return(ENOENT); } found: /* Construct the unique msqid */ *retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); return(0); } #ifndef _SYS_SYSPROTO_H_ struct msgsnd_args { int msqid; void *msgp; size_t msgsz; int msgflg; }; #endif int msgsnd(p, uap, retval) struct proc *p; register struct msgsnd_args *uap; int *retval; { int msqid = uap->msqid; void *user_msgp = uap->msgp; size_t msgsz = uap->msgsz; int msgflg = uap->msgflg; int segs_needed, eval; struct ucred *cred = p->p_ucred; register struct msqid_ds *msqptr; register struct msg *msghdr; short next; #ifdef MSG_DEBUG_OK printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz, msgflg); #endif msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { #ifdef MSG_DEBUG_OK printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni); #endif return(EINVAL); } msqptr = &msqids[msqid]; if (msqptr->msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("no such message queue id\n"); #endif return(EINVAL); } if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK printf("wrong sequence number\n"); #endif return(EINVAL); } if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_W))) { #ifdef MSG_DEBUG_OK printf("requester doesn't have write access\n"); #endif return(eval); } segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; #ifdef MSG_DEBUG_OK printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, segs_needed); #endif for (;;) { int need_more_resources = 0; /* * check msgsz * (inside this loop in case msg_qbytes changes while we sleep) */ if (msgsz > msqptr->msg_qbytes) { #ifdef MSG_DEBUG_OK printf("msgsz > msqptr->msg_qbytes\n"); #endif return(EINVAL); } if (msqptr->msg_perm.mode & MSG_LOCKED) { #ifdef MSG_DEBUG_OK printf("msqid is locked\n"); #endif need_more_resources = 1; } if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { #ifdef MSG_DEBUG_OK printf("msgsz + msg_cbytes > msg_qbytes\n"); #endif need_more_resources = 1; } if (segs_needed > nfree_msgmaps) { #ifdef MSG_DEBUG_OK printf("segs_needed > nfree_msgmaps\n"); #endif need_more_resources = 1; } if (free_msghdrs == NULL) { #ifdef MSG_DEBUG_OK printf("no more msghdrs\n"); #endif need_more_resources = 1; } if (need_more_resources) { int we_own_it; if ((msgflg & IPC_NOWAIT) != 0) { #ifdef MSG_DEBUG_OK printf("need more resources but caller doesn't want to wait\n"); #endif return(EAGAIN); } if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { #ifdef MSG_DEBUG_OK printf("we don't own the msqid_ds\n"); #endif we_own_it = 0; } else { /* Force later arrivals to wait for our request */ #ifdef MSG_DEBUG_OK printf("we own the msqid_ds\n"); #endif msqptr->msg_perm.mode |= MSG_LOCKED; we_own_it = 1; } #ifdef MSG_DEBUG_OK printf("goodnight\n"); #endif eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait", 0); #ifdef MSG_DEBUG_OK printf("good morning, eval=%d\n", eval); #endif if (we_own_it) msqptr->msg_perm.mode &= ~MSG_LOCKED; if (eval != 0) { #ifdef MSG_DEBUG_OK printf("msgsnd: interrupted system call\n"); #endif return(EINTR); } /* * Make sure that the msq queue still exists */ if (msqptr->msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("msqid deleted\n"); #endif /* The SVID says to return EIDRM. */ #ifdef EIDRM return(EIDRM); #else /* Unfortunately, BSD doesn't define that code yet! */ return(EINVAL); #endif } } else { #ifdef MSG_DEBUG_OK printf("got all the resources that we need\n"); #endif break; } } /* * We have the resources that we need. * Make sure! */ if (msqptr->msg_perm.mode & MSG_LOCKED) panic("msg_perm.mode & MSG_LOCKED"); if (segs_needed > nfree_msgmaps) panic("segs_needed > nfree_msgmaps"); if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) panic("msgsz + msg_cbytes > msg_qbytes"); if (free_msghdrs == NULL) panic("no more msghdrs"); /* * Re-lock the msqid_ds in case we page-fault when copying in the * message */ if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) panic("msqid_ds is already locked"); msqptr->msg_perm.mode |= MSG_LOCKED; /* * Allocate a message header */ msghdr = free_msghdrs; free_msghdrs = msghdr->msg_next; msghdr->msg_spot = -1; msghdr->msg_ts = msgsz; /* * Allocate space for the message */ while (segs_needed > 0) { if (nfree_msgmaps <= 0) panic("not enough msgmaps"); if (free_msgmaps == -1) panic("nil free_msgmaps"); next = free_msgmaps; if (next <= -1) panic("next too low #1"); if (next >= msginfo.msgseg) panic("next out of range #1"); #ifdef MSG_DEBUG_OK printf("allocating segment %d to message\n", next); #endif free_msgmaps = msgmaps[next].next; nfree_msgmaps--; msgmaps[next].next = msghdr->msg_spot; msghdr->msg_spot = next; segs_needed--; } /* * Copy in the message type */ if ((eval = copyin(user_msgp, &msghdr->msg_type, sizeof(msghdr->msg_type))) != 0) { #ifdef MSG_DEBUG_OK printf("error %d copying the message type\n", eval); #endif msg_freehdr(msghdr); msqptr->msg_perm.mode &= ~MSG_LOCKED; wakeup((caddr_t)msqptr); return(eval); } - user_msgp += sizeof(msghdr->msg_type); + user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); /* * Validate the message type */ if (msghdr->msg_type < 1) { msg_freehdr(msghdr); msqptr->msg_perm.mode &= ~MSG_LOCKED; wakeup((caddr_t)msqptr); #ifdef MSG_DEBUG_OK printf("mtype (%d) < 1\n", msghdr->msg_type); #endif return(EINVAL); } /* * Copy in the message body */ next = msghdr->msg_spot; while (msgsz > 0) { size_t tlen; if (msgsz > msginfo.msgssz) tlen = msginfo.msgssz; else tlen = msgsz; if (next <= -1) panic("next too low #2"); if (next >= msginfo.msgseg) panic("next out of range #2"); if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], tlen)) != 0) { #ifdef MSG_DEBUG_OK printf("error %d copying in message segment\n", eval); #endif msg_freehdr(msghdr); msqptr->msg_perm.mode &= ~MSG_LOCKED; wakeup((caddr_t)msqptr); return(eval); } msgsz -= tlen; - user_msgp += tlen; + user_msgp = (char *)user_msgp + tlen; next = msgmaps[next].next; } if (next != -1) panic("didn't use all the msg segments"); /* * We've got the message. Unlock the msqid_ds. */ msqptr->msg_perm.mode &= ~MSG_LOCKED; /* * Make sure that the msqid_ds is still allocated. */ if (msqptr->msg_qbytes == 0) { msg_freehdr(msghdr); wakeup((caddr_t)msqptr); /* The SVID says to return EIDRM. */ #ifdef EIDRM return(EIDRM); #else /* Unfortunately, BSD doesn't define that code yet! */ return(EINVAL); #endif } /* * Put the message into the queue */ if (msqptr->msg_first == NULL) { msqptr->msg_first = msghdr; msqptr->msg_last = msghdr; } else { msqptr->msg_last->msg_next = msghdr; msqptr->msg_last = msghdr; } msqptr->msg_last->msg_next = NULL; msqptr->msg_cbytes += msghdr->msg_ts; msqptr->msg_qnum++; msqptr->msg_lspid = p->p_pid; msqptr->msg_stime = time.tv_sec; wakeup((caddr_t)msqptr); *retval = 0; return(0); } #ifndef _SYS_SYSPROTO_H_ struct msgrcv_args { int msqid; void *msgp; size_t msgsz; long msgtyp; int msgflg; }; #endif int msgrcv(p, uap, retval) struct proc *p; register struct msgrcv_args *uap; int *retval; { int msqid = uap->msqid; void *user_msgp = uap->msgp; size_t msgsz = uap->msgsz; long msgtyp = uap->msgtyp; int msgflg = uap->msgflg; size_t len; struct ucred *cred = p->p_ucred; register struct msqid_ds *msqptr; register struct msg *msghdr; int eval; short next; #ifdef MSG_DEBUG_OK printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp, msgsz, msgtyp, msgflg); #endif msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { #ifdef MSG_DEBUG_OK printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni); #endif return(EINVAL); } msqptr = &msqids[msqid]; if (msqptr->msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("no such message queue id\n"); #endif return(EINVAL); } if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK printf("wrong sequence number\n"); #endif return(EINVAL); } if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { #ifdef MSG_DEBUG_OK printf("requester doesn't have read access\n"); #endif return(eval); } msghdr = NULL; while (msghdr == NULL) { if (msgtyp == 0) { msghdr = msqptr->msg_first; if (msghdr != NULL) { if (msgsz < msghdr->msg_ts && (msgflg & MSG_NOERROR) == 0) { #ifdef MSG_DEBUG_OK printf("first message on the queue is too big (want %d, got %d)\n", msgsz, msghdr->msg_ts); #endif return(E2BIG); } if (msqptr->msg_first == msqptr->msg_last) { msqptr->msg_first = NULL; msqptr->msg_last = NULL; } else { msqptr->msg_first = msghdr->msg_next; if (msqptr->msg_first == NULL) panic("msg_first/last screwed up #1"); } } } else { struct msg *previous; struct msg **prev; previous = NULL; prev = &(msqptr->msg_first); while ((msghdr = *prev) != NULL) { /* * Is this message's type an exact match or is * this message's type less than or equal to * the absolute value of a negative msgtyp? * Note that the second half of this test can * NEVER be true if msgtyp is positive since * msg_type is always positive! */ if (msgtyp == msghdr->msg_type || msghdr->msg_type <= -msgtyp) { #ifdef MSG_DEBUG_OK printf("found message type %d, requested %d\n", msghdr->msg_type, msgtyp); #endif if (msgsz < msghdr->msg_ts && (msgflg & MSG_NOERROR) == 0) { #ifdef MSG_DEBUG_OK printf("requested message on the queue is too big (want %d, got %d)\n", msgsz, msghdr->msg_ts); #endif return(E2BIG); } *prev = msghdr->msg_next; if (msghdr == msqptr->msg_last) { if (previous == NULL) { if (prev != &msqptr->msg_first) panic("msg_first/last screwed up #2"); msqptr->msg_first = NULL; msqptr->msg_last = NULL; } else { if (prev == &msqptr->msg_first) panic("msg_first/last screwed up #3"); msqptr->msg_last = previous; } } break; } previous = msghdr; prev = &(msghdr->msg_next); } } /* * We've either extracted the msghdr for the appropriate * message or there isn't one. * If there is one then bail out of this loop. */ if (msghdr != NULL) break; /* * Hmph! No message found. Does the user want to wait? */ if ((msgflg & IPC_NOWAIT) != 0) { #ifdef MSG_DEBUG_OK printf("no appropriate message found (msgtyp=%d)\n", msgtyp); #endif /* The SVID says to return ENOMSG. */ #ifdef ENOMSG return(ENOMSG); #else /* Unfortunately, BSD doesn't define that code yet! */ return(EAGAIN); #endif } /* * Wait for something to happen */ #ifdef MSG_DEBUG_OK printf("msgrcv: goodnight\n"); #endif eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait", 0); #ifdef MSG_DEBUG_OK printf("msgrcv: good morning (eval=%d)\n", eval); #endif if (eval != 0) { #ifdef MSG_DEBUG_OK printf("msgsnd: interrupted system call\n"); #endif return(EINTR); } /* * Make sure that the msq queue still exists */ if (msqptr->msg_qbytes == 0 || msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK printf("msqid deleted\n"); #endif /* The SVID says to return EIDRM. */ #ifdef EIDRM return(EIDRM); #else /* Unfortunately, BSD doesn't define that code yet! */ return(EINVAL); #endif } } /* * Return the message to the user. * * First, do the bookkeeping (before we risk being interrupted). */ msqptr->msg_cbytes -= msghdr->msg_ts; msqptr->msg_qnum--; msqptr->msg_lrpid = p->p_pid; msqptr->msg_rtime = time.tv_sec; /* * Make msgsz the actual amount that we'll be returning. * Note that this effectively truncates the message if it is too long * (since msgsz is never increased). */ #ifdef MSG_DEBUG_OK printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz, msghdr->msg_ts); #endif if (msgsz > msghdr->msg_ts) msgsz = msghdr->msg_ts; /* * Return the type to the user. */ eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp, sizeof(msghdr->msg_type)); if (eval != 0) { #ifdef MSG_DEBUG_OK printf("error (%d) copying out message type\n", eval); #endif msg_freehdr(msghdr); wakeup((caddr_t)msqptr); return(eval); } - user_msgp += sizeof(msghdr->msg_type); + user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); /* * Return the segments to the user */ next = msghdr->msg_spot; for (len = 0; len < msgsz; len += msginfo.msgssz) { size_t tlen; if (msgsz > msginfo.msgssz) tlen = msginfo.msgssz; else tlen = msgsz; if (next <= -1) panic("next too low #3"); if (next >= msginfo.msgseg) panic("next out of range #3"); eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz], user_msgp, tlen); if (eval != 0) { #ifdef MSG_DEBUG_OK printf("error (%d) copying out message segment\n", eval); #endif msg_freehdr(msghdr); wakeup((caddr_t)msqptr); return(eval); } - user_msgp += tlen; + user_msgp = (char *)user_msgp + tlen; next = msgmaps[next].next; } /* * Done, return the actual number of bytes copied out. */ msg_freehdr(msghdr); wakeup((caddr_t)msqptr); *retval = msgsz; return(0); } Index: head/sys/libkern/qsort.c =================================================================== --- head/sys/libkern/qsort.c (revision 17970) +++ head/sys/libkern/qsort.c (revision 17971) @@ -1,172 +1,172 @@ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * - * $Id$ + * $Id: qsort.c,v 1.4 1996/08/28 20:32:19 bde Exp $ */ #include typedef int cmp_t __P((const void *, const void *)); static inline char *med3 __P((char *, char *, char *, cmp_t *)); static inline void swapfunc __P((char *, char *, int, int)); #define min(a, b) (a) < (b) ? a : b /* * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". */ #define swapcode(TYPE, parmi, parmj, n) { \ long i = (n) / sizeof (TYPE); \ register TYPE *pi = (TYPE *) (parmi); \ register TYPE *pj = (TYPE *) (parmj); \ do { \ register TYPE t = *pi; \ *pi++ = *pj; \ *pj++ = t; \ } while (--i > 0); \ } #define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; static inline void swapfunc(a, b, n, swaptype) char *a, *b; int n, swaptype; { if(swaptype <= 1) swapcode(long, a, b, n) else swapcode(char, a, b, n) } #define swap(a, b) \ if (swaptype == 0) { \ long t = *(long *)(a); \ *(long *)(a) = *(long *)(b); \ *(long *)(b) = t; \ } else \ swapfunc(a, b, es, swaptype) #define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) static inline char * med3(a, b, c, cmp) char *a, *b, *c; cmp_t *cmp; { return cmp(a, b) < 0 ? (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a )) :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); } void qsort(a, n, es, cmp) void *a; size_t n, es; cmp_t *cmp; { char *pa, *pb, *pc, *pd, *pl, *pm, *pn; int d, r, swaptype, swap_cnt; loop: SWAPINIT(a, es); swap_cnt = 0; if (n < 7) { - for (pm = a + es; pm < (char *) a + n * es; pm += es) - for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; pl -= es) swap(pl, pl - es); return; } - pm = a + (n / 2) * es; + pm = (char *)a + (n / 2) * es; if (n > 7) { pl = a; - pn = a + (n - 1) * es; + pn = (char *)a + (n - 1) * es; if (n > 40) { d = (n / 8) * es; pl = med3(pl, pl + d, pl + 2 * d, cmp); pm = med3(pm - d, pm, pm + d, cmp); pn = med3(pn - 2 * d, pn - d, pn, cmp); } pm = med3(pl, pm, pn, cmp); } swap(a, pm); - pa = pb = a + es; + pa = pb = (char *)a + es; - pc = pd = a + (n - 1) * es; + pc = pd = (char *)a + (n - 1) * es; for (;;) { while (pb <= pc && (r = cmp(pb, a)) <= 0) { if (r == 0) { swap_cnt = 1; swap(pa, pb); pa += es; } pb += es; } while (pb <= pc && (r = cmp(pc, a)) >= 0) { if (r == 0) { swap_cnt = 1; swap(pc, pd); pd -= es; } pc -= es; } if (pb > pc) break; swap(pb, pc); swap_cnt = 1; pb += es; pc -= es; } if (swap_cnt == 0) { /* Switch to insertion sort */ - for (pm = a + es; pm < (char *) a + n * es; pm += es) - for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl > (char *)a && cmp(pl - es, pl) > 0; pl -= es) swap(pl, pl - es); return; } - pn = a + n * es; + pn = (char *)a + n * es; r = min(pa - (char *)a, pb - pa); vecswap(a, pb - r, r); r = min(pd - pc, pn - pd - es); vecswap(pb, pn - r, r); if ((r = pb - pa) > es) qsort(a, r / es, es, cmp); if ((r = pd - pc) > es) { /* Iterate rather than recurse to save stack space */ a = pn - r; n = r / es; goto loop; } /* qsort(pn - r, r / es, es, cmp);*/ } Index: head/sys/ufs/lfs/lfs_segment.c =================================================================== --- head/sys/ufs/lfs/lfs_segment.c (revision 17970) +++ head/sys/ufs/lfs/lfs_segment.c (revision 17971) @@ -1,1197 +1,1197 @@ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)lfs_segment.c 8.5 (Berkeley) 1/4/94 - * $Id: lfs_segment.c,v 1.16 1995/12/17 21:09:49 phk Exp $ + * $Id: lfs_segment.c,v 1.17 1996/06/12 05:11:41 gpalmer Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int count_lock_queue __P((void)); static caddr_t lfs_alloc_buffer __P((int size)); static void lfs_reclaim_buffers __P((void)); #define MAX_ACTIVE 10 #define MAX_IO_BUFS 256 #define MAX_IO_SIZE (1024*512) static int lfs_total_io_size; static int lfs_total_io_count; static volatile int lfs_total_free_count; static int lfs_free_needed; static int lfs_in_buffer_reclaim; static struct lfs_freebuf { int size; caddr_t address; } lfs_freebufs[MAX_IO_BUFS]; void lfs_free_buffer( caddr_t address, int size) { lfs_freebufs[lfs_total_free_count].address = address; lfs_freebufs[lfs_total_free_count].size = size; ++lfs_total_free_count; if( lfs_free_needed) { wakeup((caddr_t) &lfs_free_needed); lfs_free_needed = 0; } } static void lfs_reclaim_buffers() { int i,s; int reclaimed = 0; if( lfs_in_buffer_reclaim) return; lfs_in_buffer_reclaim = 1; s = splhigh(); for(i=0;i= MAX_IO_BUFS) || (lfs_total_io_size >= MAX_IO_SIZE)) { lfs_free_needed = 1; tsleep(&lfs_free_needed, PRIBIO, "lfsalc", 0); splx(s); lfs_reclaim_buffers(); s = splhigh(); } splx(s); lfs_total_io_size += size; lfs_total_io_count += 1; rtval = malloc(size, M_SEGMENT, M_WAITOK); return rtval; } /* * Determine if it's OK to start a partial in this segment, or if we need * to go on to a new segment. */ #define LFS_PARTIAL_FITS(fs) \ ((fs)->lfs_dbpseg - ((fs)->lfs_offset - (fs)->lfs_curseg) > \ 1 << (fs)->lfs_fsbtodb) static void lfs_callback __P((struct buf *)); static void lfs_gather __P((struct lfs *, struct segment *, struct vnode *, int (*) __P((struct lfs *, struct buf *)))); void lfs_iset __P((struct inode *, daddr_t, time_t)); static int lfs_match_data __P((struct lfs *, struct buf *)); static int lfs_match_dindir __P((struct lfs *, struct buf *)); static int lfs_match_indir __P((struct lfs *, struct buf *)); #ifdef TRIPLE static int lfs_match_tindir __P((struct lfs *, struct buf *)); #endif static void lfs_newseg __P((struct lfs *)); static void lfs_shellsort __P((struct buf **, daddr_t *, register int)); static void lfs_supercallback __P((struct buf *)); static void lfs_writefile __P((struct lfs *, struct segment *, struct vnode *)); static void lfs_writevnodes __P((struct lfs *fs, struct mount *mp, struct segment *sp, int dirops)); /* Statistics Counters */ #define DOSTATS struct lfs_stats lfs_stats; /* op values to lfs_writevnodes */ #define VN_REG 0 #define VN_DIROP 1 #define VN_EMPTY 2 /* * Ifile and meta data blocks are not marked busy, so segment writes MUST be * single threaded. Currently, there are two paths into lfs_segwrite, sync() * and getnewbuf(). They both mark the file system busy. Lfs_vflush() * explicitly marks the file system busy. So lfs_segwrite is safe. I think. */ int lfs_vflush(vp) struct vnode *vp; { struct inode *ip; struct lfs *fs; struct segment *sp; int error; fs = VFSTOUFS(vp->v_mount)->um_lfs; /* XXX * lfs_segwrite uses lfs_writevnodes to flush dirty vnodes. * lfs_writevnodes (by way of a check with lfs_vref) passes over * locked vnodes. Since we usually come here with vp locked, anytime * we just happen to call lfs_vflush and we are past the "MAX_ACTIVE" * threshold, we used to call lfs_seqwrite and assume it would take * care of the problem... but of course it didn't. Now the question * remains, is this the right thing to do, or should lfs_seqwrite or * lfs_writevnodes be fixed to handle locked vnodes?? */ if (fs->lfs_nactive > MAX_ACTIVE){ error = lfs_segwrite(vp->v_mount, SEGM_SYNC|SEGM_CKP); if(error) return(error); } lfs_seglock(fs, SEGM_SYNC); sp = fs->lfs_sp; ip = VTOI(vp); if (vp->v_dirtyblkhd.lh_first == NULL) lfs_writevnodes(fs, vp->v_mount, sp, VN_EMPTY); do { do { if (vp->v_dirtyblkhd.lh_first != NULL) lfs_writefile(fs, sp, vp); } while (lfs_writeinode(fs, sp, ip)); } while (lfs_writeseg(fs, sp) && ip->i_number == LFS_IFILE_INUM); if (vp->v_dirtyblkhd.lh_first != NULL) panic("lfs_vflush: dirty bufs!!!"); #ifdef DOSTATS ++lfs_stats.nwrites; if (sp->seg_flags & SEGM_SYNC) ++lfs_stats.nsync_writes; if (sp->seg_flags & SEGM_CKP) ++lfs_stats.ncheckpoints; #endif lfs_segunlock(fs); return (0); } static void lfs_writevnodes(fs, mp, sp, op) struct lfs *fs; struct mount *mp; struct segment *sp; int op; { struct inode *ip; struct vnode *vp; loop: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = vp->v_mntvnodes.le_next) { /* * If the vnode that we are about to sync is no longer * associated with this mount point, start over. */ if (vp->v_mount != mp) goto loop; /* XXX ignore dirops for now if (op == VN_DIROP && !(vp->v_flag & VDIROP) || op != VN_DIROP && (vp->v_flag & VDIROP)) continue; */ if (op == VN_EMPTY && vp->v_dirtyblkhd.lh_first) continue; if (vp->v_type == VNON) continue; if (lfs_vref(vp)) continue; /* * Write the inode/file if dirty and it's not the * the IFILE. */ ip = VTOI(vp); if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE) || vp->v_dirtyblkhd.lh_first != NULL) && ip->i_number != LFS_IFILE_INUM) { if (vp->v_dirtyblkhd.lh_first != NULL) lfs_writefile(fs, sp, vp); (void) lfs_writeinode(fs, sp, ip); } vp->v_flag &= ~VDIROP; lfs_vunref(vp); } } int lfs_segwrite(mp, flags) struct mount *mp; int flags; /* Do a checkpoint. */ { struct buf *bp; struct inode *ip; struct lfs *fs; struct segment *sp; struct vnode *vp; SEGUSE *segusep; daddr_t ibno; CLEANERINFO *cip; int clean, do_ckp, error, i; fs = VFSTOUFS(mp)->um_lfs; /* * If we have fewer than 2 clean segments, wait until cleaner * writes. */ do { LFS_CLEANERINFO(cip, fs, bp); clean = cip->clean; brelse(bp); if (clean <= 2) { printf("lfs_segwrite: ran out of clean segments, waiting for cleaner\n"); wakeup(&lfs_allclean_wakeup); if (error = tsleep(&fs->lfs_avail, PRIBIO + 1, "lfs writer", 0)) return (error); } } while (clean <= 2 ); /* * Allocate a segment structure and enough space to hold pointers to * the maximum possible number of buffers which can be described in a * single summary block. */ do_ckp = flags & SEGM_CKP || fs->lfs_nactive > MAX_ACTIVE; lfs_seglock(fs, flags | (do_ckp ? SEGM_CKP : 0)); sp = fs->lfs_sp; lfs_writevnodes(fs, mp, sp, VN_REG); /* XXX ignore ordering of dirops for now */ /* XXX fs->lfs_writer = 1; if (fs->lfs_dirops && (error = tsleep(&fs->lfs_writer, PRIBIO + 1, "lfs writer", 0))) { free(sp->bpp, M_SEGMENT); free(sp, M_SEGMENT); fs->lfs_writer = 0; return (error); } lfs_writevnodes(fs, mp, sp, VN_DIROP); */ /* * If we are doing a checkpoint, mark everything since the * last checkpoint as no longer ACTIVE. */ if (do_ckp) for (ibno = fs->lfs_cleansz + fs->lfs_segtabsz; --ibno >= fs->lfs_cleansz; ) { if (bread(fs->lfs_ivnode, ibno, fs->lfs_bsize, NOCRED, &bp)) panic("lfs: ifile read"); segusep = (SEGUSE *)bp->b_data; for (i = fs->lfs_sepb; i--; segusep++) segusep->su_flags &= ~SEGUSE_ACTIVE; error = VOP_BWRITE(bp); } if (do_ckp || fs->lfs_doifile) { redo: vp = fs->lfs_ivnode; while (vget(vp, 1)); ip = VTOI(vp); if (vp->v_dirtyblkhd.lh_first != NULL) lfs_writefile(fs, sp, vp); (void)lfs_writeinode(fs, sp, ip); vput(vp); if (lfs_writeseg(fs, sp) && do_ckp) goto redo; } else (void) lfs_writeseg(fs, sp); /* * If the I/O count is non-zero, sleep until it reaches zero. At the * moment, the user's process hangs around so we can sleep. */ /* XXX ignore dirops for now fs->lfs_writer = 0; fs->lfs_doifile = 0; wakeup(&fs->lfs_dirops); */ #ifdef DOSTATS ++lfs_stats.nwrites; if (sp->seg_flags & SEGM_SYNC) ++lfs_stats.nsync_writes; if (sp->seg_flags & SEGM_CKP) ++lfs_stats.ncheckpoints; #endif lfs_segunlock(fs); return (0); } /* * Write the dirty blocks associated with a vnode. */ static void lfs_writefile(fs, sp, vp) struct lfs *fs; struct segment *sp; struct vnode *vp; { struct buf *bp; struct finfo *fip; IFILE *ifp; if (sp->seg_bytes_left < fs->lfs_bsize || sp->sum_bytes_left < sizeof(struct finfo)) (void) lfs_writeseg(fs, sp); sp->sum_bytes_left -= sizeof(struct finfo) - sizeof(daddr_t); ++((SEGSUM *)(sp->segsum))->ss_nfinfo; fip = sp->fip; fip->fi_nblocks = 0; fip->fi_ino = VTOI(vp)->i_number; LFS_IENTRY(ifp, fs, fip->fi_ino, bp); fip->fi_version = ifp->if_version; brelse(bp); /* * It may not be necessary to write the meta-data blocks at this point, * as the roll-forward recovery code should be able to reconstruct the * list. */ lfs_gather(fs, sp, vp, lfs_match_data); lfs_gather(fs, sp, vp, lfs_match_indir); lfs_gather(fs, sp, vp, lfs_match_dindir); #ifdef TRIPLE lfs_gather(fs, sp, vp, lfs_match_tindir); #endif fip = sp->fip; if (fip->fi_nblocks != 0) { sp->fip = (struct finfo *)((caddr_t)fip + sizeof(struct finfo) + sizeof(daddr_t) * (fip->fi_nblocks - 1)); sp->start_lbp = &sp->fip->fi_blocks[0]; } else { sp->sum_bytes_left += sizeof(struct finfo) - sizeof(daddr_t); --((SEGSUM *)(sp->segsum))->ss_nfinfo; } } int lfs_writeinode(fs, sp, ip) struct lfs *fs; struct segment *sp; struct inode *ip; { struct buf *bp, *ibp; IFILE *ifp; SEGUSE *sup; daddr_t daddr; ino_t ino; int error, i, ndx; int redo_ifile = 0; if (!(ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE))) return(0); /* Allocate a new inode block if necessary. */ if (sp->ibp == NULL) { /* Allocate a new segment if necessary. */ if (sp->seg_bytes_left < fs->lfs_bsize || sp->sum_bytes_left < sizeof(daddr_t)) (void) lfs_writeseg(fs, sp); /* Get next inode block. */ daddr = fs->lfs_offset; fs->lfs_offset += fsbtodb(fs, 1); sp->ibp = *sp->cbpp++ = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, daddr, fs->lfs_bsize); /* Zero out inode numbers */ for (i = 0; i < INOPB(fs); ++i) ((struct dinode *)sp->ibp->b_data)[i].di_inumber = 0; ++sp->start_bpp; fs->lfs_avail -= fsbtodb(fs, 1); /* Set remaining space counters. */ sp->seg_bytes_left -= fs->lfs_bsize; sp->sum_bytes_left -= sizeof(daddr_t); ndx = LFS_SUMMARY_SIZE / sizeof(daddr_t) - sp->ninodes / INOPB(fs) - 1; ((daddr_t *)(sp->segsum))[ndx] = daddr; } /* Update the inode times and copy the inode onto the inode page. */ if (ip->i_flag & IN_MODIFIED) --fs->lfs_uinodes; ITIMES(ip, &time, &time); ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE); bp = sp->ibp; ((struct dinode *)bp->b_data)[sp->ninodes % INOPB(fs)] = ip->i_din; /* Increment inode count in segment summary block. */ ++((SEGSUM *)(sp->segsum))->ss_ninos; /* If this page is full, set flag to allocate a new page. */ if (++sp->ninodes % INOPB(fs) == 0) sp->ibp = NULL; /* * If updating the ifile, update the super-block. Update the disk * address and access times for this inode in the ifile. */ ino = ip->i_number; if (ino == LFS_IFILE_INUM) { daddr = fs->lfs_idaddr; fs->lfs_idaddr = bp->b_blkno; } else { LFS_IENTRY(ifp, fs, ino, ibp); daddr = ifp->if_daddr; ifp->if_daddr = bp->b_blkno; error = VOP_BWRITE(ibp); } /* * No need to update segment usage if there was no former inode address * or if the last inode address is in the current partial segment. */ if (daddr != LFS_UNUSED_DADDR && !(daddr >= fs->lfs_lastpseg && daddr <= bp->b_blkno)) { LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp); #ifdef DIAGNOSTIC if (sup->su_nbytes < sizeof(struct dinode)) { /* XXX -- Change to a panic. */ printf("lfs: negative bytes (segment %ld)\n", datosn(fs, daddr)); panic("negative bytes"); } #endif sup->su_nbytes -= sizeof(struct dinode); redo_ifile = (ino == LFS_IFILE_INUM && !(bp->b_flags & B_GATHERED)); error = VOP_BWRITE(bp); } return (redo_ifile); } int lfs_gatherblock(sp, bp, sptr) struct segment *sp; struct buf *bp; int *sptr; { struct lfs *fs; int version; /* * If full, finish this segment. We may be doing I/O, so * release and reacquire the splbio(). */ #ifdef DIAGNOSTIC if (sp->vp == NULL) panic ("lfs_gatherblock: Null vp in segment"); #endif fs = sp->fs; if (sp->sum_bytes_left < sizeof(daddr_t) || sp->seg_bytes_left < fs->lfs_bsize) { if (sptr) splx(*sptr); lfs_updatemeta(sp); version = sp->fip->fi_version; (void) lfs_writeseg(fs, sp); sp->fip->fi_version = version; sp->fip->fi_ino = VTOI(sp->vp)->i_number; /* Add the current file to the segment summary. */ ++((SEGSUM *)(sp->segsum))->ss_nfinfo; sp->sum_bytes_left -= sizeof(struct finfo) - sizeof(daddr_t); if (sptr) *sptr = splbio(); return(1); } /* Insert into the buffer list, update the FINFO block. */ bp->b_flags |= B_GATHERED; *sp->cbpp++ = bp; sp->fip->fi_blocks[sp->fip->fi_nblocks++] = bp->b_lblkno; sp->sum_bytes_left -= sizeof(daddr_t); sp->seg_bytes_left -= fs->lfs_bsize; return(0); } static void lfs_gather(fs, sp, vp, match) struct lfs *fs; struct segment *sp; struct vnode *vp; int (*match) __P((struct lfs *, struct buf *)); { struct buf *bp; int s; sp->vp = vp; s = splbio(); loop: for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = bp->b_vnbufs.le_next) { if (bp->b_flags & B_BUSY || !match(fs, bp) || bp->b_flags & B_GATHERED) continue; #ifdef DIAGNOSTIC if (!(bp->b_flags & B_DELWRI)) panic("lfs_gather: bp not B_DELWRI"); if (!(bp->b_flags & B_LOCKED)) panic("lfs_gather: bp not B_LOCKED"); #endif if (lfs_gatherblock(sp, bp, &s)) goto loop; } splx(s); lfs_updatemeta(sp); sp->vp = NULL; } /* * Update the metadata that points to the blocks listed in the FINFO * array. */ void lfs_updatemeta(sp) struct segment *sp; { SEGUSE *sup; struct buf *bp; struct lfs *fs; struct vnode *vp; struct indir a[NIADDR + 2], *ap; struct inode *ip; daddr_t daddr, lbn, off; int db_per_fsb, error, i, nblocks, num; vp = sp->vp; nblocks = &sp->fip->fi_blocks[sp->fip->fi_nblocks] - sp->start_lbp; if (vp == NULL || nblocks == 0) return; /* Sort the blocks. */ if (!(sp->seg_flags & SEGM_CLEAN)) lfs_shellsort(sp->start_bpp, sp->start_lbp, nblocks); /* * Assign disk addresses, and update references to the logical * block and the segment usage information. */ fs = sp->fs; db_per_fsb = fsbtodb(fs, 1); for (i = nblocks; i--; ++sp->start_bpp) { lbn = *sp->start_lbp++; (*sp->start_bpp)->b_blkno = off = fs->lfs_offset; fs->lfs_offset += db_per_fsb; if (error = ufs_bmaparray(vp, lbn, &daddr, a, &num, NULL, NULL)) panic("lfs_updatemeta: ufs_bmaparray %d", error); ip = VTOI(vp); switch (num) { case 0: ip->i_db[lbn] = off; break; case 1: ip->i_ib[a[0].in_off] = off; break; default: ap = &a[num - 1]; if (bread(vp, ap->in_lbn, fs->lfs_bsize, NOCRED, &bp)) panic("lfs_updatemeta: bread bno %d", ap->in_lbn); /* * Bread may create a new indirect block which needs * to get counted for the inode. */ if (bp->b_blkno == -1 && !(bp->b_flags & B_CACHE)) { printf ("Updatemeta allocating indirect block: shouldn't happen\n"); ip->i_blocks += btodb(fs->lfs_bsize); fs->lfs_bfree -= btodb(fs->lfs_bsize); } ((daddr_t *)bp->b_data)[ap->in_off] = off; VOP_BWRITE(bp); } /* Update segment usage information. */ if (daddr != UNASSIGNED && !(daddr >= fs->lfs_lastpseg && daddr <= off)) { LFS_SEGENTRY(sup, fs, datosn(fs, daddr), bp); #ifdef DIAGNOSTIC if (sup->su_nbytes < fs->lfs_bsize) { /* XXX -- Change to a panic. */ printf("lfs: negative bytes (segment %ld)\n", datosn(fs, daddr)); panic ("Negative Bytes"); } #endif sup->su_nbytes -= fs->lfs_bsize; error = VOP_BWRITE(bp); } } } /* * Start a new segment. */ int lfs_initseg(fs) struct lfs *fs; { struct segment *sp; SEGUSE *sup; SEGSUM *ssp; struct buf *bp; int repeat; sp = fs->lfs_sp; repeat = 0; /* Advance to the next segment. */ if (!LFS_PARTIAL_FITS(fs)) { /* Wake up any cleaning procs waiting on this file system. */ wakeup(&lfs_allclean_wakeup); lfs_newseg(fs); repeat = 1; fs->lfs_offset = fs->lfs_curseg; sp->seg_number = datosn(fs, fs->lfs_curseg); sp->seg_bytes_left = fs->lfs_dbpseg * DEV_BSIZE; /* * If the segment contains a superblock, update the offset * and summary address to skip over it. */ LFS_SEGENTRY(sup, fs, sp->seg_number, bp); if (sup->su_flags & SEGUSE_SUPERBLOCK) { fs->lfs_offset += LFS_SBPAD / DEV_BSIZE; sp->seg_bytes_left -= LFS_SBPAD; } brelse(bp); } else { sp->seg_number = datosn(fs, fs->lfs_curseg); sp->seg_bytes_left = (fs->lfs_dbpseg - (fs->lfs_offset - fs->lfs_curseg)) * DEV_BSIZE; } fs->lfs_lastpseg = fs->lfs_offset; sp->fs = fs; sp->ibp = NULL; sp->ninodes = 0; /* Get a new buffer for SEGSUM and enter it into the buffer list. */ sp->cbpp = sp->bpp; *sp->cbpp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, fs->lfs_offset, LFS_SUMMARY_SIZE); sp->segsum = (*sp->cbpp)->b_data; bzero(sp->segsum, LFS_SUMMARY_SIZE); sp->start_bpp = ++sp->cbpp; fs->lfs_offset += LFS_SUMMARY_SIZE / DEV_BSIZE; /* Set point to SEGSUM, initialize it. */ ssp = sp->segsum; ssp->ss_next = fs->lfs_nextseg; ssp->ss_nfinfo = ssp->ss_ninos = 0; /* Set pointer to first FINFO, initialize it. */ - sp->fip = (struct finfo *)(sp->segsum + sizeof(SEGSUM)); + sp->fip = (struct finfo *)((char *)sp->segsum + sizeof(SEGSUM)); sp->fip->fi_nblocks = 0; sp->start_lbp = &sp->fip->fi_blocks[0]; sp->seg_bytes_left -= LFS_SUMMARY_SIZE; sp->sum_bytes_left = LFS_SUMMARY_SIZE - sizeof(SEGSUM); return(repeat); } /* * Return the next segment to write. */ static void lfs_newseg(fs) struct lfs *fs; { CLEANERINFO *cip; SEGUSE *sup; struct buf *bp; int curseg, isdirty, sn; LFS_SEGENTRY(sup, fs, datosn(fs, fs->lfs_nextseg), bp); sup->su_flags |= SEGUSE_DIRTY | SEGUSE_ACTIVE; sup->su_nbytes = 0; sup->su_nsums = 0; sup->su_ninos = 0; (void) VOP_BWRITE(bp); LFS_CLEANERINFO(cip, fs, bp); --cip->clean; ++cip->dirty; (void) VOP_BWRITE(bp); fs->lfs_lastseg = fs->lfs_curseg; fs->lfs_curseg = fs->lfs_nextseg; for (sn = curseg = datosn(fs, fs->lfs_curseg);;) { sn = (sn + 1) % fs->lfs_nseg; if (sn == curseg) panic("lfs_nextseg: no clean segments"); LFS_SEGENTRY(sup, fs, sn, bp); isdirty = sup->su_flags & SEGUSE_DIRTY; brelse(bp); if (!isdirty) break; } ++fs->lfs_nactive; fs->lfs_nextseg = sntoda(fs, sn); #ifdef DOSTATS ++lfs_stats.segsused; #endif } int lfs_writeseg(fs, sp) struct lfs *fs; struct segment *sp; { struct buf **bpp, *bp, *cbp; SEGUSE *sup; SEGSUM *ssp; dev_t i_dev; size_t size; u_long *datap, *dp; int ch_per_blk, do_again, i, nblocks, num, s; int (*strategy)__P((struct vop_strategy_args *)); struct vop_strategy_args vop_strategy_a; u_short ninos; char *p; /* * If there are no buffers other than the segment summary to write * and it is not a checkpoint, don't do anything. On a checkpoint, * even if there aren't any buffers, you need to write the superblock. */ if ((nblocks = sp->cbpp - sp->bpp) == 1) return (0); ssp = (SEGSUM *)sp->segsum; /* Update the segment usage information. */ LFS_SEGENTRY(sup, fs, sp->seg_number, bp); ninos = (ssp->ss_ninos + INOPB(fs) - 1) / INOPB(fs); sup->su_nbytes += nblocks - 1 - ninos << fs->lfs_bshift; sup->su_nbytes += ssp->ss_ninos * sizeof(struct dinode); sup->su_nbytes += LFS_SUMMARY_SIZE; sup->su_lastmod = time.tv_sec; sup->su_ninos += ninos; ++sup->su_nsums; do_again = !(bp->b_flags & B_GATHERED); (void)VOP_BWRITE(bp); /* * Compute checksum across data and then across summary; the first * block (the summary block) is skipped. Set the create time here * so that it's guaranteed to be later than the inode mod times. * * XXX * Fix this to do it inline, instead of malloc/copy. */ datap = dp = malloc(nblocks * sizeof(u_long), M_SEGMENT, M_WAITOK); for (bpp = sp->bpp, i = nblocks - 1; i--;) { if ((*++bpp)->b_flags & B_INVAL) { if (copyin((*bpp)->b_saveaddr, dp++, sizeof(u_long))) panic("lfs_writeseg: copyin failed"); } else *dp++ = ((u_long *)(*bpp)->b_data)[0]; } ssp->ss_create = time.tv_sec; ssp->ss_datasum = cksum(datap, (nblocks - 1) * sizeof(u_long)); ssp->ss_sumsum = cksum(&ssp->ss_datasum, LFS_SUMMARY_SIZE - sizeof(ssp->ss_sumsum)); free(datap, M_SEGMENT); #ifdef DIAGNOSTIC if (fs->lfs_bfree < fsbtodb(fs, ninos) + LFS_SUMMARY_SIZE / DEV_BSIZE) panic("lfs_writeseg: No diskspace for summary"); #endif fs->lfs_bfree -= (fsbtodb(fs, ninos) + LFS_SUMMARY_SIZE / DEV_BSIZE); i_dev = VTOI(fs->lfs_ivnode)->i_dev; strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)]; /* * When we simply write the blocks we lose a rotation for every block * written. To avoid this problem, we allocate memory in chunks, copy * the buffers into the chunk and write the chunk. MAXPHYS is the * largest size I/O devices can handle. * When the data is copied to the chunk, turn off the the B_LOCKED bit * and brelse the buffer (which will move them to the LRU list). Add * the B_CALL flag to the buffer header so we can count I/O's for the * checkpoints and so we can release the allocated memory. * * XXX * This should be removed if the new virtual memory system allows us to * easily make the buffers contiguous in kernel memory and if that's * fast enough. */ ch_per_blk = MAXPHYS / fs->lfs_bsize; for (bpp = sp->bpp, i = nblocks; i;) { num = ch_per_blk; if (num > i) num = i; i -= num; size = num * fs->lfs_bsize; cbp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, (*bpp)->b_blkno, size); cbp->b_dev = i_dev; cbp->b_flags |= B_ASYNC | B_BUSY; s = splbio(); ++fs->lfs_iocount; for (p = cbp->b_data; num--;) { bp = *bpp++; /* * Fake buffers from the cleaner are marked as B_INVAL. * We need to copy the data from user space rather than * from the buffer indicated. * XXX == what do I do on an error? */ if (bp->b_flags & B_INVAL) { if (copyin(bp->b_saveaddr, p, bp->b_bcount)) panic("lfs_writeseg: copyin failed"); } else bcopy(bp->b_data, p, bp->b_bcount); p += bp->b_bcount; if (bp->b_flags & B_LOCKED) --locked_queue_count; bp->b_flags &= ~(B_ERROR | B_READ | B_DELWRI | B_LOCKED | B_GATHERED); if (bp->b_flags & B_CALL) { /* if B_CALL, it was created with newbuf */ if (!(bp->b_flags & B_INVAL)) lfs_free_buffer( bp->b_data, roundup( bp->b_bufsize, DEV_BSIZE)); relpbuf(bp); } else { bremfree(bp); bp->b_flags |= B_DONE; reassignbuf(bp, bp->b_vp); brelse(bp); } } cbp->b_bcount = p - (char *)cbp->b_data; ++cbp->b_vp->v_numoutput; splx(s); /* * XXXX This is a gross and disgusting hack. Since these * buffers are physically addressed, they hang off the * device vnode (devvp). As a result, they have no way * of getting to the LFS superblock or lfs structure to * keep track of the number of I/O's pending. So, I am * going to stuff the fs into the saveaddr field of * the buffer (yuk). */ cbp->b_saveaddr = (caddr_t)fs; vop_strategy_a.a_desc = VDESC(vop_strategy); vop_strategy_a.a_bp = cbp; (strategy)(&vop_strategy_a); } /* * XXX * Vinvalbuf can move locked buffers off the locked queue * and we have no way of knowing about this. So, after * doing a big write, we recalculate how many bufers are * really still left on the locked queue. */ locked_queue_count = count_lock_queue(); wakeup(&locked_queue_count); #ifdef DOSTATS ++lfs_stats.psegwrites; lfs_stats.blocktot += nblocks - 1; if (fs->lfs_sp->seg_flags & SEGM_SYNC) ++lfs_stats.psyncwrites; if (fs->lfs_sp->seg_flags & SEGM_CLEAN) { ++lfs_stats.pcleanwrites; lfs_stats.cleanblocks += nblocks - 1; } #endif return (lfs_initseg(fs) || do_again); } void lfs_writesuper(fs) struct lfs *fs; { struct buf *bp; dev_t i_dev; int (*strategy) __P((struct vop_strategy_args *)); int s; struct vop_strategy_args vop_strategy_a; i_dev = VTOI(fs->lfs_ivnode)->i_dev; strategy = VTOI(fs->lfs_ivnode)->i_devvp->v_op[VOFFSET(vop_strategy)]; /* Checksum the superblock and copy it into a buffer. */ fs->lfs_cksum = cksum(fs, sizeof(struct lfs) - sizeof(fs->lfs_cksum)); bp = lfs_newbuf(VTOI(fs->lfs_ivnode)->i_devvp, fs->lfs_sboffs[0], LFS_SBPAD); *(struct lfs *)bp->b_data = *fs; /* XXX Toggle between first two superblocks; for now just write first */ bp->b_dev = i_dev; bp->b_flags |= B_BUSY | B_CALL | B_ASYNC; bp->b_flags &= ~(B_DONE | B_ERROR | B_READ | B_DELWRI); bp->b_iodone = lfs_supercallback; vop_strategy_a.a_desc = VDESC(vop_strategy); vop_strategy_a.a_bp = bp; s = splbio(); ++bp->b_vp->v_numoutput; splx(s); (strategy)(&vop_strategy_a); } /* * Logical block number match routines used when traversing the dirty block * chain. */ static int lfs_match_data(fs, bp) struct lfs *fs; struct buf *bp; { return (bp->b_lblkno >= 0); } static int lfs_match_indir(fs, bp) struct lfs *fs; struct buf *bp; { int lbn; lbn = bp->b_lblkno; return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 0); } static int lfs_match_dindir(fs, bp) struct lfs *fs; struct buf *bp; { int lbn; lbn = bp->b_lblkno; return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 1); } #ifdef TRIPLE static int lfs_match_tindir(fs, bp) struct lfs *fs; struct buf *bp; { int lbn; lbn = bp->b_lblkno; return (lbn < 0 && (-lbn - NDADDR) % NINDIR(fs) == 2); } #endif /* * Allocate a new buffer header. */ struct buf * lfs_newbuf(vp, daddr, size) struct vnode *vp; daddr_t daddr; size_t size; { struct buf *bp; size_t nbytes; nbytes = roundup(size, DEV_BSIZE); bp = getpbuf(); if (nbytes) bp->b_data = lfs_alloc_buffer( nbytes); bp->b_bufsize = size; bp->b_bcount = size; bp->b_lblkno = daddr; bp->b_blkno = daddr; bp->b_error = 0; bp->b_resid = 0; bp->b_iodone = lfs_callback; bp->b_flags |= B_BUSY | B_CALL | B_NOCACHE; return (bp); } static void lfs_callback(bp) struct buf *bp; { struct lfs *fs; fs = (struct lfs *)bp->b_saveaddr; #ifdef DIAGNOSTIC if (fs->lfs_iocount == 0) panic("lfs_callback: zero iocount"); #endif if (--fs->lfs_iocount == 0) wakeup(&fs->lfs_iocount); lfs_free_buffer( bp->b_data, roundup( bp->b_bufsize, DEV_BSIZE)); relpbuf(bp); } static void lfs_supercallback(bp) struct buf *bp; { if( bp->b_data) lfs_free_buffer( bp->b_data, roundup( bp->b_bufsize, DEV_BSIZE)); relpbuf(bp); } /* * Shellsort (diminishing increment sort) from Data Structures and * Algorithms, Aho, Hopcraft and Ullman, 1983 Edition, page 290; * see also Knuth Vol. 3, page 84. The increments are selected from * formula (8), page 95. Roughly O(N^3/2). */ /* * This is our own private copy of shellsort because we want to sort * two parallel arrays (the array of buffer pointers and the array of * logical block numbers) simultaneously. Note that we cast the array * of logical block numbers to a unsigned in this routine so that the * negative block numbers (meta data blocks) sort AFTER the data blocks. */ static void lfs_shellsort(bp_array, lb_array, nmemb) struct buf **bp_array; daddr_t *lb_array; register int nmemb; { static int __rsshell_increments[] = { 4, 1, 0 }; register int incr, *incrp, t1, t2; struct buf *bp_temp; u_long lb_temp; for (incrp = __rsshell_increments; incr = *incrp++;) for (t1 = incr; t1 < nmemb; ++t1) for (t2 = t1 - incr; t2 >= 0;) if (lb_array[t2] > lb_array[t2 + incr]) { lb_temp = lb_array[t2]; lb_array[t2] = lb_array[t2 + incr]; lb_array[t2 + incr] = lb_temp; bp_temp = bp_array[t2]; bp_array[t2] = bp_array[t2 + incr]; bp_array[t2 + incr] = bp_temp; t2 -= incr; } else break; } /* * Check VXLOCK. Return 1 if the vnode is locked. Otherwise, vget it. */ int lfs_vref(vp) register struct vnode *vp; { if ((vp->v_flag & VXLOCK) || (vp->v_usecount == 0 && vp->v_freelist.tqe_prev == (struct vnode **)0xdeadb)) return(1); return (vget(vp, 0)); } void lfs_vunref(vp) register struct vnode *vp; { /* * This is vrele except that we do not want to VOP_INACTIVE * this vnode. Rather than inline vrele here, we flag the vnode * to tell lfs_inactive not to run on this vnode. Not as gross as * a global. */ vp->v_flag |= VNINACT; vrele(vp); vp->v_flag &= ~VNINACT; }