<commit_before>/*-
 * Copyright (c) 1999 <NAME>.
 * 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.
 *
 * $FreeBSD$
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/buf.h>   
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/bus.h>

#include <machine/bus_pio.h>
#include <machine/bus.h>
#include <machine/clock.h>

#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_sim.h>
#include <cam/cam_xpt_sim.h>
#include <cam/cam_debug.h>

#include <cam/scsi/scsi_message.h>

#include <dev/aic/aic6360reg.h>
#include <dev/aic/aicvar.h>

static void aic_action __P((struct cam_sim *sim, union ccb *ccb));
static void aic_execute_scb __P((void *arg, bus_dma_segment_t *dm_segs,
				int nseg, int error));
static void aic_start __P((struct aic_softc *aic));
static void aic_select __P((struct aic_softc *aic));
static void aic_selected __P((struct aic_softc *aic));
static void aic_reselected __P((struct aic_softc *aic));
static void aic_cmd __P((struct aic_softc *aic));
static void aic_msgin __P((struct aic_softc *aic));
static void aic_handle_msgin __P((struct aic_softc *aic));
static void aic_sched_msgout __P((struct aic_softc *aic, u_int8_t msg));
static void aic_msgout __P((struct aic_softc *aic));
static void aic_datain __P((struct aic_softc *aic));
static void aic_dataout __P((struct aic_softc *aic));
static void aic_done __P((struct aic_softc *aic, struct aic_scb *scb));
static void aic_poll __P((struct cam_sim *sim));
static void aic_timeout __P((void *arg));
static void aic_scsi_reset __P((struct aic_softc *aic));
static void aic_chip_reset __P((struct aic_softc *aic));
static void aic_reset __P((struct aic_softc *aic, int initiate_reset));

static struct aic_scb *free_scbs;

static struct aic_scb *
aic_get_scb(struct aic_softc *aic)
{
	struct aic_scb *scb;
	int s = splcam();
	if ((scb = free_scbs) != NULL)
		free_scbs = (struct aic_scb *)free_scbs->ccb;
	splx(s);
	return (scb);
}

static void
aic_free_scb(struct aic_softc *aic, struct aic_scb *scb)
{
	int s = splcam();
	if ((aic->flags & AIC_RESOURCE_SHORTAGE) != 0 &&
	    (scb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) {
		scb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
		aic->flags &= ~AIC_RESOURCE_SHORTAGE;
	}
	scb->flags = 0;
	scb->ccb = (union ccb *)free_scbs;
	free_scbs = scb;
	splx(s);
}

static void
aic_action(struct cam_sim *sim, union ccb *ccb)
{
	struct aic_softc *aic;
	int s;

	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("aic_action\n"));

	aic = (struct aic_softc *)cam_sim_softc(sim);

	switch (ccb->ccb_h.func_code) {
	case XPT_SCSI_IO:	/* Execute the requested I/O operation */
	case XPT_RESET_DEV:	/* Bus Device Reset the specified SCSI device */
        {               
		struct aic_scb *scb;

		if ((scb = aic_get_scb(aic)) == NULL) {
			s = splcam();
			aic->flags |= AIC_RESOURCE_SHORTAGE;
			splx(s);
			xpt_freeze_simq(aic->sim, /*count*/1);
			ccb->ccb_h.status = CAM_REQUEUE_REQ;
			xpt_done(ccb);
			return;
		}

		scb->ccb = ccb;
		ccb->ccb_h.ccb_scb_ptr = scb;
		ccb->ccb_h.ccb_aic_ptr = aic;

		scb->target = ccb->ccb_h.target_id;
		scb->lun = ccb->ccb_h.target_lun;

		if (ccb->ccb_h.func_code == XPT_SCSI_IO) {
			scb->cmd_len = ccb->csio.cdb_len;
			if (ccb->ccb_h.flags & CAM_CDB_POINTER) {
				if (ccb->ccb_h.flags & CAM_CDB_PHYS) {
					ccb->ccb_h.status = CAM_REQ_INVALID;
					aic_free_scb(aic, scb);
					xpt_done(ccb);
					return;
				}
				scb->cmd_ptr = ccb->csio.cdb_io.cdb_ptr;
			} else {
				scb->cmd_ptr = ccb->csio.cdb_io.cdb_bytes;
			}
			if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
				if ((ccb->ccb_h.flags & CAM_SCATTER_VALID) ||
				    (ccb->ccb_h.flags & CAM_DATA_PHYS)) {
					ccb->ccb_h.status = CAM_REQ_INVALID;
					aic_free_scb(aic, scb);
					xpt_done(ccb);
					return;
				}
				scb->data_ptr = ccb->csio.data_ptr;
				scb->data_len = ccb->csio.dxfer_len;
			} else {
				scb->data_ptr = NULL;
				scb->data_len = 0;
			}
			aic_execute_scb(scb, NULL, 0, 0);
		} else {
			scb->flags |= SCB_DEVICE_RESET;
			aic_execute_scb(scb, NULL, 0, 0);
		}
		break;
	}
	case XPT_SET_TRAN_SETTINGS:
	{
		struct ccb_trans_settings *cts;
		struct aic_tinfo *ti;

		cts = &ccb->cts;
		ti = &aic->tinfo[ccb->ccb_h.target_id];

		s = splcam();

		if ((cts->valid & CCB_TRANS_DISC_VALID) != 0 &&
		    (aic->flags & AIC_DISC_ENABLE) != 0) {
			if ((cts->flags & CCB_TRANS_DISC_ENB) != 0)
				ti->flags |= TINFO_DISC_ENB;
			else
				ti->flags &= ~TINFO_DISC_ENB;
		}

		if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) {
			if ((cts->flags & CCB_TRANS_TAG_ENB) != 0)
				ti->flags |= TINFO_TAG_ENB;
			else
				ti->flags &= ~TINFO_TAG_ENB;
		}

		if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) {
			ti->goal.period = cts->sync_period;
			if (ti->goal.period != ti->current.period)
				ti->flags |= TINFO_SDTR_NEGO;
		}

		if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) {
			ti->goal.offset = cts->sync_offset;
			if (ti->goal.offset != ti->current.offset)
				ti->flags |= TINFO_SDTR_NEGO;
		}

		splx(s);
		ccb->ccb_h.status = CAM_REQ_CMP;
		xpt_done(ccb);
		break;
	}
	case XPT_GET_TRAN_SETTINGS:
	{
		struct ccb_trans_settings *cts;
		struct aic_tinfo *ti;

		cts = &ccb->cts;
		ti = &aic->tinfo[ccb->ccb_h.target_id];

		s = splcam();

		cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB);
		if ((ti->flags & TINFO_DISC_ENB) != 0)
			cts->flags |= CCB_TRANS_DISC_ENB;
		if ((ti->flags & TINFO_TAG_ENB) != 0)
			cts->flags |= CCB_TRANS_TAG_ENB;

		if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) {
			cts->sync_period = ti->current.period;
			cts->sync_offset = ti->current.offset;
		} else {
			cts->sync_period = ti->user.period;
			cts->sync_offset = ti->user.offset;
		}
		cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT;

		splx(s);

		cts->valid = CCB_TRANS_SYNC_RATE_VALID
			   | CCB_TRANS_SYNC_OFFSET_VALID
			   | CCB_TRANS_BUS_WIDTH_VALID
			   | CCB_TRANS_DISC_VALID
			   | CCB_TRANS_TQ_VALID;

		ccb->ccb_h.status = CAM_REQ_CMP;
		xpt_done(ccb);
		break;
	}
	case XPT_CALC_GEOMETRY:
	{
		struct ccb_calc_geometry *ccg;
		u_int32_t size_mb;
		u_int32_t secs_per_cylinder;
		int extended = 0;

		ccg = &ccb->ccg;
		size_mb = ccg->volume_size
			/ ((1024L * 1024L) / ccg->block_size);

		if (size_mb >= 1024 && extended) {
			ccg->heads = 255;
			ccg->secs_per_track = 63;
		} else {
			ccg->heads = 64;
			ccg->secs_per_track = 32;
		}
		secs_per_cylinder = ccg->heads * ccg->secs_per_track;
		ccg->cylinders = ccg->volume_size / secs_per_cylinder;
		ccb->ccb_h.status = CAM_REQ_CMP;
		xpt_done(ccb);
		break;
	}
	case XPT_RESET_BUS:		/* Reset the specified SCSI bus */
		aic_reset(aic, /*initiate_reset*/TRUE);
		ccb->ccb_h.status = CAM_REQ_CMP;
		xpt_done(ccb);
		break;
        case XPT_PATH_INQ:              /* Path routing inquiry */
        {       
                struct ccb_pathinq *cpi = &ccb->cpi;

                cpi->version_num = 1; /* XXX??? */
                cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE;
                cpi->target_sprt = 0;
                cpi->hba_misc = 0;
                cpi->hba_eng_cnt = 0;
                cpi->max_target = 7;
                cpi->max_lun = 7;
                cpi->initiator_id = aic->initiator;
                cpi->bus_id = cam_sim_bus(sim);
		cpi->base_transfer_speed = 3300;
                strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
                strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN);
                strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
                cpi->unit_number = cam_sim_unit(sim);
                cpi->ccb_h.status = CAM_REQ_CMP;
                xpt_done(ccb);
                break;
        }
	default:
                ccb->ccb_h.status = CAM_REQ_INVALID;
                xpt_done(ccb);
                break;
	}
}

static void
aic_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
{
	struct aic_scb *scb = (struct aic_scb *)arg;
	union ccb *ccb = scb->ccb;
	struct aic_softc *aic = (struct aic_softc *)ccb->ccb_h.ccb_aic_ptr;
	int s;

	s = splcam();

	if (ccb->ccb_h.status != CAM_REQ_INPROG) {
		splx(s);
		aic_free_scb(aic, scb);
		xpt_done(ccb);
		return;
	}

	scb->flags |= SCB_ACTIVE;
	ccb->ccb_h.status |= CAM_SIM_QUEUED;
	TAILQ_INSERT_TAIL(&aic->pending_ccbs, &ccb->ccb_h, sim_links.tqe);

	ccb->ccb_h.timeout_ch = timeout(aic_timeout, (caddr_t)scb,
		(ccb->ccb_h.timeout * hz) / 1000);

	aic_start(aic);
	splx(s);
}

static void
aic_start(struct aic_softc *aic)
{
	struct ccb_hdr *ccb_h;
	struct aic_tinfo *ti;

	if (aic->state != AIC_IDLE)
		return;

	TAILQ_FOREACH(ccb_h, &aic->pending_ccbs, sim_links.tqe) {
		ti = &aic->tinfo[ccb_h->target_id];
		if ((ti->lubusy & (1 << ccb_h->target_lun)) == 0) {
			TAILQ_REMOVE(&aic->pending_ccbs, ccb_h, sim_links.tqe);
			aic->nexus = (struct aic_scb *)ccb_h->ccb_scb_ptr;
			aic_select(aic);
			return;
		}
	}

	aic_outb(aic, SIMODE0, ENSELDI);
	aic_outb(aic, SIMODE1, ENSCSIRST);
	aic_outb(aic, SCSISEQ, ENRESELI);
}

static void
aic_select(struct aic_softc *aic)
{
	struct aic_scb *scb = aic->nexus;

	CAM_DEBUG(scb->ccb->ccb_h.path, CAM_DEBUG_TRACE,
		  ("aic_select - ccb %p\n", scb->ccb));

	aic->state = AIC_SELECTING;

	aic_outb(aic, DMACNTRL1, 0);
	aic_outb(aic, SCSIID, aic->initiator << OID_S | scb->target);
	aic_outb(aic, SXFRCTL1, STIMO_256ms | ENSTIMER |
	    (aic->flags & AIC_PARITY_ENABLE ? ENSPCHK : 0));

	aic_outb(aic, SIMODE0, ENSELDI|ENSELDO);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENSELTIMO);
	aic_outb(aic, SCSISEQ, ENRESELI|ENSELO|ENAUTOATNO);
}

static void
aic_selected(struct aic_softc *aic)
{
	struct aic_scb *scb = aic->nexus;
	union ccb *ccb = scb->ccb;
	struct aic_tinfo *ti = &aic->tinfo[scb->target];

	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
		  ("aic_selected - ccb %p\n", ccb));

	aic->state = AIC_HASNEXUS;

	if (scb->flags & SCB_DEVICE_RESET) {
		aic->msg_buf[0] = MSG_BUS_DEV_RESET;
		aic->msg_len = 1;
		aic->msg_outq = AIC_MSG_MSGBUF;
	} else {
		aic->msg_outq = AIC_MSG_IDENTIFY;
		if ((ti->flags & TINFO_TAG_ENB) != 0 &&
		    (ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0)
			aic->msg_outq |= AIC_MSG_TAG_Q;
		if ((ti->flags & TINFO_SDTR_NEGO) != 0)
			aic->msg_outq |= AIC_MSG_SDTR;
	}
	if ((aic->msg_outq & AIC_MSG_TAG_Q) == 0)
		ti->lubusy |= 1 << scb->lun;

	aic_outb(aic, CLRSINT0, CLRSELDO);
	aic_outb(aic, CLRSINT1, CLRBUSFREE);
	aic_outb(aic, SCSISEQ, ENAUTOATNP);
	aic_outb(aic, SIMODE0, 0);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
	aic_outb(aic, SCSIRATE, ti->scsirate);
}

static void
aic_reselected(struct aic_softc *aic)
{
	u_int8_t selid;
	struct aic_tinfo *ti;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reselected\n"));

	if (aic->nexus) {
		TAILQ_INSERT_HEAD(&aic->pending_ccbs,
		    &aic->nexus->ccb->ccb_h, sim_links.tqe);
		aic->nexus = NULL;
	}

	selid = aic_inb(aic, SELID) & SCSI_ID_MASK;
	if (selid & (selid - 1)) {
		;
	}

	aic->state = AIC_RESELECTED;
	aic->target = ffs(selid) - 1;
	aic->lun = -1;
	ti = &aic->tinfo[aic->target];

	aic_outb(aic, CLRSINT0, CLRSELDI);
	aic_outb(aic, CLRSINT1, CLRBUSFREE);
	aic_outb(aic, SIMODE0, 0);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
	aic_outb(aic, SCSISEQ, ENAUTOATNP);
	aic_outb(aic, SCSIRATE, ti->scsirate);
}

static __inline int
aic_spiordy(struct aic_softc *aic)
{
	int spincount = 100;
	u_int8_t spiordy = 0;

	while (spincount-- && !(aic_inb(aic, DMASTAT) & INTSTAT) &&
	    !(spiordy = aic_inb(aic, SSTAT0) & SPIORDY))
		;
	return (spiordy);
}

static void
aic_msgin(struct aic_softc *aic)
{
	int msglen;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_msgin\n"));

	aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
	aic_outb(aic, SXFRCTL0, CHEN|SPIOEN);

	aic->flags &= ~AIC_DROP_MSGIN;
	aic->msg_len = 0;
	do {
		if (aic_inb(aic, SSTAT1) & SCSIPERR) {
			aic_outb(aic, CLRSINT1, CLRSCSIPERR);
			aic->flags |= AIC_DROP_MSGIN;
			aic_sched_msgout(aic, MSG_PARITY_ERROR);
		}
		if ((aic->flags & AIC_DROP_MSGIN)) {
			aic_inb(aic, SCSIDAT);
			continue;
		}
		aic->msg_buf[aic->msg_len++] = aic_inb(aic, SCSIBUS);
		if (aic->msg_buf[0] == MSG_EXTENDED) {
			if (aic->msg_len < 2) {
				(void) aic_inb(aic, SCSIDAT);
				continue;
			}
			switch (aic->msg_buf[2]) {
			case MSG_EXT_SDTR:
				msglen = MSG_EXT_SDTR_LEN;
				break;
			case MSG_EXT_WDTR:
				msglen = MSG_EXT_WDTR_LEN;
				break;
			default:
				msglen = 0;
				break;
			}
			if (aic->msg_buf[1] != msglen) {
				aic->flags |= AIC_DROP_MSGIN;
				aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
			}
			msglen += 2;
		} else if (aic->msg_buf[0] & 0x20)
			msglen = 2;
		else
			msglen = 1;
		if (aic->msg_len == msglen) {
			aic_handle_msgin(aic);
			aic->msg_len = 0;
		}
		(void) aic_inb(aic, SCSIDAT);
	} while (aic_spiordy(aic));

	aic_outb(aic, SXFRCTL0, CHEN);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}

static void
aic_handle_msgin(struct aic_softc *aic)
{
	struct aic_scb *scb;
	struct ccb_hdr *ccb_h;
	struct aic_tinfo *ti;
	struct ccb_trans_settings neg;

	if (aic->state == AIC_RESELECTED) {
		int tag;

		ti = &aic->tinfo[aic->target];

		if (MSG_ISIDENTIFY(aic->msg_buf[0])) {
			aic->lun = aic->msg_buf[0] & MSG_IDENTIFY_LUNMASK;
			if (ti->flags & TINFO_TAG_ENB)
				return;
			else
				tag = -1;
		} else if (aic->msg_buf[0] >= MSG_SIMPLE_Q_TAG &&
			   aic->msg_buf[0] <= MSG_ORDERED_Q_TAG) {
			tag = aic->msg_buf[1];
		} else {
			aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
			return;
		}

		TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) {
			scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
			if (ccb_h->target_id == aic->target &&
			    ccb_h->target_lun == aic->lun &&
			    (tag == -1 || scb->tag == tag))
				break;
		}

		if (!ccb_h) {
			aic_sched_msgout(aic, MSG_ABORT); /* MSG_ABORT_TAG?*/
			return;
		}

		TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
		aic->nexus = scb;
		scb->flags &= ~SCB_DISCONNECTED;
		aic->state = AIC_HASNEXUS;
		return;
	}

	switch (aic->msg_buf[0]) {
	case MSG_CMDCOMPLETE: {
		struct ccb_scsiio *csio;
		scb = aic->nexus;
		ccb_h = &scb->ccb->ccb_h;
		csio = &scb->ccb->csio;
		ccb_h->status &= ~CAM_STATUS_MASK;
		if ((scb->flags & SCB_SENSE) != 0) {
			scb->flags &= ~SCB_SENSE;
			csio->sense_resid = scb->data_len;
			if (scb->status == SCSI_STATUS_OK) {
				ccb_h->status |=
				    CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID;
				scsi_sense_print(csio);
			} else {
				ccb_h->status |= CAM_AUTOSENSE_FAIL;
				printf("ccb %p sense failed %x\n",
				    ccb_h, scb->status);
			}
		} else {
			csio->scsi_status = scb->status;
			csio->resid = scb->data_len;
			if (scb->status == SCSI_STATUS_OK) {
				ccb_h->status |= CAM_REQ_CMP;
			} else if ((ccb_h->flags & CAM_DIS_AUTOSENSE) == 0 &&
			    (csio->scsi_status == SCSI_STATUS_CHECK_COND ||
			     csio->scsi_status == SCSI_STATUS_CMD_TERMINATED)) {
				scb->flags |= SCB_SENSE;
				aic->flags |= AIC_BUSFREE_OK;
				return;
			} else
				ccb_h->status |= CAM_SCSI_STATUS_ERROR;
		}
		aic_done(aic, scb);
		aic->flags |= AIC_BUSFREE_OK;
		break;
	}
	case MSG_EXTENDED:
		switch (aic->msg_buf[2]) {
		case MSG_EXT_SDTR:
			scb = aic->nexus;
			ti = &aic->tinfo[scb->target];
			if (ti->flags & TINFO_SDTR_SENT) {
				ti->current.period = aic->msg_buf[3];
				ti->current.offset = aic->msg_buf[4];
			} else {
				ti->current.period = aic->msg_buf[3] =
					max(ti->goal.period, aic->msg_buf[3]);
				ti->current.offset = aic->msg_buf[4] =
					min(ti->goal.offset, aic->msg_buf[4]);
				aic_sched_msgout(aic, 0);
			}
			ti->flags &= ~(TINFO_SDTR_SENT|TINFO_SDTR_NEGO);
			ti->scsirate = ti->current.offset ? ti->current.offset |
			    ((ti->current.period * 4 + 49) / 50 - 2) << 4 : 0;
			aic_outb(aic, SCSIRATE, ti->scsirate);
			neg.sync_period = ti->goal.period = ti->current.period;
			neg.sync_offset = ti->goal.offset = ti->current.offset;
			neg.valid = CCB_TRANS_SYNC_RATE_VALID
				  | CCB_TRANS_SYNC_OFFSET_VALID;
			ccb_h = &scb->ccb->ccb_h;
			xpt_setup_ccb(&neg.ccb_h, ccb_h->path, 1);
			xpt_async(AC_TRANSFER_NEG, ccb_h->path, &neg);
			break;
		case MSG_EXT_WDTR:
		default:
			aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
			break;
		}
		break;
	case MSG_DISCONNECT:
		scb = aic->nexus;
		ccb_h = &scb->ccb->ccb_h;
		TAILQ_INSERT_HEAD(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
		scb->flags |= SCB_DISCONNECTED;
		ti = &aic->tinfo[scb->target];
		aic->flags |= AIC_BUSFREE_OK;
		break;
	case MSG_MESSAGE_REJECT:
		switch (aic->msg_outq & -aic->msg_outq) {
		case AIC_MSG_TAG_Q:
			scb = aic->nexus;
			ti = &aic->tinfo[scb->target];
			ti->flags &= ~TINFO_TAG_ENB;
			break;
		case AIC_MSG_SDTR:
			scb = aic->nexus;
			ti = &aic->tinfo[scb->target];
			ti->current.period = ti->goal.period = 0;
			ti->current.offset = ti->goal.offset = 0;
			ti->flags &= ~(TINFO_SDTR_SENT|TINFO_SDTR_NEGO);
			ti->scsirate = 0;
			aic_outb(aic, SCSIRATE, ti->scsirate);
			neg.sync_period = ti->current.period;
			neg.sync_offset = ti->current.offset;
			neg.valid = CCB_TRANS_SYNC_RATE_VALID
				  | CCB_TRANS_SYNC_OFFSET_VALID;
			ccb_h = &scb->ccb->ccb_h;
			xpt_setup_ccb(&neg.ccb_h, ccb_h->path, 1);
			xpt_async(AC_TRANSFER_NEG, ccb_h->path, &neg);
			break;
		default:
			break;
		}
		break;
	case MSG_SAVEDATAPOINTER:
		break;  
	case MSG_RESTOREPOINTERS:
		break;
	case MSG_NOOP:
		break;
	default:
		aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
		break;
	}
}

static void
aic_sched_msgout(struct aic_softc *aic, u_int8_t msg)
{
	if (msg) {
		aic->msg_buf[0] = msg;
		aic->msg_len = 1;
	}
	aic->msg_outq |= AIC_MSG_MSGBUF;
	aic_outb(aic, SCSISIGO, aic_inb(aic, SCSISIGI) | ATNO);
}

static void
aic_msgout(struct aic_softc *aic)
{
	struct aic_scb *scb;
	union ccb *ccb;
	struct aic_tinfo *ti;
	int msgidx = 0;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_msgout\n"));

	aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
	aic_outb(aic, SXFRCTL0, CHEN|SPIOEN);

	if (aic->prev_phase == PH_MSGOUT)
		aic->msg_outq = aic->msg_sent;

	do {
		int q = aic->msg_outq;
		if (msgidx > 0 && msgidx == aic->msg_len) {
			q &= -q;
			aic->msg_sent |= q;
			aic->msg_outq ^= q;
			q = aic->msg_outq;
			msgidx = 0;
		}
		if (msgidx == 0) {
			switch (q & -q) {
			case AIC_MSG_IDENTIFY:
				scb = aic->nexus;
				ccb = scb->ccb;
				ti = &aic->tinfo[scb->target];
				aic->msg_buf[0] = MSG_IDENTIFY(scb->lun,
				    (ti->flags & TINFO_DISC_ENB) &&
				    (ccb->ccb_h.flags & CAM_DIS_DISCONNECT));
				aic->msg_len = 1;
				break;
			case AIC_MSG_TAG_Q:
				scb = aic->nexus;
				ccb = scb->ccb;
				aic->msg_buf[0] = ccb->csio.tag_action;
				aic->msg_buf[1] = scb->tag;
				aic->msg_len = 2;
				break;
			case AIC_MSG_SDTR:
				scb = aic->nexus;
				ti = &aic->tinfo[scb->target];
				aic->msg_buf[0] = MSG_EXTENDED;
				aic->msg_buf[1] = MSG_EXT_SDTR_LEN;
				aic->msg_buf[2] = MSG_EXT_SDTR;
				aic->msg_buf[3] = ti->goal.period;
				aic->msg_buf[4] = ti->goal.offset;
				aic->msg_len = MSG_EXT_SDTR_LEN + 2;
				ti->flags |= TINFO_SDTR_SENT;
				break;
			case AIC_MSG_MSGBUF:
				if (aic->msg_buf[0] == MSG_BUS_DEV_RESET ||
				    aic->msg_buf[0] == MSG_ABORT ||
				    aic->msg_buf[0] == MSG_ABORT_TAG)
					aic->flags |= AIC_BUSFREE_OK;
				break;
			}
		}
		if ((q & (q - 1)) == 0 && msgidx == aic->msg_len - 1)
			aic_outb(aic, CLRSINT1, CLRATNO);
		aic_outb(aic, SCSIDAT, aic->msg_buf[msgidx++]);
	} while (aic_spiordy(aic));

	aic_outb(aic, SXFRCTL0, CHEN);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}

static void
aic_datain(struct aic_softc *aic)
{
	struct aic_scb *scb = aic->nexus;
	u_int8_t dmastat;
	int n;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_datain\n"));

	aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
	aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN);

	while (scb->data_len > 0) {
		for (;;) {
			dmastat = aic_inb(aic, DMASTAT);
			if (dmastat & (INTSTAT|DFIFOFULL))
				break;
		}
		if (dmastat & DFIFOFULL) {
			n = FIFOSIZE;
		} else {
			while (!(aic_inb(aic, SSTAT2) & SEMPTY))
				;
			n = aic_inb(aic, FIFOSTAT);
		}
		n = imin(scb->data_len, n);
		if (n >= 12 && (aic->flags & AIC_DWIO_ENABLE)) {
			aic_outb(aic, DMACNTRL0, ENDMA|DWORDPIO);
			aic_insl(aic, DMADATALONG, scb->data_ptr, n >> 2);
			scb->data_ptr += n & ~3;
			scb->data_len -= n & ~3;
			n &= 3;
		}
		if (n >= 8) {
			aic_outb(aic, DMACNTRL0, ENDMA);
			aic_insw(aic, DMADATA, scb->data_ptr, n >> 1);
			scb->data_ptr += n & ~1;
			scb->data_len -= n & ~1;
			n &= 1;
		}
		if (n) {
			aic_outb(aic, DMACNTRL0, ENDMA|B8MODE);
			aic_insb(aic, DMADATA, scb->data_ptr, n);
			scb->data_ptr += n;
			scb->data_len -= n;
		}

		if (dmastat & INTSTAT)
			break;
	}

	aic_outb(aic, SXFRCTL0, CHEN);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}

static void
aic_dataout(struct aic_softc *aic)
{
	struct aic_scb *scb = aic->nexus;
	u_int8_t dmastat;
	int n;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_dataout\n"));

	aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
	aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN);

	while (scb->data_len > 0) {
		for (;;) {
			dmastat = aic_inb(aic, DMASTAT);
			if (dmastat & (INTSTAT|DFIFOEMP))
				break;
		}
		if (dmastat & INTSTAT)
			break;
		n = imin(scb->data_len, FIFOSIZE);
		if (n >= 12 && (aic->flags & AIC_DWIO_ENABLE)) {
			aic_outb(aic, DMACNTRL0, ENDMA|WRITE|DWORDPIO);
			aic_outsl(aic, DMADATALONG, scb->data_ptr, n >> 2);
			scb->data_ptr += n & ~3;
			scb->data_len -= n & ~3;
			n &= 3;
		}
		if (n >= 8) {
			aic_outb(aic, DMACNTRL0, ENDMA|WRITE);
			aic_outsl(aic, DMADATA, scb->data_ptr, n >> 2);
			scb->data_ptr += n & ~1;
			scb->data_len -= n & ~1;
			n &= 1;
		}
		if (n) {
			aic_outb(aic, DMACNTRL0, ENDMA|WRITE|B8MODE);
			aic_outsb(aic, DMADATA, scb->data_ptr, n);
			scb->data_ptr += n;
			scb->data_len -= n;
		}
	}

	for (;;) {
		dmastat = aic_inb(aic, DMASTAT);
		if (dmastat & INTSTAT)
			break;
		if ((dmastat & DFIFOEMP) && (aic_inb(aic, SSTAT2) & SEMPTY))
			break;
	}

	aic_outb(aic, SXFRCTL0, CHEN);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}

static void
aic_cmd(struct aic_softc *aic)
{
	struct aic_scb *scb = aic->nexus;
	struct scsi_request_sense sense_cmd;

	if (scb->flags & SCB_SENSE) {
		sense_cmd.opcode = REQUEST_SENSE;
		sense_cmd.byte2 = scb->lun << 2;
		sense_cmd.length = scb->ccb->csio.sense_len;
		sense_cmd.control = 0;
		sense_cmd.unused[0] = 0;
		sense_cmd.unused[1] = 0;
		scb->cmd_ptr = (u_int8_t *)&sense_cmd;
		scb->cmd_len = sizeof(sense_cmd);
		scb->data_ptr = (u_int8_t *)&scb->ccb->csio.sense_data;
		scb->data_len = scb->ccb->csio.sense_len;
	}

	aic_outb(aic, DMACNTRL0, ENDMA|WRITE);
	aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN);
	aic_outsw(aic, DMADATA, (u_int16_t *)scb->cmd_ptr,
		scb->cmd_len >> 1);
	while ((aic_inb(aic, SSTAT2) & SEMPTY) == 0 &&
	    (aic_inb(aic, DMASTAT) & INTSTAT) == 0)
		;
	aic_outb(aic, SXFRCTL0, CHEN);
}

static void
aic_done(struct aic_softc *aic, struct aic_scb *scb)
{
	union ccb *ccb = scb->ccb;

	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
		  ("aic_done - ccb %p status %x resid %d\n",
		   ccb, ccb->ccb_h.status, ccb->csio.resid));

	untimeout(aic_timeout, (caddr_t)scb, ccb->ccb_h.timeout_ch);

	if ((scb->flags & SCB_DEVICE_RESET) != 0 &&
	    ccb->ccb_h.func_code != XPT_RESET_DEV) {
		struct cam_path *path;
		struct ccb_hdr *ccb_h;
		cam_status error;

		error = xpt_create_path(&path, /*periph*/NULL,
					cam_sim_path(aic->sim),
					scb->target,
					CAM_LUN_WILDCARD);

		if (error == CAM_REQ_CMP)
			xpt_async(AC_SENT_BDR, path, NULL);

		ccb_h = TAILQ_FIRST(&aic->pending_ccbs);
		while (ccb_h != NULL) {
			struct aic_scb *pending_scb;

			pending_scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
			if (ccb_h->target_id == scb->target) {
				ccb_h->status = CAM_BDR_SENT;
				ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
				TAILQ_REMOVE(&aic->pending_ccbs,
				    &pending_scb->ccb->ccb_h, sim_links.tqe);
				aic_done(aic, pending_scb);
			} else {
				ccb_h->timeout_ch =
				    timeout(aic_timeout, (caddr_t)pending_scb,
					(ccb_h->timeout * hz) / 1000);
				ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
			}
		}

		ccb_h = TAILQ_FIRST(&aic->nexus_ccbs);
		while (ccb_h != NULL) {
			struct aic_scb *nexus_scb;

			nexus_scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
			if (ccb_h->target_id == scb->target) {
				ccb_h->status = CAM_BDR_SENT;
				ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
				TAILQ_REMOVE(&aic->nexus_ccbs,
				    &nexus_scb->ccb->ccb_h, sim_links.tqe);
				aic_done(aic, nexus_scb);
			} else {
				ccb_h->timeout_ch =
				    timeout(aic_timeout, (caddr_t)nexus_scb,
					(ccb_h->timeout * hz) / 1000);
				ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
			}
		}
	}

	if (aic->nexus == scb || scb->flags & SCB_DISCONNECTED)
		aic->tinfo[scb->target].lubusy &= ~(1 << scb->lun);
	
	if (aic->nexus == scb) {
		aic->nexus = NULL;
		aic->state = AIC_IDLE;
	}
	aic_free_scb(aic, scb);
	xpt_done(ccb);
}

static void
aic_poll(struct cam_sim *sim)
{
	aic_intr(cam_sim_softc(sim));
}

static void
aic_timeout(void *arg)
{
	struct aic_scb *scb = (struct aic_scb *)arg;
	union ccb *ccb = scb->ccb;
	struct aic_softc *aic = (struct aic_softc *)ccb->ccb_h.ccb_aic_ptr;
	int s;

	xpt_print_path(ccb->ccb_h.path);
	printf("ccb %p - timed out", ccb);
	if (aic->nexus && aic->nexus != scb)
		printf(", nexus %p", aic->nexus->ccb);
	printf("\n");

	s = splcam();

	if ((scb->flags & SCB_ACTIVE) == 0) {
		splx(s);
		xpt_print_path(ccb->ccb_h.path);
		printf("ccb %p - timed out already completed\n", ccb);
		return;
	}

	if ((scb->flags & SCB_DEVICE_RESET) == 0 && aic->nexus == scb) {
		struct ccb_hdr *ccb_h = &scb->ccb->ccb_h;

		if ((ccb_h->status & CAM_RELEASE_SIMQ) == 0) {
			xpt_freeze_simq(aic->sim, /*count*/1);
			ccb_h->status |= CAM_RELEASE_SIMQ;
		}

		TAILQ_FOREACH(ccb_h, &aic->pending_ccbs, sim_links.tqe) {
			untimeout(aic_timeout, (caddr_t)ccb_h->ccb_scb_ptr,
			    ccb_h->timeout_ch);
		}

		TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) {
			untimeout(aic_timeout, (caddr_t)ccb_h->ccb_scb_ptr,
			    ccb_h->timeout_ch);
		}

		scb->flags |= SCB_DEVICE_RESET;
		ccb->ccb_h.timeout_ch =
		    timeout(aic_timeout, (caddr_t)scb, 5 * hz);
		aic_sched_msgout(aic, MSG_BUS_DEV_RESET);
	} else {
		if (aic->nexus == scb) {
			ccb->ccb_h.status &= ~CAM_STATUS_MASK;
			ccb->ccb_h.status |= CAM_CMD_TIMEOUT;
			aic_done(aic, scb);
		}
		aic_reset(aic, /*initiate_reset*/TRUE);
	}

	splx(s);
}

void
aic_intr(void *arg)
{
	struct aic_softc *aic = (struct aic_softc *)arg;
	u_int8_t sstat0, sstat1;
	union ccb *ccb;
	struct aic_scb *scb;

	if (!(aic_inb(aic, DMASTAT) & INTSTAT))
		return;

	aic_outb(aic, DMACNTRL0, 0);

	sstat0 = aic_inb(aic, SSTAT0);
	sstat1 = aic_inb(aic, SSTAT1);

	if ((sstat1 & SCSIRSTI) != 0) {
		aic_outb(aic, CLRSINT1, CLRSCSIRSTI);
		aic_reset(aic, /*initiate_reset*/FALSE);
		return;
	}

	if ((sstat1 & SCSIPERR) != 0) {
		aic_outb(aic, CLRSINT1, CLRSCSIPERR);
		aic_sched_msgout(aic, MSG_PARITY_ERROR);
		aic_outb(aic, DMACNTRL0, INTEN);
		return;
	}

	if (aic_inb(aic, SSTAT4)) {
		aic_outb(aic, CLRSERR, CLRSYNCERR|CLRFWERR|CLRFRERR);
		aic_reset(aic, /*initiate_reset*/TRUE);
		return;
	}

	if (aic->state != AIC_HASNEXUS) {
		if ((sstat0 & SELDI) != 0) {
			aic_reselected(aic);
			aic_outb(aic, DMACNTRL0, INTEN);
			return;
		}

		if ((sstat0 & SELDO) != 0) {
			aic_selected(aic);
			aic_outb(aic, DMACNTRL0, INTEN);
			return;
		}

		if ((sstat1 & SELTO) != 0) {
			scb = aic->nexus;
			ccb = scb->ccb;
			ccb->ccb_h.status = CAM_SEL_TIMEOUT;
			aic_done(aic, scb);
			while ((sstat1 & BUSFREE) == 0)
				sstat1 = aic_inb(aic, SSTAT1);
			aic->flags |= AIC_BUSFREE_OK;
		}
	}

	if ((sstat1 & REQINIT) != 0) {
		u_int8_t phase = aic_inb(aic, SCSISIGI) & PH_MASK;
		aic_outb(aic, SCSISIGO, phase);
		aic_outb(aic, CLRSINT1, CLRPHASECHG);

		switch (phase) {
		case PH_MSGOUT:
			aic_msgout(aic);
			break;
		case PH_MSGIN:
			aic_msgin(aic);
			break;
		case PH_STAT:
			scb = aic->nexus;
			ccb = scb->ccb;
			aic_outb(aic, DMACNTRL0, 0);
			aic_outb(aic, SXFRCTL0, CHEN|SPIOEN);
			scb->status = aic_inb(aic, SCSIDAT);
			aic_outb(aic, SXFRCTL0, CHEN);
			break;
		case PH_CMD:
			aic_cmd(aic);
			break;
		case PH_DATAIN:
			aic_datain(aic);
			break;
		case PH_DATAOUT:
			aic_dataout(aic);
			break;
		}
		aic->prev_phase = phase;
		aic_outb(aic, DMACNTRL0, INTEN);
		return;
	}

	if ((sstat1 & BUSFREE) != 0) {
		aic_outb(aic, SCSISEQ, 0);
		aic_outb(aic, CLRSINT0, sstat0);
		aic_outb(aic, CLRSINT1, sstat1);
		aic_outb(aic, SIMODE0, ENSELDI);
		aic_outb(aic, SIMODE1, ENSCSIRST);
		aic_outb(aic, SCSISEQ, ENRESELI);
		if ((scb = aic->nexus)) {
			if ((aic->flags & AIC_BUSFREE_OK) == 0) {
				ccb = scb->ccb;
				ccb->ccb_h.status = CAM_UNEXP_BUSFREE;
				aic_done(aic, scb);
			} else if (scb->flags & SCB_SENSE) {
				aic->flags &= ~AIC_BUSFREE_OK;
				aic_select(aic);
				aic_outb(aic, DMACNTRL0, INTEN);
				return;
			}
		}
		aic->flags &= ~AIC_BUSFREE_OK;
		aic->state = AIC_IDLE;
		aic_start(aic);
		aic_outb(aic, DMACNTRL0, INTEN);
		return;
	}

	printf("aic_intr: unexpected intr sstat0 %x sstat1 %x\n",
		sstat0, sstat1);
	aic_outb(aic, DMACNTRL0, INTEN);
}

static void
aic_chip_reset(struct aic_softc *aic)
{
	/*
	 * Doc. recommends to clear these two registers before
	 * operations commence
	 */
	aic_outb(aic, SCSITEST, 0);
	aic_outb(aic, TEST, 0);

	/* Reset SCSI-FIFO and abort any transfers */
	aic_outb(aic, SXFRCTL0, CHEN|CLRCH|CLRSTCNT);

	/* Reset HOST-FIFO */
	aic_outb(aic, DMACNTRL0, RSTFIFO);
	aic_outb(aic, DMACNTRL1, 0);

	/* Disable all selection features */
	aic_outb(aic, SCSISEQ, 0);
	aic_outb(aic, SXFRCTL1, 0);

	/* Disable interrupts */
	aic_outb(aic, SIMODE0, 0);
	aic_outb(aic, SIMODE1, 0);

	/* Clear interrupts */
	aic_outb(aic, CLRSINT0, 0x7f);
	aic_outb(aic, CLRSINT1, 0xef);

	/* Disable synchronous transfers */
	aic_outb(aic, SCSIRATE, 0);

	/* Haven't seen ant errors (yet) */
	aic_outb(aic, CLRSERR, 0x07);

	/* Set our SCSI-ID */
	aic_outb(aic, SCSIID, aic->initiator << OID_S);
	aic_outb(aic, BRSTCNTRL, EISA_BRST_TIM);
}

static void
aic_scsi_reset(struct aic_softc *aic)
{
	aic_outb(aic, SCSISEQ, SCSIRSTO);
	DELAY(500);
	aic_outb(aic, SCSISEQ, 0);
	DELAY(50);
}

static void
aic_reset(struct aic_softc *aic, int initiate_reset)
{
	struct ccb_hdr *ccb_h;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reset\n"));

	if (initiate_reset)
		aic_scsi_reset(aic);
	aic_chip_reset(aic);

	xpt_async(AC_BUS_RESET, aic->path, NULL);

	while ((ccb_h = TAILQ_FIRST(&aic->pending_ccbs)) != NULL) {
		TAILQ_REMOVE(&aic->pending_ccbs, ccb_h, sim_links.tqe);
		ccb_h->status &= ~CAM_STATUS_MASK;
		ccb_h->status |= CAM_SCSI_BUS_RESET;
		aic_done(aic, (struct aic_scb *)ccb_h->ccb_scb_ptr);
	}

	while ((ccb_h = TAILQ_FIRST(&aic->nexus_ccbs)) != NULL) {
		TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
		ccb_h->status &= ~CAM_STATUS_MASK;
		ccb_h->status |= CAM_SCSI_BUS_RESET;
		aic_done(aic, (struct aic_scb *)ccb_h->ccb_scb_ptr);
	}

	if (aic->nexus) {
		ccb_h = &aic->nexus->ccb->ccb_h;
		ccb_h->status &= ~CAM_STATUS_MASK;<commit_msg>Non-functional changes. Add some comments before I start to forget how everything works myself.<commit_after>/*-
 * Copyright (c) 1999 <NAME>.
 * 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.
 *
 * $FreeBSD$
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/buf.h>   
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/bus.h>

#include <machine/bus_pio.h>
#include <machine/bus.h>
#include <machine/clock.h>

#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_sim.h>
#include <cam/cam_xpt_sim.h>
#include <cam/cam_debug.h>

#include <cam/scsi/scsi_message.h>

#include <dev/aic/aic6360reg.h>
#include <dev/aic/aicvar.h>

static void aic_action __P((struct cam_sim *sim, union ccb *ccb));
static void aic_execute_scb __P((void *arg, bus_dma_segment_t *dm_segs,
				int nseg, int error));
static void aic_start __P((struct aic_softc *aic));
static void aic_select __P((struct aic_softc *aic));
static void aic_selected __P((struct aic_softc *aic));
static void aic_reselected __P((struct aic_softc *aic));
static void aic_cmd __P((struct aic_softc *aic));
static void aic_msgin __P((struct aic_softc *aic));
static void aic_handle_msgin __P((struct aic_softc *aic));
static void aic_sched_msgout __P((struct aic_softc *aic, u_int8_t msg));
static void aic_msgout __P((struct aic_softc *aic));
static void aic_datain __P((struct aic_softc *aic));
static void aic_dataout __P((struct aic_softc *aic));
static void aic_done __P((struct aic_softc *aic, struct aic_scb *scb));
static void aic_poll __P((struct cam_sim *sim));
static void aic_timeout __P((void *arg));
static void aic_scsi_reset __P((struct aic_softc *aic));
static void aic_chip_reset __P((struct aic_softc *aic));
static void aic_reset __P((struct aic_softc *aic, int initiate_reset));

static struct aic_scb *free_scbs;

static struct aic_scb *
aic_get_scb(struct aic_softc *aic)
{
	struct aic_scb *scb;
	int s = splcam();
	if ((scb = free_scbs) != NULL)
		free_scbs = (struct aic_scb *)free_scbs->ccb;
	splx(s);
	return (scb);
}

static void
aic_free_scb(struct aic_softc *aic, struct aic_scb *scb)
{
	int s = splcam();
	if ((aic->flags & AIC_RESOURCE_SHORTAGE) != 0 &&
	    (scb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) {
		scb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
		aic->flags &= ~AIC_RESOURCE_SHORTAGE;
	}
	scb->flags = 0;
	scb->ccb = (union ccb *)free_scbs;
	free_scbs = scb;
	splx(s);
}

static void
aic_action(struct cam_sim *sim, union ccb *ccb)
{
	struct aic_softc *aic;
	int s;

	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("aic_action\n"));

	aic = (struct aic_softc *)cam_sim_softc(sim);

	switch (ccb->ccb_h.func_code) {
	case XPT_SCSI_IO:	/* Execute the requested I/O operation */
	case XPT_RESET_DEV:	/* Bus Device Reset the specified SCSI device */
        {               
		struct aic_scb *scb;

		if ((scb = aic_get_scb(aic)) == NULL) {
			s = splcam();
			aic->flags |= AIC_RESOURCE_SHORTAGE;
			splx(s);
			xpt_freeze_simq(aic->sim, /*count*/1);
			ccb->ccb_h.status = CAM_REQUEUE_REQ;
			xpt_done(ccb);
			return;
		}

		scb->ccb = ccb;
		ccb->ccb_h.ccb_scb_ptr = scb;
		ccb->ccb_h.ccb_aic_ptr = aic;

		scb->target = ccb->ccb_h.target_id;
		scb->lun = ccb->ccb_h.target_lun;

		if (ccb->ccb_h.func_code == XPT_SCSI_IO) {
			scb->cmd_len = ccb->csio.cdb_len;
			if (ccb->ccb_h.flags & CAM_CDB_POINTER) {
				if (ccb->ccb_h.flags & CAM_CDB_PHYS) {
					ccb->ccb_h.status = CAM_REQ_INVALID;
					aic_free_scb(aic, scb);
					xpt_done(ccb);
					return;
				}
				scb->cmd_ptr = ccb->csio.cdb_io.cdb_ptr;
			} else {
				scb->cmd_ptr = ccb->csio.cdb_io.cdb_bytes;
			}
			if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
				if ((ccb->ccb_h.flags & CAM_SCATTER_VALID) ||
				    (ccb->ccb_h.flags & CAM_DATA_PHYS)) {
					ccb->ccb_h.status = CAM_REQ_INVALID;
					aic_free_scb(aic, scb);
					xpt_done(ccb);
					return;
				}
				scb->data_ptr = ccb->csio.data_ptr;
				scb->data_len = ccb->csio.dxfer_len;
			} else {
				scb->data_ptr = NULL;
				scb->data_len = 0;
			}
			aic_execute_scb(scb, NULL, 0, 0);
		} else {
			scb->flags |= SCB_DEVICE_RESET;
			aic_execute_scb(scb, NULL, 0, 0);
		}
		break;
	}
	case XPT_SET_TRAN_SETTINGS:
	{
		struct ccb_trans_settings *cts;
		struct aic_tinfo *ti;

		cts = &ccb->cts;
		ti = &aic->tinfo[ccb->ccb_h.target_id];

		s = splcam();

		if ((cts->valid & CCB_TRANS_DISC_VALID) != 0 &&
		    (aic->flags & AIC_DISC_ENABLE) != 0) {
			if ((cts->flags & CCB_TRANS_DISC_ENB) != 0)
				ti->flags |= TINFO_DISC_ENB;
			else
				ti->flags &= ~TINFO_DISC_ENB;
		}

		if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) {
			if ((cts->flags & CCB_TRANS_TAG_ENB) != 0)
				ti->flags |= TINFO_TAG_ENB;
			else
				ti->flags &= ~TINFO_TAG_ENB;
		}

		if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) {
			ti->goal.period = cts->sync_period;
			if (ti->goal.period != ti->current.period)
				ti->flags |= TINFO_SDTR_NEGO;
		}

		if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) {
			ti->goal.offset = cts->sync_offset;
			if (ti->goal.offset != ti->current.offset)
				ti->flags |= TINFO_SDTR_NEGO;
		}

		splx(s);
		ccb->ccb_h.status = CAM_REQ_CMP;
		xpt_done(ccb);
		break;
	}
	case XPT_GET_TRAN_SETTINGS:
	{
		struct ccb_trans_settings *cts;
		struct aic_tinfo *ti;

		cts = &ccb->cts;
		ti = &aic->tinfo[ccb->ccb_h.target_id];

		s = splcam();

		cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB);
		if ((ti->flags & TINFO_DISC_ENB) != 0)
			cts->flags |= CCB_TRANS_DISC_ENB;
		if ((ti->flags & TINFO_TAG_ENB) != 0)
			cts->flags |= CCB_TRANS_TAG_ENB;

		if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) {
			cts->sync_period = ti->current.period;
			cts->sync_offset = ti->current.offset;
		} else {
			cts->sync_period = ti->user.period;
			cts->sync_offset = ti->user.offset;
		}
		cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT;

		splx(s);

		cts->valid = CCB_TRANS_SYNC_RATE_VALID
			   | CCB_TRANS_SYNC_OFFSET_VALID
			   | CCB_TRANS_BUS_WIDTH_VALID
			   | CCB_TRANS_DISC_VALID
			   | CCB_TRANS_TQ_VALID;

		ccb->ccb_h.status = CAM_REQ_CMP;
		xpt_done(ccb);
		break;
	}
	case XPT_CALC_GEOMETRY:
	{
		struct ccb_calc_geometry *ccg;
		u_int32_t size_mb;
		u_int32_t secs_per_cylinder;
		int extended = 0;

		ccg = &ccb->ccg;
		size_mb = ccg->volume_size
			/ ((1024L * 1024L) / ccg->block_size);

		if (size_mb >= 1024 && extended) {
			ccg->heads = 255;
			ccg->secs_per_track = 63;
		} else {
			ccg->heads = 64;
			ccg->secs_per_track = 32;
		}
		secs_per_cylinder = ccg->heads * ccg->secs_per_track;
		ccg->cylinders = ccg->volume_size / secs_per_cylinder;
		ccb->ccb_h.status = CAM_REQ_CMP;
		xpt_done(ccb);
		break;
	}
	case XPT_RESET_BUS:		/* Reset the specified SCSI bus */
		aic_reset(aic, /*initiate_reset*/TRUE);
		ccb->ccb_h.status = CAM_REQ_CMP;
		xpt_done(ccb);
		break;
        case XPT_PATH_INQ:              /* Path routing inquiry */
        {       
                struct ccb_pathinq *cpi = &ccb->cpi;

                cpi->version_num = 1; /* XXX??? */
                cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE;
                cpi->target_sprt = 0;
                cpi->hba_misc = 0;
                cpi->hba_eng_cnt = 0;
                cpi->max_target = 7;
                cpi->max_lun = 7;
                cpi->initiator_id = aic->initiator;
                cpi->bus_id = cam_sim_bus(sim);
		cpi->base_transfer_speed = 3300;
                strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
                strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN);
                strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
                cpi->unit_number = cam_sim_unit(sim);
                cpi->ccb_h.status = CAM_REQ_CMP;
                xpt_done(ccb);
                break;
        }
	default:
                ccb->ccb_h.status = CAM_REQ_INVALID;
                xpt_done(ccb);
                break;
	}
}

static void
aic_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
{
	struct aic_scb *scb = (struct aic_scb *)arg;
	union ccb *ccb = scb->ccb;
	struct aic_softc *aic = (struct aic_softc *)ccb->ccb_h.ccb_aic_ptr;
	int s;

	s = splcam();

	if (ccb->ccb_h.status != CAM_REQ_INPROG) {
		splx(s);
		aic_free_scb(aic, scb);
		xpt_done(ccb);
		return;
	}

	scb->flags |= SCB_ACTIVE;
	ccb->ccb_h.status |= CAM_SIM_QUEUED;
	TAILQ_INSERT_TAIL(&aic->pending_ccbs, &ccb->ccb_h, sim_links.tqe);

	ccb->ccb_h.timeout_ch = timeout(aic_timeout, (caddr_t)scb,
		(ccb->ccb_h.timeout * hz) / 1000);

	aic_start(aic);
	splx(s);
}

/*
 * Start another command if the controller is not busy.
 */
static void
aic_start(struct aic_softc *aic)
{
	struct ccb_hdr *ccb_h;
	struct aic_tinfo *ti;

	if (aic->state != AIC_IDLE)
		return;

	TAILQ_FOREACH(ccb_h, &aic->pending_ccbs, sim_links.tqe) {
		ti = &aic->tinfo[ccb_h->target_id];
		if ((ti->lubusy & (1 << ccb_h->target_lun)) == 0) {
			TAILQ_REMOVE(&aic->pending_ccbs, ccb_h, sim_links.tqe);
			aic->nexus = (struct aic_scb *)ccb_h->ccb_scb_ptr;
			aic_select(aic);
			return;
		}
	}

	aic_outb(aic, SIMODE0, ENSELDI);
	aic_outb(aic, SIMODE1, ENSCSIRST);
	aic_outb(aic, SCSISEQ, ENRESELI);
}

/*
 * Start a selection.
 */
static void
aic_select(struct aic_softc *aic)
{
	struct aic_scb *scb = aic->nexus;

	CAM_DEBUG(scb->ccb->ccb_h.path, CAM_DEBUG_TRACE,
		  ("aic_select - ccb %p\n", scb->ccb));

	aic->state = AIC_SELECTING;

	aic_outb(aic, DMACNTRL1, 0);
	aic_outb(aic, SCSIID, aic->initiator << OID_S | scb->target);
	aic_outb(aic, SXFRCTL1, STIMO_256ms | ENSTIMER |
	    (aic->flags & AIC_PARITY_ENABLE ? ENSPCHK : 0));

	aic_outb(aic, SIMODE0, ENSELDI|ENSELDO);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENSELTIMO);
	aic_outb(aic, SCSISEQ, ENRESELI|ENSELO|ENAUTOATNO);
}

/*
 * We have successfully selected a target, prepare for the information
 * transfer phases.
 */
static void
aic_selected(struct aic_softc *aic)
{
	struct aic_scb *scb = aic->nexus;
	union ccb *ccb = scb->ccb;
	struct aic_tinfo *ti = &aic->tinfo[scb->target];

	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
		  ("aic_selected - ccb %p\n", ccb));

	aic->state = AIC_HASNEXUS;

	if (scb->flags & SCB_DEVICE_RESET) {
		aic->msg_buf[0] = MSG_BUS_DEV_RESET;
		aic->msg_len = 1;
		aic->msg_outq = AIC_MSG_MSGBUF;
	} else {
		aic->msg_outq = AIC_MSG_IDENTIFY;
		if ((ti->flags & TINFO_TAG_ENB) != 0 &&
		    (ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0)
			aic->msg_outq |= AIC_MSG_TAG_Q;
		if ((ti->flags & TINFO_SDTR_NEGO) != 0)
			aic->msg_outq |= AIC_MSG_SDTR;
	}

	/* mark target/lun busy only for untagged operations */
	if ((aic->msg_outq & AIC_MSG_TAG_Q) == 0)
		ti->lubusy |= 1 << scb->lun;

	aic_outb(aic, CLRSINT0, CLRSELDO);
	aic_outb(aic, CLRSINT1, CLRBUSFREE);
	aic_outb(aic, SCSISEQ, ENAUTOATNP);
	aic_outb(aic, SIMODE0, 0);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
	aic_outb(aic, SCSIRATE, ti->scsirate);
}

/*
 * We are re-selected by a target, save the target id and wait for the
 * target to further identify itself.
 */
static void
aic_reselected(struct aic_softc *aic)
{
	u_int8_t selid;
	struct aic_tinfo *ti;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reselected\n"));

	/*
	 * If we have started a selection, it must have lost out in
	 * the arbitration, put the command back to the pending queue.
	 */
	if (aic->nexus) {
		TAILQ_INSERT_HEAD(&aic->pending_ccbs,
		    &aic->nexus->ccb->ccb_h, sim_links.tqe);
		aic->nexus = NULL;
	}

	selid = aic_inb(aic, SELID) & SCSI_ID_MASK;
	if (selid & (selid - 1)) {
		/* this should never have happened */
		aic_reset(aic, /*initiate_reset*/TRUE);
		return;
	}

	aic->state = AIC_RESELECTED;
	aic->target = ffs(selid) - 1;
	aic->lun = -1;
	ti = &aic->tinfo[aic->target];

	aic_outb(aic, CLRSINT0, CLRSELDI);
	aic_outb(aic, CLRSINT1, CLRBUSFREE);
	aic_outb(aic, SIMODE0, 0);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
	aic_outb(aic, SCSISEQ, ENAUTOATNP);
	aic_outb(aic, SCSIRATE, ti->scsirate);
}

/*
 * Wait for SPIORDY (SCSI PIO ready) flag, or a phase change.
 */
static __inline int
aic_spiordy(struct aic_softc *aic)
{
	int spincount = 100;
	u_int8_t spiordy = 0;

	while (spincount-- && !(aic_inb(aic, DMASTAT) & INTSTAT) &&
	    !(spiordy = aic_inb(aic, SSTAT0) & SPIORDY))
		;
	return (spiordy);
}

/*
 * Read messages.
 */
static void
aic_msgin(struct aic_softc *aic)
{
	int msglen;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_msgin\n"));

	aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
	aic_outb(aic, SXFRCTL0, CHEN|SPIOEN);

	aic->flags &= ~AIC_DROP_MSGIN;
	aic->msg_len = 0;
	do {
		/*
		 * If a parity error is detected, drop the remaining
		 * bytes and inform the target so it could resend
		 * the messages.
		 */
		if (aic_inb(aic, SSTAT1) & SCSIPERR) {
			aic_outb(aic, CLRSINT1, CLRSCSIPERR);
			aic->flags |= AIC_DROP_MSGIN;
			aic_sched_msgout(aic, MSG_PARITY_ERROR);
		}
		if ((aic->flags & AIC_DROP_MSGIN)) {
			aic_inb(aic, SCSIDAT);
			continue;
		}
		/* read the message byte without ACKing on it */
		aic->msg_buf[aic->msg_len++] = aic_inb(aic, SCSIBUS);
		if (aic->msg_buf[0] == MSG_EXTENDED) {
			if (aic->msg_len < 2) {
				(void) aic_inb(aic, SCSIDAT);
				continue;
			}
			switch (aic->msg_buf[2]) {
			case MSG_EXT_SDTR:
				msglen = MSG_EXT_SDTR_LEN;
				break;
			case MSG_EXT_WDTR:
				msglen = MSG_EXT_WDTR_LEN;
				break;
			default:
				msglen = 0;
				break;
			}
			if (aic->msg_buf[1] != msglen) {
				aic->flags |= AIC_DROP_MSGIN;
				aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
			}
			msglen += 2;
		} else if (aic->msg_buf[0] & 0x20)
			msglen = 2;
		else
			msglen = 1;
		/*
		 * If we have a complete message, handle it before the final
		 * ACK (in case we decide to reject the message).
		 */
		if (aic->msg_len == msglen) {
			aic_handle_msgin(aic);
			aic->msg_len = 0;
		}
		/* ACK on the message byte */
		(void) aic_inb(aic, SCSIDAT);
	} while (aic_spiordy(aic));

	aic_outb(aic, SXFRCTL0, CHEN);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}

/*
 * Handle a message.
 */
static void
aic_handle_msgin(struct aic_softc *aic)
{
	struct aic_scb *scb;
	struct ccb_hdr *ccb_h;
	struct aic_tinfo *ti;
	struct ccb_trans_settings neg;

	if (aic->state == AIC_RESELECTED) {
		int tag;

		ti = &aic->tinfo[aic->target];
		/*
		 * We expect to see an IDENTIFY message, possibly followed
		 * by a tagged queue message.
		 */
		if (MSG_ISIDENTIFY(aic->msg_buf[0])) {
			aic->lun = aic->msg_buf[0] & MSG_IDENTIFY_LUNMASK;
			if (ti->flags & TINFO_TAG_ENB)
				return;
			else
				tag = -1;
		} else if (aic->msg_buf[0] >= MSG_SIMPLE_Q_TAG &&
			   aic->msg_buf[0] <= MSG_ORDERED_Q_TAG) {
			tag = aic->msg_buf[1];
		} else {
			aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
			return;
		}

		/* Find the nexus */
		TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) {
			scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
			if (ccb_h->target_id == aic->target &&
			    ccb_h->target_lun == aic->lun &&
			    (tag == -1 || scb->tag == tag))
				break;
		}

		/* ABORT if nothing is found */
		if (!ccb_h) {
			aic_sched_msgout(aic, MSG_ABORT); /* MSG_ABORT_TAG?*/
			return;
		}

		/* Reestablish the nexus */
		TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
		aic->nexus = scb;
		scb->flags &= ~SCB_DISCONNECTED;
		aic->state = AIC_HASNEXUS;
		return;
	}

	switch (aic->msg_buf[0]) {
	case MSG_CMDCOMPLETE: {
		struct ccb_scsiio *csio;
		scb = aic->nexus;
		ccb_h = &scb->ccb->ccb_h;
		csio = &scb->ccb->csio;
		ccb_h->status &= ~CAM_STATUS_MASK;
		if ((scb->flags & SCB_SENSE) != 0) {
			/* auto REQUEST SENSE command */
			scb->flags &= ~SCB_SENSE;
			csio->sense_resid = scb->data_len;
			if (scb->status == SCSI_STATUS_OK) {
				ccb_h->status |=
				    CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID;
				/*scsi_sense_print(csio);*/
			} else {
				ccb_h->status |= CAM_AUTOSENSE_FAIL;
				printf("ccb %p sense failed %x\n",
				    ccb_h, scb->status);
			}
		} else {
			csio->scsi_status = scb->status;
			csio->resid = scb->data_len;
			if (scb->status == SCSI_STATUS_OK) {
				/* everything goes well */
				ccb_h->status |= CAM_REQ_CMP;
			} else if ((ccb_h->flags & CAM_DIS_AUTOSENSE) == 0 &&
			    (csio->scsi_status == SCSI_STATUS_CHECK_COND ||
			     csio->scsi_status == SCSI_STATUS_CMD_TERMINATED)) {
				/* try to retrieve sense information */
				scb->flags |= SCB_SENSE;
				aic->flags |= AIC_BUSFREE_OK;
				return;
			} else
				ccb_h->status |= CAM_SCSI_STATUS_ERROR;
		}
		aic_done(aic, scb);
		aic->flags |= AIC_BUSFREE_OK;
		break;
	}
	case MSG_EXTENDED:
		switch (aic->msg_buf[2]) {
		case MSG_EXT_SDTR:
			scb = aic->nexus;
			ti = &aic->tinfo[scb->target];
			if (ti->flags & TINFO_SDTR_SENT) {
				ti->current.period = aic->msg_buf[3];
				ti->current.offset = aic->msg_buf[4];
			} else {
				ti->current.period = aic->msg_buf[3] =
					max(ti->goal.period, aic->msg_buf[3]);
				ti->current.offset = aic->msg_buf[4] =
					min(ti->goal.offset, aic->msg_buf[4]);
				/*
				 * The target initiated the negotiation,
				 * send back a response.
				 */
				aic_sched_msgout(aic, 0);
			}
			ti->flags &= ~(TINFO_SDTR_SENT|TINFO_SDTR_NEGO);
			ti->scsirate = ti->current.offset ? ti->current.offset |
			    ((ti->current.period * 4 + 49) / 50 - 2) << 4 : 0;
			aic_outb(aic, SCSIRATE, ti->scsirate);
			neg.sync_period = ti->goal.period = ti->current.period;
			neg.sync_offset = ti->goal.offset = ti->current.offset;
			neg.valid = CCB_TRANS_SYNC_RATE_VALID
				  | CCB_TRANS_SYNC_OFFSET_VALID;
			ccb_h = &scb->ccb->ccb_h;
			xpt_setup_ccb(&neg.ccb_h, ccb_h->path, 1);
			xpt_async(AC_TRANSFER_NEG, ccb_h->path, &neg);
			break;
		case MSG_EXT_WDTR:
		default:
			aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
			break;
		}
		break;
	case MSG_DISCONNECT:
		scb = aic->nexus;
		ccb_h = &scb->ccb->ccb_h;
		TAILQ_INSERT_HEAD(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
		scb->flags |= SCB_DISCONNECTED;
		ti = &aic->tinfo[scb->target];
		aic->flags |= AIC_BUSFREE_OK;
		break;
	case MSG_MESSAGE_REJECT:
		switch (aic->msg_outq & -aic->msg_outq) {
		case AIC_MSG_TAG_Q:
			scb = aic->nexus;
			ti = &aic->tinfo[scb->target];
			ti->flags &= ~TINFO_TAG_ENB;
			ti->lubusy |= 1 << scb->lun;
			break;
		case AIC_MSG_SDTR:
			scb = aic->nexus;
			ti = &aic->tinfo[scb->target];
			ti->current.period = ti->goal.period = 0;
			ti->current.offset = ti->goal.offset = 0;
			ti->flags &= ~(TINFO_SDTR_SENT|TINFO_SDTR_NEGO);
			ti->scsirate = 0;
			aic_outb(aic, SCSIRATE, ti->scsirate);
			neg.sync_period = ti->current.period;
			neg.sync_offset = ti->current.offset;
			neg.valid = CCB_TRANS_SYNC_RATE_VALID
				  | CCB_TRANS_SYNC_OFFSET_VALID;
			ccb_h = &scb->ccb->ccb_h;
			xpt_setup_ccb(&neg.ccb_h, ccb_h->path, 1);
			xpt_async(AC_TRANSFER_NEG, ccb_h->path, &neg);
			break;
		default:
			break;
		}
		break;
	case MSG_SAVEDATAPOINTER:
		break;  
	case MSG_RESTOREPOINTERS:
		break;
	case MSG_NOOP:
		break;
	default:
		aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
		break;
	}
}

/*
 * Raise ATNO to signal the target that we have a message for it.
 */
static void
aic_sched_msgout(struct aic_softc *aic, u_int8_t msg)
{
	if (msg) {
		aic->msg_buf[0] = msg;
		aic->msg_len = 1;
	}
	aic->msg_outq |= AIC_MSG_MSGBUF;
	aic_outb(aic, SCSISIGO, aic_inb(aic, SCSISIGI) | ATNO);
}

/*
 * Send messages.
 */
static void
aic_msgout(struct aic_softc *aic)
{
	struct aic_scb *scb;
	union ccb *ccb;
	struct aic_tinfo *ti;
	int msgidx = 0;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_msgout\n"));

	aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
	aic_outb(aic, SXFRCTL0, CHEN|SPIOEN);

	/*
	 * If the previous phase is also the message out phase,
	 * we need to retransmit all the messages, probably
	 * because the target has detected a parity error during
	 * the past transmission.
	 */
	if (aic->prev_phase == PH_MSGOUT)
		aic->msg_outq = aic->msg_sent;

	do {
		int q = aic->msg_outq;
		if (msgidx > 0 && msgidx == aic->msg_len) {
			/* complete message sent, start the next one */
			q &= -q;
			aic->msg_sent |= q;
			aic->msg_outq ^= q;
			q = aic->msg_outq;
			msgidx = 0;
		}
		if (msgidx == 0) {
			/* setup the message */
			switch (q & -q) {
			case AIC_MSG_IDENTIFY:
				scb = aic->nexus;
				ccb = scb->ccb;
				ti = &aic->tinfo[scb->target];
				aic->msg_buf[0] = MSG_IDENTIFY(scb->lun,
				    (ti->flags & TINFO_DISC_ENB) &&
				    (ccb->ccb_h.flags & CAM_DIS_DISCONNECT));
				aic->msg_len = 1;
				break;
			case AIC_MSG_TAG_Q:
				scb = aic->nexus;
				ccb = scb->ccb;
				aic->msg_buf[0] = ccb->csio.tag_action;
				aic->msg_buf[1] = scb->tag;
				aic->msg_len = 2;
				break;
			case AIC_MSG_SDTR:
				scb = aic->nexus;
				ti = &aic->tinfo[scb->target];
				aic->msg_buf[0] = MSG_EXTENDED;
				aic->msg_buf[1] = MSG_EXT_SDTR_LEN;
				aic->msg_buf[2] = MSG_EXT_SDTR;
				aic->msg_buf[3] = ti->goal.period;
				aic->msg_buf[4] = ti->goal.offset;
				aic->msg_len = MSG_EXT_SDTR_LEN + 2;
				ti->flags |= TINFO_SDTR_SENT;
				break;
			case AIC_MSG_MSGBUF:
				/* a single message already in the buffer */
				if (aic->msg_buf[0] == MSG_BUS_DEV_RESET ||
				    aic->msg_buf[0] == MSG_ABORT ||
				    aic->msg_buf[0] == MSG_ABORT_TAG)
					aic->flags |= AIC_BUSFREE_OK;
				break;
			}
		}
		/*
		 * If this is the last message byte of all messages,
		 * clear ATNO to signal transmission complete.
		 */
		if ((q & (q - 1)) == 0 && msgidx == aic->msg_len - 1)
			aic_outb(aic, CLRSINT1, CLRATNO);
		/* transmit the message byte */
		aic_outb(aic, SCSIDAT, aic->msg_buf[msgidx++]);
	} while (aic_spiordy(aic));

	aic_outb(aic, SXFRCTL0, CHEN);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}

/*
 * Read data bytes.
 */
static void
aic_datain(struct aic_softc *aic)
{
	struct aic_scb *scb = aic->nexus;
	u_int8_t dmastat;
	int n;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_datain\n"));

	aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
	aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN);

	while (scb->data_len > 0) {
		for (;;) {
			/* wait for the fifo the fill up or a phase change */
			dmastat = aic_inb(aic, DMASTAT);
			if (dmastat & (INTSTAT|DFIFOFULL))
				break;
		}
		if (dmastat & DFIFOFULL) {
			n = FIFOSIZE;
		} else {
			/*
			 * No more data, wait for the remaining bytes in
			 * the scsi fifo to be transfer to the host fifo.
			 */
			while (!(aic_inb(aic, SSTAT2) & SEMPTY))
				;
			n = aic_inb(aic, FIFOSTAT);
		}
		n = imin(scb->data_len, n);
		if (n >= 12 && (aic->flags & AIC_DWIO_ENABLE)) {
			aic_outb(aic, DMACNTRL0, ENDMA|DWORDPIO);
			aic_insl(aic, DMADATALONG, scb->data_ptr, n >> 2);
			scb->data_ptr += n & ~3;
			scb->data_len -= n & ~3;
			n &= 3;
		}
		if (n >= 8) {
			aic_outb(aic, DMACNTRL0, ENDMA);
			aic_insw(aic, DMADATA, scb->data_ptr, n >> 1);
			scb->data_ptr += n & ~1;
			scb->data_len -= n & ~1;
			n &= 1;
		}
		if (n) {
			aic_outb(aic, DMACNTRL0, ENDMA|B8MODE);
			aic_insb(aic, DMADATA, scb->data_ptr, n);
			scb->data_ptr += n;
			scb->data_len -= n;
		}

		if (dmastat & INTSTAT)
			break;
	}

	aic_outb(aic, SXFRCTL0, CHEN);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}

/*
 * Send data bytes.
 */
static void
aic_dataout(struct aic_softc *aic)
{
	struct aic_scb *scb = aic->nexus;
	u_int8_t dmastat;
	int n;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_dataout\n"));

	aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
	aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN);

	while (scb->data_len > 0) {
		for (;;) {
			/* wait for the fifo to clear up or a phase change */
			dmastat = aic_inb(aic, DMASTAT);
			if (dmastat & (INTSTAT|DFIFOEMP))
				break;
		}
		if (dmastat & INTSTAT)
			break;
		n = imin(scb->data_len, FIFOSIZE);
		if (n >= 12 && (aic->flags & AIC_DWIO_ENABLE)) {
			aic_outb(aic, DMACNTRL0, ENDMA|WRITE|DWORDPIO);
			aic_outsl(aic, DMADATALONG, scb->data_ptr, n >> 2);
			scb->data_ptr += n & ~3;
			scb->data_len -= n & ~3;
			n &= 3;
		}
		if (n >= 8) {
			aic_outb(aic, DMACNTRL0, ENDMA|WRITE);
			aic_outsl(aic, DMADATA, scb->data_ptr, n >> 2);
			scb->data_ptr += n & ~1;
			scb->data_len -= n & ~1;
			n &= 1;
		}
		if (n) {
			aic_outb(aic, DMACNTRL0, ENDMA|WRITE|B8MODE);
			aic_outsb(aic, DMADATA, scb->data_ptr, n);
			scb->data_ptr += n;
			scb->data_len -= n;
		}
	}

	for (;;) {
		/* wait until all bytes in the fifos are transmitted */
		dmastat = aic_inb(aic, DMASTAT);
		if (dmastat & INTSTAT)
			break;
		if ((dmastat & DFIFOEMP) && (aic_inb(aic, SSTAT2) & SEMPTY))
			break;
	}

	aic_outb(aic, SXFRCTL0, CHEN);
	aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}

/*
 * Send the scsi command.
 */
static void
aic_cmd(struct aic_softc *aic)
{
	struct aic_scb *scb = aic->nexus;
	struct scsi_request_sense sense_cmd;

	if (scb->flags & SCB_SENSE) {
		/* autosense request */
		sense_cmd.opcode = REQUEST_SENSE;
		sense_cmd.byte2 = scb->lun << 2;
		sense_cmd.length = scb->ccb->csio.sense_len;
		sense_cmd.control = 0;
		sense_cmd.unused[0] = 0;
		sense_cmd.unused[1] = 0;
		scb->cmd_ptr = (u_int8_t *)&sense_cmd;
		scb->cmd_len = sizeof(sense_cmd);
		scb->data_ptr = (u_int8_t *)&scb->ccb->csio.sense_data;
		scb->data_len = scb->ccb->csio.sense_len;
	}

	aic_outb(aic, DMACNTRL0, ENDMA|WRITE);
	aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN);
	aic_outsw(aic, DMADATA, (u_int16_t *)scb->cmd_ptr,
		scb->cmd_len >> 1);
	while ((aic_inb(aic, SSTAT2) & SEMPTY) == 0 &&
	    (aic_inb(aic, DMASTAT) & INTSTAT) == 0)
		;
	aic_outb(aic, SXFRCTL0, CHEN);
}

/*
 * Finish off a command. The caller is responsible to remove the ccb
 * from any queue.
 */
static void
aic_done(struct aic_softc *aic, struct aic_scb *scb)
{
	union ccb *ccb = scb->ccb;

	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
		  ("aic_done - ccb %p status %x resid %d\n",
		   ccb, ccb->ccb_h.status, ccb->csio.resid));

	untimeout(aic_timeout, (caddr_t)scb, ccb->ccb_h.timeout_ch);

	if ((scb->flags & SCB_DEVICE_RESET) != 0 &&
	    ccb->ccb_h.func_code != XPT_RESET_DEV) {
		struct cam_path *path;
		struct ccb_hdr *ccb_h;
		cam_status error;

		error = xpt_create_path(&path, /*periph*/NULL,
					cam_sim_path(aic->sim),
					scb->target,
					CAM_LUN_WILDCARD);

		if (error == CAM_REQ_CMP)
			xpt_async(AC_SENT_BDR, path, NULL);

		ccb_h = TAILQ_FIRST(&aic->pending_ccbs);
		while (ccb_h != NULL) {
			struct aic_scb *pending_scb;

			pending_scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
			if (ccb_h->target_id == scb->target) {
				ccb_h->status = CAM_BDR_SENT;
				ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
				TAILQ_REMOVE(&aic->pending_ccbs,
				    &pending_scb->ccb->ccb_h, sim_links.tqe);
				aic_done(aic, pending_scb);
			} else {
				ccb_h->timeout_ch =
				    timeout(aic_timeout, (caddr_t)pending_scb,
					(ccb_h->timeout * hz) / 1000);
				ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
			}
		}

		ccb_h = TAILQ_FIRST(&aic->nexus_ccbs);
		while (ccb_h != NULL) {
			struct aic_scb *nexus_scb;

			nexus_scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
			if (ccb_h->target_id == scb->target) {
				ccb_h->status = CAM_BDR_SENT;
				ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
				TAILQ_REMOVE(&aic->nexus_ccbs,
				    &nexus_scb->ccb->ccb_h, sim_links.tqe);
				aic_done(aic, nexus_scb);
			} else {
				ccb_h->timeout_ch =
				    timeout(aic_timeout, (caddr_t)nexus_scb,
					(ccb_h->timeout * hz) / 1000);
				ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
			}
		}
	}

	if (aic->nexus == scb || scb->flags & SCB_DISCONNECTED)
		aic->tinfo[scb->target].lubusy &= ~(1 << scb->lun);
	
	if (aic->nexus == scb) {
		aic->nexus = NULL;
		aic->state = AIC_IDLE;
	}
	aic_free_scb(aic, scb);
	xpt_done(ccb);
}

static void
aic_poll(struct cam_sim *sim)
{
	aic_intr(cam_sim_softc(sim));
}

static void
aic_timeout(void *arg)
{
	struct aic_scb *scb = (struct aic_scb *)arg;
	union ccb *ccb = scb->ccb;
	struct aic_softc *aic = (struct aic_softc *)ccb->ccb_h.ccb_aic_ptr;
	int s;

	xpt_print_path(ccb->ccb_h.path);
	printf("ccb %p - timed out", ccb);
	if (aic->nexus && aic->nexus != scb)
		printf(", nexus %p", aic->nexus->ccb);
	printf("\n");

	s = splcam();

	if ((scb->flags & SCB_ACTIVE) == 0) {
		splx(s);
		xpt_print_path(ccb->ccb_h.path);
		printf("ccb %p - timed out already completed\n", ccb);
		return;
	}

	if ((scb->flags & SCB_DEVICE_RESET) == 0 && aic->nexus == scb) {
		struct ccb_hdr *ccb_h = &scb->ccb->ccb_h;

		if ((ccb_h->status & CAM_RELEASE_SIMQ) == 0) {
			xpt_freeze_simq(aic->sim, /*count*/1);
			ccb_h->status |= CAM_RELEASE_SIMQ;
		}

		TAILQ_FOREACH(ccb_h, &aic->pending_ccbs, sim_links.tqe) {
			untimeout(aic_timeout, (caddr_t)ccb_h->ccb_scb_ptr,
			    ccb_h->timeout_ch);
		}

		TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) {
			untimeout(aic_timeout, (caddr_t)ccb_h->ccb_scb_ptr,
			    ccb_h->timeout_ch);
		}

		scb->flags |= SCB_DEVICE_RESET;
		ccb->ccb_h.timeout_ch =
		    timeout(aic_timeout, (caddr_t)scb, 5 * hz);
		aic_sched_msgout(aic, MSG_BUS_DEV_RESET);
	} else {
		if (aic->nexus == scb) {
			ccb->ccb_h.status &= ~CAM_STATUS_MASK;
			ccb->ccb_h.status |= CAM_CMD_TIMEOUT;
			aic_done(aic, scb);
		}
		aic_reset(aic, /*initiate_reset*/TRUE);
	}

	splx(s);
}

void
aic_intr(void *arg)
{
	struct aic_softc *aic = (struct aic_softc *)arg;
	u_int8_t sstat0, sstat1;
	union ccb *ccb;
	struct aic_scb *scb;

	if (!(aic_inb(aic, DMASTAT) & INTSTAT))
		return;

	aic_outb(aic, DMACNTRL0, 0);

	sstat0 = aic_inb(aic, SSTAT0);
	sstat1 = aic_inb(aic, SSTAT1);

	if ((sstat1 & SCSIRSTI) != 0) {
		/* a device-initiated bus reset */
		aic_outb(aic, CLRSINT1, CLRSCSIRSTI);
		aic_reset(aic, /*initiate_reset*/FALSE);
		return;
	}

	if ((sstat1 & SCSIPERR) != 0) {
		aic_outb(aic, CLRSINT1, CLRSCSIPERR);
		aic_sched_msgout(aic, MSG_PARITY_ERROR);
		aic_outb(aic, DMACNTRL0, INTEN);
		return;
	}

	if (aic_inb(aic, SSTAT4)) {
		aic_outb(aic, CLRSERR, CLRSYNCERR|CLRFWERR|CLRFRERR);
		aic_reset(aic, /*initiate_reset*/TRUE);
		return;
	}

	if (aic->state != AIC_HASNEXUS) {
		if ((sstat0 & SELDI) != 0) {
			aic_reselected(aic);
			aic_outb(aic, DMACNTRL0, INTEN);
			return;
		}

		if ((sstat0 & SELDO) != 0) {
			aic_selected(aic);
			aic_outb(aic, DMACNTRL0, INTEN);
			return;
		}

		if ((sstat1 & SELTO) != 0) {
			scb = aic->nexus;
			ccb = scb->ccb;
			ccb->ccb_h.status = CAM_SEL_TIMEOUT;
			aic_done(aic, scb);
			while ((sstat1 & BUSFREE) == 0)
				sstat1 = aic_inb(aic, SSTAT1);
			aic->flags |= AIC_BUSFREE_OK;
		}
	}

	if ((sstat1 & REQINIT) != 0) {
		u_int8_t phase = aic_inb(aic, SCSISIGI) & PH_MASK;
		aic_outb(aic, SCSISIGO, phase);
		aic_outb(aic, CLRSINT1, CLRPHASECHG);

		switch (phase) {
		case PH_MSGOUT:
			aic_msgout(aic);
			break;
		case PH_MSGIN:
			aic_msgin(aic);
			break;
		case PH_STAT:
			scb = aic->nexus;
			ccb = scb->ccb;
			aic_outb(aic, DMACNTRL0, 0);
			aic_outb(aic, SXFRCTL0, CHEN|SPIOEN);
			scb->status = aic_inb(aic, SCSIDAT);
			aic_outb(aic, SXFRCTL0, CHEN);
			break;
		case PH_CMD:
			aic_cmd(aic);
			break;
		case PH_DATAIN:
			aic_datain(aic);
			break;
		case PH_DATAOUT:
			aic_dataout(aic);
			break;
		}
		aic->prev_phase = phase;
		aic_outb(aic, DMACNTRL0, INTEN);
		return;
	}

	if ((sstat1 & BUSFREE) != 0) {
		aic_outb(aic, SCSISEQ, 0);
		aic_outb(aic, CLRSINT0, sstat0);
		aic_outb(aic, CLRSINT1, sstat1);
		aic_outb(aic, SIMODE0, ENSELDI);
		aic_outb(aic, SIMODE1, ENSCSIRST);
		aic_outb(aic, SCSISEQ, ENRESELI);
		if ((scb = aic->nexus)) {
			if ((aic->flags & AIC_BUSFREE_OK) == 0) {
				ccb = scb->ccb;
				ccb->ccb_h.status = CAM_UNEXP_BUSFREE;
				aic_done(aic, scb);
			} else if (scb->flags & SCB_SENSE) {
				/* autosense request */
				aic->flags &= ~AIC_BUSFREE_OK;
				aic_select(aic);
				aic_outb(aic, DMACNTRL0, INTEN);
				return;
			}
		}
		aic->flags &= ~AIC_BUSFREE_OK;
		aic->state = AIC_IDLE;
		aic_start(aic);
		aic_outb(aic, DMACNTRL0, INTEN);
		return;
	}

	printf("aic_intr: unexpected intr sstat0 %x sstat1 %x\n",
		sstat0, sstat1);
	aic_outb(aic, DMACNTRL0, INTEN);
}

/*
 * Reset ourselves.
 */
static void
aic_chip_reset(struct aic_softc *aic)
{
	/*
	 * Doc. recommends to clear these two registers before
	 * operations commence
	 */
	aic_outb(aic, SCSITEST, 0);
	aic_outb(aic, TEST, 0);

	/* Reset SCSI-FIFO and abort any transfers */
	aic_outb(aic, SXFRCTL0, CHEN|CLRCH|CLRSTCNT);

	/* Reset HOST-FIFO */
	aic_outb(aic, DMACNTRL0, RSTFIFO);
	aic_outb(aic, DMACNTRL1, 0);

	/* Disable all selection features */
	aic_outb(aic, SCSISEQ, 0);
	aic_outb(aic, SXFRCTL1, 0);

	/* Disable interrupts */
	aic_outb(aic, SIMODE0, 0);
	aic_outb(aic, SIMODE1, 0);

	/* Clear interrupts */
	aic_outb(aic, CLRSINT0, 0x7f);
	aic_outb(aic, CLRSINT1, 0xef);

	/* Disable synchronous transfers */
	aic_outb(aic, SCSIRATE, 0);

	/* Haven't seen ant errors (yet) */
	aic_outb(aic, CLRSERR, 0x07);

	/* Set our SCSI-ID */
	aic_outb(aic, SCSIID, aic->initiator << OID_S);
	aic_outb(aic, BRSTCNTRL, EISA_BRST_TIM);
}

/*
 * Reset the SCSI bus
 */
static void
aic_scsi_reset(struct aic_softc *aic)
{
	aic_outb(aic, SCSISEQ, SCSIRSTO);
	DELAY(500);
	aic_outb(aic, SCSISEQ, 0);
	DELAY(50);
}

/*
 * Reset. Abort all pending commands.
 */
static void
aic_reset(struct aic_softc *aic, int initiate_reset)
{
	struct ccb_hdr *ccb_h;

	CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reset\n"));

	if (initiate_reset)
		aic_scsi_reset(aic);
	aic_chip_reset(aic);

	xpt_async(AC_BUS_RESET, aic->path, NULL);

	while ((ccb_h = TAILQ_FIRST(&aic->pending_ccbs)) != NULL) {
		TAILQ_REMOVE(&aic->pending_ccbs, ccb_h, sim_links.tqe);
		ccb_h->status &= ~CAM_STATUS_MASK;
		ccb_h->status |= CAM_SCSI_BUS_RESET;
		aic_done(aic, (struct aic_scb *)ccb_h->ccb_scb_ptr);
	}

	while ((ccb_h = TAILQ_FIRST(&aic->nexus_ccbs)) != NULL) {
		TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
		ccb_h->status &= ~CAM_STATUS_MASK;
		ccb_h->status |= CAM_SCSI_BUS_RESET;
		aic_done(aic, (struct aic_scb *)ccb_h->ccb_scb_ptr);
	}

	if (aic->nexus) {
		ccb_h = &aic->nexus->ccb->ccb_h;
		ccb_h->status &= ~CAM_STATUS_MASK;
{'fraction_non_alphanumeric': 0.15161386730424387, 'fraction_numerical': 0.010505080693365212, 'mean_word_length': 3.5533782404572363, 'pattern_counts': {'":': 0, '<': 75, '<?xml version=': 0, '>': 1049, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1631322', 'n_tokens_mistral': 34450, 'n_tokens_neox': 30079, 'n_words': 5490}