#include "../head/param.h"
#include "../head/systm.h"
#include "../head/user.h"
#include "../head/userx.h"
#include "../head/buf.h"
#include "../head/conf.h"
#include "../head/ipcomm.h"
#include "../head/proc.h"
#include "../head/reg.h"
#include "../head/elog.h"

#define ESTART	1
#define EEND	0
#define SSR3	0172516		/* Memory Management Register 3 */

	struct msgqhdr *logqp;		/* message queue header of logger */
	struct msghdr *lasterr;
	struct errtab *errtabs[];	/* pointers to device errtabs */
	int mmemwant, msgcore[];	/* interface to messages */

/*
 * Error logging system call
 * Activates and terminates error logging after ensuring
 * that all the prerequisites are met.
 */

serrlog()
{
	register struct msgqhdr *qp;
	register struct msghdr *mp;
	register struct estart *esp;

	if(!suser())
		return;
	if((qp = u.u_msgqhdr) == NULL) {
		u.u_error = ENOALOC;
		return;
	}
	switch(u.u_arg[0]) {
		case ESTART:
			if(logqp != NULL) {
				if(qp != logqp)
					u.u_error = EBUSY;
				return;
			}
			while((mp = msgsetup(sizeof(*esp))) == NULL) {
				mmemwant++;
				sleep(msgcore, PMSG-1);	/* grab space first */
			}
			esp = &mp[1];
			esp->es_cpu = cputype;
			if(cputype == 40)
				esp->es_mmr3 = 0;
			else
				esp->es_mmr3 = SSR3->integ;
			esp->es_nbdev = nblkdev;
			esp->es_ncdev = nchrdev;
			clrstat(&esp->es_conf);
			logqp = qp;		/* starts logging */
			logmsg(mp, E_GO);
			return;
		case EEND:
			if(qp != logqp)
				u.u_error = EFUNC;
			else
				termlog();
			return;
	}
	u.u_error = EINVAL;
}

/*
 * When error logging is started, clrstat examines all the
 * device errtabs. It clears all the IO statistics for
 * any devices that keeps such records. An additional
 * responsibility is to fill in the system configuration portion
 * of the error log startup record.
 */

clrstat(cp)
	int *cp;
{
	register struct errtab **ep;
	register int c, *p;
	struct iostat ios[0];

	cp->e_bconf = 0;
	cp->e_cconf = 0;
	for(ep = errtabs; *ep; ep++) {
		c = (1<<(*ep)->e_devnum.d_major);
		switch((*ep)->e_devtyp&E_TYPE) {
			case E_BLK:
				cp->e_bconf =| c;
				break;
			case E_CHR:
				cp->e_cconf =| c;
		}
		if((p = (*ep)->e_iostat) != NULL) {
			c = (sizeof(ios[0])/2)*((*ep)->e_nunit);
			do
				*p++ = 0;
			while(--c);
		}
	}
}

/*
 * Whenever the system's concept of the time of day is
 * changed, an error log entry showing the old and the new
 * time iw written by logtchg.
 */

logtchg()
{
	register struct msghdr *mp;
	struct etchg etp[0];

	if(logqp) {
		if((mp = msgsetup(sizeof(etp[0]))) != NULL) {
			(&mp[1])->e_ntime.hiword = u.u_ar0[R0];
			(&mp[1])->e_ntime.loword = u.u_ar0[R1];
			logmsg(mp, E_TCHG);
		}
	}
}

/*
 * Sends error message type "type" to the error logging process.
 * Time stamps all such messages.
 */

logmsg(mp, type)
	register struct msghdr *mp;
{
	mp->mq_sender = 0;
	mp->mq_type = E_NOACK;		/* no ack on message */
	(&mp[1])->e_type = type;
	(&mp[1])->e_ctime = time;
	msgsend(logqp, mp);
}

/*
 * Log an actual error for the device whose errtab is pointed to
 * by "ep". The "sclass" argument allows the driver to
 * attach a suffix to the error log entry type.
 */

logerr(ep, sclass)
	register struct errtab *ep;
{
	if(ep->e_emsg != NULL) {
		logmsg(ep->e_emsg, (ep->e_devtyp&E_TYPE)|sclass);
		ep->e_emsg = NULL;
	}
}

/*
 * Perform the actual work of terminating error logging.
 * Any pending error messages and a stop message are sent
 * to the logger.
 */

termlog()
{
	register struct errtab **ep;
	register int *ip;
	struct eend eep[0];

	spl6();		/* ensure no one sneaks in */
	for(ep = errtabs; *ep; ep++) {
		if((*ep)->e_emsg != NULL)
			logerr(*ep, E_EARLY);
	}
	if((ip = msgsetup(sizeof(eep[0]))) != NULL)
		logmsg(ip, E_STOP);
	logqp = NULL;	/* stops logging */
	spl0();
}

/*
 * Format a block device error record
 */

fmtblk(ep, devloc, nregs, bp)
	int *devloc;
	register struct errtab *ep;
	register struct buf *bp;
{
	register struct eblock *ebp;
	struct msghdr *mp;
	struct iostat ios[0];

	if(logqp == NULL)
		return;
	if((mp = ep->e_emsg) != NULL) {
		(&mp[1])->e_rtry++;
		return;
	}
	if(cputype != 70 && ep->e_devtyp&E_RH70)
		nregs =- 2;			/* no bae or cs3 on non 70s */
	if((mp = msgsetup(sizeof(*ebp)+(nregs<<1))) == NULL) {
		ep->e_aunit->io_unlog++;
		return;
	}
	ebp = &mp[1];
	ebp->e_dev = ep->e_devnum;	/* "true" major + minor */
	bcopy(ep->e_aunit, &ebp->e_stat, sizeof(ios[0])/2);
	ebp->e_bacty = blkacty;
	ebp->e_cacty = chracty;
	ebp->e_rtry = 0;
	ebp->e_nreg = nregs;
	ebp->e_rloc = devloc;
	bcopy(devloc, &ebp[1], nregs);
	ebp->e_dev.d_minor = bp->b_dev.d_minor;
	ebp->eb_bflg = bp->b_flags;
	ebp->eb_wcnt = bp->b_wcount;
	ebp->eb_maddr.hiword = bp->b_xmem;
	ebp->eb_maddr.loword = bp->b_addr;
	ebp->eb_bno = bp->b_blkno;
	ep->e_emsg = mp;
	lasterr = mp;
}
