#include "sys/param.h"
#include "sys/types.h"
#include "sys/sysmacros.h"
#include "sys/systm.h"
#include "sys/buf.h"
#include "sys/conf.h"
#include "sys/map.h"
#include "sys/utsname.h"
#include "sys/elog.h"
#include "sys/erec.h"
#include "sys/err.h"
#include "sys/iobuf.h"
#include "sys/var.h"

typedef int	mem_t;

int	blkacty;

errinit()
{
	register struct err *errp;

	errp = &err;
	if(errp->e_nslot) {
		mapinit(errp->e_map, (errp->e_nslot+3)/2);
		mfree(errp->e_map, (mem_t)errp->e_nslot, (mem_t)1);
	}
	errp->e_org = errp->e_ptrs;
	errp->e_nxt = errp->e_ptrs;
}

struct errhdr *
geteslot(size)
{
	register ns, *p;
	register struct errhdr *ep;
	int n, sps;

	ns = (size+sizeof(struct errhdr)+sizeof(struct errslot)-1)
		/sizeof(struct errslot);
	sps = spl7();
	n = malloc(err.e_map, (mem_t)ns);
	splx(sps);
	if(n == 0)
		return(NULL);
	ep = (struct errhdr *)(&err.e_slot[--n]);
	ns *= sizeof(struct errslot)/sizeof(int);
	p = (int *)ep;
	do {
		*p++ = 0;
	} while(--ns);
	ep->e_len = size + sizeof(struct errhdr);
	return(++ep);
}

freeslot(ep)
register struct errhdr *ep;
{
	register ns, sps;

	ns = (ep->e_len+sizeof(struct errslot)-1)/sizeof(struct errslot);
	sps = spl7();
	mfree(err.e_map, (mem_t)ns,
		(mem_t)((((struct errslot *)ep)-err.e_slot)+1));
	splx(sps);
}

struct errhdr *
geterec()
{
	register sps;
	register struct errhdr *ep;
	register struct err *errp;

	errp = &err;
	sps = spl7();
	while(*errp->e_org == NULL)
		(void) sleep((caddr_t)&errp->e_org,PZERO+1);
	ep = *errp->e_org;
	*errp->e_org++ = NULL;
	if(errp->e_org >= &errp->e_ptrs[errp->e_nslot])
		errp->e_org = errp->e_ptrs;
	splx(sps);
	return(ep);
}

puterec(ep, type)
register struct errhdr *ep;
{
	register sps;
	register struct err *errp;

	errp = &err;
	(--ep)->e_type = type;
	ep->e_time = time;
	sps = spl7();
	*errp->e_nxt++ = ep;
	if(errp->e_nxt >= &errp->e_ptrs[errp->e_nslot])
		errp->e_nxt = errp->e_ptrs;
	splx(sps);
	wakeup((caddr_t)&errp->e_org);
}

logstart()
{
	register sps;
	register struct estart *ep;
	register struct bdevsw *bdp;
	register struct err *errp;
	extern nodev();

	errp = &err;

	sps = spl7();
	for(errp->e_org = &errp->e_ptrs[errp->e_nslot-1];
				errp->e_org >= errp->e_ptrs; errp->e_org--)
		if(*errp->e_org != NULL) {
			freeslot(*errp->e_org);
			*errp->e_org = NULL;
		}
	errp->e_org = errp->e_ptrs;
	errp->e_nxt = errp->e_ptrs;
	ep = (struct estart *)geteslot(sizeof(struct estart));
	splx(sps);
	if(ep == NULL)
		return;
	ep->e_name = utsname;
	for(bdp = &bdevsw[bdevcnt-1]; bdp >= bdevsw; bdp--)
		if(bdp->d_strategy != nodev)
			ep->e_bconf |= 1 << (&bdevsw[0]-bdp);
	ep->e_bconf = blkacty;
	puterec((struct errhdr *)ep, E_GOTS);
}

logtchg(nt)
time_t nt;
{
	register struct etimchg *ep;

	if((ep = (struct etimchg *)geteslot(sizeof(struct etimchg))) != NULL) {
		ep->e_ntime = nt;
		puterec((struct errhdr *)ep,E_TCHG);
	}
}

logstray(addr)
physadr addr;
{
	register struct estray *ep;

	if((ep = (struct estray *)geteslot(sizeof(struct estray))) != NULL) {
		ep->e_saddr = addr;
		ep->e_sbacty = blkacty;
		puterec((struct errhdr *)ep,E_STRAY);
	}
}

logparity(addr)
register paddr_t addr;
{
	register struct eparity *ep;

	if((ep = (struct eparity *)geteslot(sizeof(struct eparity))) != NULL) {
		ep->e_parreg = addr;
		puterec((struct errhdr *)ep,E_PRTY);
	}
}

/*
 *	fmtberr() is used by block device drivers to build up a valid
 *	eblock structure to be sent to the error log.
 *
 *	dp 		the address of the io queue item.
 *	unit		the Physical Device error report field
 *			the Logical Device field is the minor device number
 *	cyl		the cylinder number
 *	trk		the track number
 *	sector		the sector number
 *	regcnt		the number of following register structures
 *	regs		is the address of an array of structures each of which
 *			contain the elements described in struct deverreg.
 */

/* VARARGS7 */
fmtberr(dp, unit, cyl, trk, sector, regcnt, regs)
register struct iobuf *dp;
unsigned unit;
unsigned cyl;
unsigned trk;
unsigned sector;
long regcnt;
struct deverreg *regs;
{
	register struct eblock *ep;
	register struct buf *bp;
	register struct deverreg **dr;
	register char *str1;
	register char *pp;
	register short argc;
	register short nn;
	struct br {	/* just used to generate addr after eblock */
		struct eblock eb;
		char cregs[1];
	};
	struct  iostat  *iosp;
	extern char *longcopy();

	if(dp->io_erec != NULL) {
		dp->io_erec->e_rtry++;
		return;
	}
	/* count the length of the values and strings */
	nn = 0;
	argc = regcnt;
	dr = &regs;
	while (argc--) {
		nn += sizeof((*dr)->draddr);
		nn += sizeof((*dr)->drvalue);
		nn += strlen((*dr)->drname) + 1;	/* + null */
		nn += strlen((*dr)->drbits) + 1;	/* + null */
		nn += (nn & 1);	/* round to even number of bytes */
		dr++;
	}
	iosp = dp->io_stp;
	/* want sizeof eblock to the next long address */
	if((ep = (struct eblock *)
		geteslot(sizeof(struct eblock) + nn)) == NULL) {
		iosp->io_unlog++;
		return;
	}
	nn = major(dp->b_dev);
	bp = dp->b_actf;
	ep->e_dev = makedev(nn,(bp==NULL)?minor(dp->b_dev):minor(bp->b_dev));
	ep->e_bacty = blkacty;
	ep->e_stats.io_ops = iosp->io_ops;
	ep->e_stats.io_misc = iosp->io_misc;
	ep->e_stats.io_unlog = iosp->io_unlog;
	ep->e_pos.unit = unit;
	ep->e_pos.cyl = cyl;
	ep->e_pos.trk = trk;
	ep->e_pos.sector = sector;
	if(bp != NULL) {
		ep->e_bflags = (bp->b_flags&B_READ) ? E_READ : E_WRITE;
		if(bp->b_flags & B_PHYS)
			ep->e_bflags |= E_PHYS;
		if(bp->b_flags & B_MAP)
			ep->e_bflags |= E_MAP;
		ep->e_bnum = bp->b_blkno;
		ep->e_bytes = bp->b_bcount;
		ep->e_memadd = paddr(bp);
	}
	else
		ep->e_bflags = E_NOIO;
	ep->e_nreg = regcnt;
	pp = &(((struct br *)ep)->cregs[0]);
	dr = &regs;
	while(--regcnt >= 0) {
		/* copy out the number values */
		pp = longcopy((char *)&((*dr)->draddr),pp);
		pp = longcopy((char *)&((*dr)->drvalue),pp);
		/* copy out the strings themselves */
		str1 = (*dr)->drname;
		while (*str1) {
			*pp++ = *str1++;
		}
		/* copy the terminating null too */
		*pp++ = '\0';
		str1 = (*dr)->drbits;
		while (*str1) {
			*pp++ = *str1++;
		}
		*pp++ = '\0';
		dr++;
	}
	dp->io_erec = ep;
}

logberr(dp,error)
register struct iobuf *dp;
{
	register struct eblock *ep;

	if((ep = dp->io_erec) == NULL)
		return;
	if(error)
		ep->e_bflags |= E_ERROR;
	puterec((struct errhdr *)ep,E_BLK);
	dp->io_erec = NULL;
}

/* may not be on long address boundary when copied to b2,
   avoiding any alignment problems on some machines? */
char *
longcopy(b1,b2)
register char *b1,*b2;
{
	register int ii;

	for (ii=0; ii < sizeof(long); ii++) {
		*b2++ = *b1++;
	}
	return(b2);
}
