/*	$OpenBSD: ises.c,v 1.31 2006/06/29 21:34:51 deraadt Exp $	*/

/*
 * Copyright (c) 2000, 2001 H�kan Olsson (ho@crt.se)
 * 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 ``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 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.
 */

/*
 * PCC-ISES hardware crypto accelerator
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/timeout.h>
#include <sys/device.h>
#include <sys/queue.h>

#include <crypto/cryptodev.h>
#include <crypto/cryptosoft.h>
#include <dev/rndvar.h>
#include <crypto/md5.h>
#include <crypto/sha1.h>
#include <crypto/rmd160.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <dev/pci/isesreg.h>
#include <dev/pci/isesvar.h>
#include <dev/microcode/ises/ises_fw.h>

/*
 * Prototypes and count for the pci_device structure
 */
int	ises_match(struct device *, void *, void *);
void	ises_attach(struct device *, struct device *, void *);

void	ises_initstate(void *);
void	ises_hrng_init(struct ises_softc *);
void	ises_hrng(void *);
void	ises_process_oqueue(struct ises_softc *);
int	ises_queue_cmd(struct ises_softc *, u_int32_t, u_int32_t *, 
		       u_int32_t (*)(struct ises_softc *, struct ises_cmd *));
u_int32_t ises_get_fwversion(struct ises_softc *);
int	ises_assert_cmd_mode(struct ises_softc *);

int	ises_intr(void *);
int	ises_newsession(u_int32_t *, struct cryptoini *);
int	ises_freesession(u_int64_t);
int	ises_process(struct cryptop *);
void	ises_callback(struct ises_q *);
int	ises_feed(struct ises_softc *);
int	ises_bchu_switch_session(struct ises_softc *, 
				      struct ises_session *, int);
u_int32_t ises_bchu_switch_final(struct ises_softc *, struct ises_cmd *);

void	ises_read_dma(struct ises_softc *);

#define READ_REG(sc,r) \
    bus_space_read_4((sc)->sc_memt, (sc)->sc_memh,r)

#define WRITE_REG(sc,reg,val) \
    bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, reg, val)

/* XXX This should probably be (x) = htole32((x)) */
#define SWAP32(x) ((x) = swap32((x)))

#ifdef ISESDEBUG
#  define DPRINTF(x) printf x
#else
#  define DPRINTF(x)
#endif

#ifdef ISESDEBUG
void	ises_debug_init(struct ises_softc *);
void	ises_debug_2(void);
void	ises_debug_loop(void *);
void	ises_showreg(void);
void	ises_debug_parse_omr(struct ises_softc *);
void	ises_debug_simple_cmd(struct ises_softc *, u_int32_t, u_int32_t);
struct ises_softc *ises_sc;
struct timeout ises_db_timeout;
int ises_db;
#endif

/* For HRNG entropy collection, these values gather 1600 bytes/s */
#ifndef ISESRNGBITS
#define ISESRNGBITS	128		/* Bits per iteration (mult. of 32) */
#define ISESRNGIPS	100		/* Iterations per second */
#endif

/* XXX Disable HRNG while debugging. */
#define ISES_HRNG_DISABLED

/* Maximum number of times we try to download the firmware. */
#define ISES_MAX_DOWNLOAD_RETRIES	3

struct cfattach ises_ca = {
	sizeof(struct ises_softc), ises_match, ises_attach,
};

struct cfdriver ises_cd = {
	0, "ises", DV_DULL
};

struct ises_stats {
	u_int64_t	ibytes;
	u_int64_t	obytes;
	u_int32_t	ipkts;
	u_int32_t	opkts;
	u_int32_t	invalid;
	u_int32_t	nomem;
} isesstats;

int
ises_match(struct device *parent, void *match, void *aux)
{
	struct pci_attach_args *pa = (struct pci_attach_args *)aux;

	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_PIJNENBURG &&
	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_PIJNENBURG_PCC_ISES)
		return (1);

	return (0);
}

void
ises_attach(struct device *parent, struct device *self, void *aux)
{
	struct ises_softc *sc = (struct ises_softc *)self;
	struct pci_attach_args *pa = aux;
	pci_chipset_tag_t pc = pa->pa_pc;
	pci_intr_handle_t ih;
	const char *intrstr = NULL;
	bus_size_t memsize;

	bus_dma_segment_t seg;
	int nsegs, error, state;

	SIMPLEQ_INIT(&sc->sc_queue);
	SIMPLEQ_INIT(&sc->sc_qchip);
	SIMPLEQ_INIT(&sc->sc_cmdq);
	state = 0;

	/* Map control/status registers. */
	if (pci_mapreg_map(pa, PCI_MAPREG_START,
	    PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->sc_memt,
	    &sc->sc_memh, NULL, &memsize, 0)) {
		printf(": can't find mem space\n");
		return;
	}
	state++;

	/* Map interrupt. */
	if (pci_intr_map(pa, &ih)) {
		printf(": couldn't map interrupt\n");
		goto fail;
	}
	state++;

	intrstr = pci_intr_string(pc, ih);
	sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, ises_intr, sc,
	    self->dv_xname);
	if (sc->sc_ih == NULL) {
		printf(": couldn't establish interrupt\n");
		if (intrstr != NULL)
			printf(" at %s", intrstr);
		printf("\n");
		goto fail;
	}

	/* Initialize DMA map */
	sc->sc_dmat = pa->pa_dmat;
	error = bus_dmamap_create(sc->sc_dmat, 1 << PGSHIFT, 1, 1 << PGSHIFT,
	    0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_dmamap);
	if (error) {
		printf(": cannot create dma map (%d)\n", error);
		goto fail;
	}
	state++;

	/* Allocate in DMAable memory. */
	if (bus_dmamem_alloc(sc->sc_dmat, ISES_B_DATASIZE, 1, 0, &seg, 1,
	    &nsegs, BUS_DMA_NOWAIT)) {
		printf(": can't alloc dma buffer space\n");
		goto fail;
	}
	state++;

	if (bus_dmamem_map(sc->sc_dmat, &seg, nsegs, ISES_B_DATASIZE,
	    &sc->sc_dma_data, 0)) {
		printf(": can't map dma buffer space\n");
		goto fail;
	}
	state++;

	printf(": %s\n", intrstr);

	bzero(&isesstats, sizeof(isesstats));

	sc->sc_cid = crypto_get_driverid(0);

	if (sc->sc_cid < 0)
		goto fail;

	/*
	 * Since none of the initialization steps generate interrupts
	 * for example, the hardware reset, we use a number of timeouts
	 * (or init states) to do the rest of the chip initialization.
	 */

	sc->sc_initstate = 0;
	startuphook_establish(ises_initstate, sc);

#ifdef ISESDEBUG
	ises_debug_init(sc);
#endif
	return;

 fail:
	switch (state) { /* Always fallthrough here. */
	case 5:
		bus_dmamem_unmap(sc->sc_dmat, (caddr_t)&sc->sc_dma_data,
		    sizeof sc->sc_dma_data);
		/* FALLTHROUGH */
	case 4:
		bus_dmamem_free(sc->sc_dmat, &seg, nsegs);
		/* FALLTHROUGH */
	case 3:
		bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
		/* FALLTHROUGH */
	case 2:
		pci_intr_disestablish(pc, sc->sc_ih);
		/* FALLTHROUGH */
	case 1:
		bus_space_unmap(sc->sc_memt, sc->sc_memh, memsize);
		/* FALLTHROUGH */
	default: /* 0 */
		break;
	}
	return;
}

void
ises_initstate(void *v)
{
	/*
	 * Step through chip initialization.
	 * sc->sc_initstate tells us what to do.
	 */
	extern int hz;
	struct ises_softc *sc = v;
	char *dv = sc->sc_dv.dv_xname;
	u_int32_t stat;
	int p, ticks, algs[CRYPTO_ALGORITHM_MAX + 1];
	static int retry_count = 0; /* XXX Should be in softc */

	ticks = hz * 3 / 2; /* 1.5s */ 

	p = ISES_STAT_IDP_STATE(READ_REG(sc, ISES_A_STAT));
	DPRINTF(("%s: initstate %d, IDP state is %d \"%s\"\n", dv, 
		  sc->sc_initstate, p, ises_idp_state[p]));

	switch (sc->sc_initstate) {
	case 0:
		/* Called by dostartuphooks(9). */
		timeout_set(&sc->sc_timeout, ises_initstate, sc);
		sc->sc_initstate++;

		/* FALLTHROUGH */
	case 1:
		/* Power up the chip (clear powerdown bit) */
		stat = READ_REG(sc, ISES_BO_STAT);
		if (stat & ISES_BO_STAT_POWERDOWN) {
			stat &= ~ISES_BO_STAT_POWERDOWN;
			WRITE_REG(sc, ISES_BO_STAT, stat);
			/* Selftests will take 1 second. */
			break;
		}
#if 1
		else {
			/* Power down the chip for sane init, then rerun. */
			stat |= ISES_BO_STAT_POWERDOWN;
			WRITE_REG(sc, ISES_BO_STAT, stat);
			sc->sc_initstate--; /* Rerun state 1. */
			break;
		}
#else
		/* FALLTHROUGH (chip is already powered up) */
		sc->sc_initstate++;
#endif

	case 2:
		/* Perform a hardware reset */
		stat = 0;

		printf ("%s: initializing...\n", dv);

		/* Clear all possible bypass bits. */
		for (p = 0; p < 128; p++)
			WRITE_REG(sc, ISES_B_BDATAOUT, 0L);

		stat |= ISES_BO_STAT_HWRESET;
		WRITE_REG(sc, ISES_BO_STAT, stat);
		stat &= ~ISES_BO_STAT_HWRESET;
		WRITE_REG(sc, ISES_BO_STAT, stat);
		/* Again, selftests will take 1 second. */
		break;

	case 3:
		/* Set AConf to zero, i.e 32-bits access to A-int. */
		stat = READ_REG(sc, ISES_BO_STAT);
		stat &= ~ISES_BO_STAT_ACONF;
		WRITE_REG(sc, ISES_BO_STAT, stat);

		/* Is the firmware already loaded? */
		if (READ_REG(sc, ISES_A_STAT) & ISES_STAT_HW_DA) {
			/* Yes it is, jump ahead a bit */
			ticks = 1;
			sc->sc_initstate += 3; /* Next step --> 7 */
			break;
		}

		/*
		 * Download the Basic Functionality firmware.
		 */

		p = ISES_STAT_IDP_STATE(READ_REG(sc, ISES_A_STAT));
		if (p == ISES_IDP_WFPL) {
			/* We're ready to download. */
			ticks = 1;
			sc->sc_initstate += 2; /* Next step --> 6 */
			break;
		}

		/*
		 * Prior to downloading we need to reset the NSRAM.
		 * Setting the tamper bit will erase the contents
		 * in 1 microsecond.
		 */
		stat = READ_REG(sc, ISES_BO_STAT);
		stat |= ISES_BO_STAT_TAMPER;
		WRITE_REG(sc, ISES_BO_STAT, stat);
		ticks = 1;
		break;

	case 4:
		/* After tamper bit has been set, powerdown chip. */
		stat = READ_REG(sc, ISES_BO_STAT);
		stat |= ISES_BO_STAT_POWERDOWN;
		WRITE_REG(sc, ISES_BO_STAT, stat);
		/* Wait one second for power to dissipate. */
		break;

	case 5:
		/* Clear tamper and powerdown bits. */
		stat = READ_REG(sc, ISES_BO_STAT);
		stat &= ~(ISES_BO_STAT_TAMPER | ISES_BO_STAT_POWERDOWN);
		WRITE_REG(sc, ISES_BO_STAT, stat);
		/* Again we need to wait a second for selftests. */
		break;

	case 6:
		/*
		 * We'll need some space in the input queue (IQF)
		 * and we need to be in the 'waiting for program
		 * length' IDP state (0x4).
		 */
		p = ISES_STAT_IDP_STATE(READ_REG(sc, ISES_A_STAT));
		if (READ_REG(sc, ISES_A_IQF) < 4 || p != ISES_IDP_WFPL) {
			if (retry_count++ < ISES_MAX_DOWNLOAD_RETRIES) {
				/* Retry download. */
				sc->sc_initstate -= 5; /* Next step --> 2 */
				ticks = 1;
				break;
			}
			retry_count = 0;
			printf("%s: cannot download firmware, "
			    "IDP state is \"%s\"\n", dv, ises_idp_state[p]);
			return;
		}

		/* Write firmware length */
		WRITE_REG(sc, ISES_A_IQD, ISES_BF_IDPLEN);

		/* Write firmware code */
		for (p = 0; p < sizeof(ises_bf_fw)/sizeof(u_int32_t); p++) {
			WRITE_REG(sc, ISES_A_IQD, ises_bf_fw[p]);
			if (READ_REG(sc, ISES_A_IQF) < 4)
				DELAY(10);
		}

		/* Write firmware CRC */
		WRITE_REG(sc, ISES_A_IQD, ISES_BF_IDPCRC);

		/* Wait 1s while chip resets and runs selftests */
		break;

	case 7:
		/* Did the download succed? */
		if (READ_REG(sc, ISES_A_STAT) & ISES_STAT_HW_DA) {
			ticks = 1;
			break;
		}

		/* We failed. */
		goto fail;

	case 8:
		if (ises_assert_cmd_mode(sc) < 0)
			goto fail;

		/*
		 * Now that the basic functionality firmware should be
		 * up and running, try to get the firmware version.
		 */

		stat = ises_get_fwversion(sc);
		if (stat == 0)
			goto fail;

		printf("%s: firmware v%d.%d loaded (%d bytes)", dv,
		    stat & 0xffff, (stat >> 16) & 0xffff, ISES_BF_IDPLEN << 2);

		/* We can use firmware versions 1.x & 2.x */
		switch (stat & 0xffff) {
		case 0:
			printf(" diagnostic, %s disabled\n", dv);
			goto fail;
		case 1: /* Basic Func "base" firmware */
		case 2: /* Basic Func "ipsec" firmware, no ADP code */
			break;
		default:
			printf(" unknown, %s disabled\n", dv);
			goto fail;
		}

		stat = READ_REG(sc, ISES_A_STAT);
		DPRINTF((", mode %s",
		    ises_sw_mode[ISES_STAT_SW_MODE(stat)]));

		/* Reuse the timeout for HRNG entropy collection. */
		timeout_del(&sc->sc_timeout);
		ises_hrng_init(sc);

		/* Set the interrupt mask */
		sc->sc_intrmask = ISES_STAT_BCHU_OAF | ISES_STAT_BCHU_ERR |
		    ISES_STAT_BCHU_OFHF | ISES_STAT_SW_OQSINC |
		    ISES_STAT_LNAU_BUSY_1 | ISES_STAT_LNAU_ERR_1 |
		    ISES_STAT_LNAU_BUSY_2 | ISES_STAT_LNAU_ERR_2;
#if 0
		    ISES_STAT_BCHU_ERR | ISES_STAT_BCHU_OAF |
		    ISES_STAT_BCHU_IFE | ISES_STAT_BCHU_IFHE |
		    ISES_STAT_BCHU_OFHF | ISES_STAT_BCHU_OFF;
#endif

		WRITE_REG(sc, ISES_A_INTE, sc->sc_intrmask);

		/* We're done. */
		printf("\n");

		/* Register ourselves with crypto framework. */
		bzero(algs, sizeof(algs));

		algs[CRYPTO_3DES_CBC] = CRYPTO_ALG_FLAG_SUPPORTED;
		algs[CRYPTO_DES_CBC] = CRYPTO_ALG_FLAG_SUPPORTED;
		algs[CRYPTO_MD5_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
		algs[CRYPTO_SHA1_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
		algs[CRYPTO_RIPEMD160_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;

		crypto_register(sc->sc_cid, algs,
		    ises_newsession, ises_freesession, ises_process);
		return;

	default:
		printf("%s: entered unknown initstate %d\n", dv,
		    sc->sc_initstate);
		goto fail;
	}

	/* Increment state counter and schedule next step in 'ticks' ticks. */
	sc->sc_initstate++;
	timeout_add(&sc->sc_timeout, ticks);
	return;

 fail:
	printf("%s: firmware failure\n", dv);
	timeout_del(&sc->sc_timeout);
	return;
}

/* Put a command on the A-interface queue. */
int
ises_queue_cmd(struct ises_softc *sc, u_int32_t cmd, u_int32_t *data, 
	       u_int32_t (*callback)(struct ises_softc *, struct ises_cmd *))
{
	struct ises_cmd *cq;
	int p, len, s, code;

	len = cmd >> 24;
	code = (cmd >> 16) & 0xFF;

#ifdef ISESDEBUG
	if (code != ISES_CMD_HBITS) /* ... since this happens 100 times/s */
		DPRINTF(("%s: queueing cmd 0x%x len %d\n", sc->sc_dv.dv_xname,
		    code, len));
#endif

	s = splnet();

	if (len > READ_REG(sc, ISES_A_IQF)) {
		splx(s);
		return (EAGAIN); /* XXX ENOMEM ? */
	}

	cq = (struct ises_cmd *) 
	    malloc(sizeof (struct ises_cmd), M_DEVBUF, M_NOWAIT);
	if (cq == NULL) {
		splx(s);
		isesstats.nomem++;
		return (ENOMEM);
	}
	bzero(cq, sizeof (struct ises_cmd));
	cq->cmd_code = code;
	cq->cmd_cb = callback;
	cq->cmd_session = sc->sc_cursession;
	SIMPLEQ_INSERT_TAIL(&sc->sc_cmdq, cq, cmd_next);

	WRITE_REG(sc, ISES_A_IQD, cmd);

	/* LNAU register data should be written in reverse order */
	if ((code >= ISES_CMD_LW_A_1 && code <= ISES_CMD_LW_U_1) || /* LNAU1 */
	    (code >= ISES_CMD_LW_A_2 && code <= ISES_CMD_LW_U_2))   /* LNAU2 */
		for (p = len - 1; p >= 0; p--)
			WRITE_REG(sc, ISES_A_IQD, *(data + p));
	else
		for (p = 0; p < len; p++)
			WRITE_REG(sc, ISES_A_IQD, *(data + p));

	/* Signal 'command ready'. */
	WRITE_REG(sc, ISES_A_IQS, 0);

	splx(s);
	return (0);
}

/* Process all completed responses in the output queue. */
void
ises_process_oqueue(struct ises_softc *sc)
{
#ifdef ISESDEBUG
	char *dv = sc->sc_dv.dv_xname;
#endif
	struct ises_cmd *cq;
	struct ises_session *ses;
	u_int32_t oqs, r, d;
	int cmd, len, c, s;

	r = READ_REG(sc, ISES_A_OQS);
	if (r > 1)
		DPRINTF(("%s:process_oqueue: OQS=%d\n", dv, r));

	/* OQS gives us the number of responses we have to process. */
	while ((oqs = READ_REG(sc, ISES_A_OQS)) > 0) {
		/* Read command response. [ len(8) | cmd(8) | rc(16) ] */
		r = READ_REG(sc, ISES_A_OQD);
		len = (r >> 24);
		cmd = (r >> 16) & 0xff;
		r   = r & 0xffff;

		s = splnet();
		if (!SIMPLEQ_EMPTY(&sc->sc_cmdq)) {
			cq = SIMPLEQ_FIRST(&sc->sc_cmdq);
			SIMPLEQ_REMOVE_HEAD(&sc->sc_cmdq, cmd_next);
			cq->cmd_rlen = len;
		} else {
			cq = NULL;
			DPRINTF(("%s:process_oqueue: cmd queue empty!\n", dv));
		}
		splx(s);

		if (r) {
			/* Ouch. This command generated an error */
			DPRINTF(("%s:process_oqueue: cmd 0x%x err %d\n", dv, 
			    cmd, (r & ISES_RC_MASK)));
			/* Abort any running session switch to force a retry.*/
			sc->sc_switching = 0;
			/* Return to CMD mode. This will reset all queues. */
			(void)ises_assert_cmd_mode(sc);
		} else {
			/* Use specified callback, if any */
			if (cq && cq->cmd_cb) {
				if (cmd == cq->cmd_code) {
					cq->cmd_cb(sc, cq);
					cmd = ISES_CMD_NONE;
				} else {
					DPRINTF(("%s:process_oqueue: expected"
					    " cmd 0x%x, got 0x%x\n", dv, 
					    cq->cmd_code, cmd));
					/* XXX Some error handling here? */
				}
			}

			switch (cmd) {
			case ISES_CMD_NONE:
				break;

			case ISES_CMD_HBITS:
				/* XXX How about increasing the pool size? */
				/* XXX Use add_entropy_words instead? */
				/* XXX ... at proper spl */
				/* Cmd generated by ises_rng() via timeouts */
				while (len--) {
					d = READ_REG(sc, ISES_A_OQD);
					add_true_randomness(d);
				}
				break;

			case ISES_CMD_LUPLOAD_1:
				/* Get result of LNAU 1 operation. */
				DPRINTF(("%s:process_oqueue: LNAU 1 result "
				     "upload (len=%d)\n", dv, len));
				sc->sc_lnau1_rlen = len;
				bzero(sc->sc_lnau1_r, 2048 / 8);
				while (len--) {
					/* first word is LSW */
					sc->sc_lnau1_r[len] = 
					    READ_REG(sc, ISES_A_OQD);
				}
				break;

			case ISES_CMD_LUPLOAD_2:
				/* Get result of LNAU 1 operation. */
				DPRINTF(("%s:process_oqueue: LNAU 2 result "
				     "upload (len=%d)\n", dv, len));
				sc->sc_lnau2_rlen = len;
				bzero(sc->sc_lnau1_r, 2048 / 8);
				while (len--) {
					/* first word is LSW */
					sc->sc_lnau2_r[len] = 
					    READ_REG(sc, ISES_A_OQD);
				}
				break;

			case ISES_CMD_BR_OMR:
				ses = &sc->sc_sessions[cq->cmd_session];
				ses->omr = READ_REG(sc, ISES_A_OQD);
				DPRINTF(("%s:process_oqueue: read OMR[%08x]\n",
				    dv, ses->omr));
#ifdef ISESDEBUG
				ises_debug_parse_omr(sc);
#endif
				break;

			case ISES_CMD_BSWITCH:
				/* XXX Currently BSWITCH does not work. */
				DPRINTF(("%s:process_oqueue: BCHU_SWITCH\n"));
				/* Put switched BCHU session in cur session. */
				ses = &sc->sc_sessions[cq->cmd_session];
				for(c = 0; len > 0; len--, c++)
#if 0 /* Don't store the key, just drain the data */
					*((u_int32_t *)&ses + c) =
#endif
					    READ_REG(sc, ISES_A_OQD);

				sc->sc_switching = 0;
				ises_feed (sc);
				break;

			case ISES_CMD_BW_HMLR:
				/* XXX Obsoleted by ises_bchu_switch_final */
				DPRINTF(("%s:process_oqueue: CMD_BW_HMLR !?\n",
				    dv));
				break;

			default:
				/* All other are ok (no response data) */
				DPRINTF(("%s:process_oqueue cmd 0x%x len %d\n",
				    dv, cmd, len));
				if (cq && cq->cmd_cb) 
					len -= cq->cmd_cb(sc, cq);
			}
		}

		if (cq)
			free(cq, M_DEVBUF);
		
		/* This will drain any remaining data and ACK this reponse. */
		while (len-- > 0)
			d = READ_REG(sc, ISES_A_OQD);
		WRITE_REG(sc, ISES_A_OQS, 0);
		if (oqs > 1)
			DELAY(1); /* Wait for fw to decrement OQS (8 clocks) */
	}
}

int
ises_intr(void *arg)
{
	struct ises_softc *sc = arg;
	u_int32_t ints, dma_status, cmd; 
	char *dv = sc->sc_dv.dv_xname;

	dma_status = READ_REG(sc, ISES_DMA_STATUS);

	if (!(dma_status & (ISES_DMA_STATUS_R_ERR | ISES_DMA_STATUS_W_ERR))) {
		if ((sc->sc_dma_mask & ISES_DMA_STATUS_R_RUN) != 0 &&
		    (dma_status & ISES_DMA_STATUS_R_RUN) == 0) {
			DPRINTF(("%s: DMA read complete\n", dv));

			bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0,
			    sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD);

			/* XXX Pick up and return the data.*/

			WRITE_REG(sc, ISES_DMA_RESET, 0);
		}
		if ((sc->sc_dma_mask & ISES_DMA_STATUS_W_RUN) != 0 &&
		    (dma_status & ISES_DMA_STATUS_W_RUN) == 0) {
			DPRINTF(("%s: DMA write complete\n", dv));

			bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0,
			    sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE);

			WRITE_REG(sc, ISES_DMA_RESET, 0);
			ises_feed(sc);
		}
	} else {
		printf ("%s: DMA error\n", dv);
		WRITE_REG(sc, ISES_DMA_RESET, 0);
	}

	ints = READ_REG(sc, ISES_A_INTS);
	if (!(ints & sc->sc_intrmask)) {
		DPRINTF (("%s: other intr mask [%08x]\n", ints));
		return (0); /* Not our interrupt. */
	}

	/* Clear all set intr bits. */
	WRITE_REG(sc, ISES_A_INTS, ints);

#if 0
	/* Check it we've got room for more data. */
	if (READ_REG(sc, ISES_A_STAT) &
	    (ISES_STAT_BCHU_IFE | ISES_STAT_BCHU_IFHE))
		ises_feed(sc);
#endif

	/* Does the A-intf output queue have data we need to process? */
	if (ints & ISES_STAT_SW_OQSINC)
		ises_process_oqueue(sc);

	if (ints & ISES_STAT_LNAU_BUSY_1) {
		DPRINTF(("%s:ises_intr: LNAU 1 job complete\n", dv));
		/* upload LNAU 1 result (into sc->sc_lnau1_r) */
		cmd = ISES_MKCMD(ISES_CMD_LUPLOAD_1, 0);
		ises_queue_cmd(sc, cmd, NULL, NULL);
	}

	if (ints & ISES_STAT_LNAU_BUSY_2) {
		DPRINTF(("%s:ises_intr: LNAU 2 job complete\n", dv));
		/* upload LNAU 2 result (into sc->sc_lnau2_r) */
		cmd = ISES_MKCMD(ISES_CMD_LUPLOAD_2, 0);
		ises_queue_cmd(sc, cmd, NULL, NULL);
	}

	if (ints & ISES_STAT_LNAU_ERR_1) {
		DPRINTF(("%s:ises_intr: LNAU 1 error\n", dv));
		sc->sc_lnau1_rlen = -1;
	}

	if (ints & ISES_STAT_LNAU_ERR_2) {
		DPRINTF(("%s:ises_intr: LNAU 2 error\n", dv));
		sc->sc_lnau2_rlen = -1;
	}

	if (ints & ISES_STAT_BCHU_OAF) {	/* output data available */
		DPRINTF(("%s:ises_intr: BCHU_OAF bit set\n", dv));
		/* Read DMA data from B-interface. */
		ises_read_dma (sc);
	}

	if (ints & ISES_STAT_BCHU_ERR) {	/* We got a BCHU error */
		DPRINTF(("%s:ises_intr: BCHU error\n", dv));
		/* XXX Error handling */
	}

	if (ints & ISES_STAT_BCHU_OFHF) {	/* Output is half full */
		DPRINTF(("%s:ises_intr: BCHU output FIFO half full\n", dv));
		/* XXX drain data? */
	}

#if 0 /* XXX Useful? */
	if (ints & ISES_STAT_BCHU_OFF) {	/* Output is full */
		/* XXX drain data / error handling? */
	}
#endif
	return (1);
}

int
ises_feed(struct ises_softc *sc)
{
	struct ises_q *q;
	bus_dma_segment_t *ds = &sc->sc_dmamap->dm_segs[0];
	u_int32_t dma_status;
	int s;
#ifdef ISESDEBUG
	char *dv = sc->sc_dv.dv_xname;
#endif

	DPRINTF(("%s:ises_feed: called (sc = %p)\n", dv, sc));
	DELAY(1000000);

	s = splnet();
	/* Anything to do? */
	if (SIMPLEQ_EMPTY(&sc->sc_queue) ||
	    (READ_REG(sc, ISES_A_STAT) & ISES_STAT_BCHU_IFF)) {
		splx(s);
		return (0);
	}

	/* Pick the first */
	q = SIMPLEQ_FIRST(&sc->sc_queue);
	splx(s);

	/* If we're currently switching sessions, we'll have to wait. */
	if (sc->sc_switching != 0) {
		DPRINTF(("%s:ises_feed: waiting for session switch\n", dv));
		return (0);
	}

	/* If on-chip data is not correct for this data, switch session. */
	if (sc->sc_cursession != q->q_sesn) {
		/* Session switch required */
		DPRINTF(("%s:ises_feed: initiating session switch\n", dv));
		if (ises_bchu_switch_session (sc, &q->q_session, q->q_sesn))
			sc->sc_cursession = q->q_sesn;
		else
			DPRINTF(("%s:ises_feed: session switch failed\n", dv));
		return (0);
	}

	DPRINTF(("%s:ises_feed: feed to chip (q = %p)\n", dv, q));
	DELAY(2000000);

	s = splnet();
	SIMPLEQ_REMOVE_HEAD(&sc->sc_queue, q_next);
	SIMPLEQ_INSERT_TAIL(&sc->sc_qchip, q, q_next);
	--sc->sc_nqueue;
	splx(s);

	if (q->q_crp->crp_flags & CRYPTO_F_IMBUF)
		bus_dmamap_load_mbuf(sc->sc_dmat, sc->sc_dmamap, 
		    q->q_src.mbuf, BUS_DMA_NOWAIT);
	else if (q->q_crp->crp_flags & CRYPTO_F_IOV)
		bus_dmamap_load_uio(sc->sc_dmat, sc->sc_dmamap, q->q_src.uio,
		    BUS_DMA_NOWAIT);
	/* ... else */	

	/* Start writing data to the ises. */
	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0,
	    sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
	
	DPRINTF(("%s:ises_feed: writing DMA\n", dv));
	DELAY(1000000);

	sc->sc_dma_mask |= ISES_DMA_STATUS_W_RUN;

	WRITE_REG(sc, ISES_DMA_WRITE_START, ds->ds_addr);
	WRITE_REG(sc, ISES_DMA_WRITE_COUNT, ISES_DMA_WCOUNT(ds->ds_len));

	dma_status = READ_REG(sc, ISES_DMA_STATUS);
	dma_status |= ISES_DMA_CTRL_ILT | ISES_DMA_CTRL_RLINE;
	WRITE_REG(sc, ISES_DMA_CTRL, dma_status);

	DPRINTF(("%s:ises_feed: done\n", dv));
	return (0);
}

/*
 * Allocate a new 'session' and return an encoded session id.  'sidp'
 * contains our registration id, and should contain an encoded session
 * id on successful allocation.
 */
int
ises_newsession(u_int32_t *sidp, struct cryptoini *cri)
{
	struct cryptoini *c, *mac = NULL, *enc = NULL;
	struct ises_softc *sc = NULL;
	struct ises_session *ses;
	MD5_CTX	   md5ctx;
	SHA1_CTX   sha1ctx;
	RMD160_CTX rmd160ctx;
	int i, sesn;
#ifdef ISESDEBUG
	char *dv;
#endif

	if (sidp == NULL || cri == NULL)
		return (EINVAL);

	for (i = 0; i < ises_cd.cd_ndevs; i++) {
		sc = ises_cd.cd_devs[i];
		if (sc == NULL || sc->sc_cid == (*sidp))
			break;
	}
	if (sc == NULL)
		return (EINVAL);
#ifdef ISESDEBUG
	dv = sc->sc_dv.dv_xname;
#endif

	DPRINTF(("%s:ises_newsession: start\n", dv));

	for (c = cri; c != NULL; c = c->cri_next) {
		if (c->cri_alg == CRYPTO_MD5_HMAC ||
		    c->cri_alg == CRYPTO_SHA1_HMAC ||
		    c->cri_alg == CRYPTO_RIPEMD160_HMAC) {
			if (mac)
				return (EINVAL);
			mac = c;
		} else if (c->cri_alg == CRYPTO_DES_CBC ||
		    c->cri_alg == CRYPTO_3DES_CBC) {
			if (enc)
				return (EINVAL);
			enc = c;
		} else
			return (EINVAL);
	}
	if (mac == 0 && enc == 0)
		return (EINVAL);

#ifdef ISESDEBUG
	printf ("%s:ises_newsession: mac=%p(%d) enc=%p(%d)\n",
	   dv, mac, (mac ? mac->cri_alg : -1), enc, (enc ? enc->cri_alg : -1));
#endif

	/* Allocate a new session */
	if (sc->sc_sessions == NULL) {
		ses = sc->sc_sessions = (struct ises_session *)
		    malloc(sizeof(struct ises_session), M_DEVBUF, M_NOWAIT);
		if (ses == NULL) {
			isesstats.nomem++;
			return (ENOMEM);
		}
		sc->sc_cursession = -1;
		sesn = 0;
		sc->sc_nsessions = 1;
	} else {
		ses = NULL;
		for (sesn = 0; sesn < sc->sc_nsessions; sesn++)
			if (sc->sc_sessions[sesn].omr == 0) {
				ses = &sc->sc_sessions[sesn];
				sc->sc_cursession = sesn;
				break;
			}

		if (ses == NULL) {
			i = sc->sc_nsessions * sizeof(struct ises_session);
			ses = (struct ises_session *)
			    malloc(i + sizeof(struct ises_session), M_DEVBUF,
			    M_NOWAIT);
			if (ses == NULL) {
				isesstats.nomem++;
				return (ENOMEM);
			}

			bcopy(sc->sc_sessions, ses, i);
			bzero(sc->sc_sessions, i);
			free(sc->sc_sessions, M_DEVBUF);
			sc->sc_sessions = ses;
			ses = &sc->sc_sessions[sc->sc_nsessions];
			sc->sc_cursession = sc->sc_nsessions;
			sc->sc_nsessions++;
		}
	}

	DPRINTF(("%s:ises_newsession: nsessions=%d cursession=%d\n", dv,
	    sc->sc_nsessions, sc->sc_cursession));

	bzero(ses, sizeof(struct ises_session));

	/* Select data path through B-interface. */
	ses->omr |= ISES_SELR_BCHU_DIS;

	if (enc) {
		/* get an IV, network byte order */
		/* XXX switch to using builtin HRNG ! */
		get_random_bytes(ses->sccr, sizeof(ses->sccr));

		/* crypto key */
		if (enc->cri_alg == CRYPTO_DES_CBC) {
			bcopy(enc->cri_key, &ses->kr[0], 8);
			bcopy(enc->cri_key, &ses->kr[2], 8);
			bcopy(enc->cri_key, &ses->kr[4], 8);
		} else
			bcopy(enc->cri_key, &ses->kr[0], 24);

		SWAP32(ses->kr[0]);
		SWAP32(ses->kr[1]);
		SWAP32(ses->kr[2]);
		SWAP32(ses->kr[3]);
		SWAP32(ses->kr[4]);
		SWAP32(ses->kr[5]);
	}

	if (mac) {
		for (i = 0; i < mac->cri_klen / 8; i++)
			mac->cri_key[i] ^= HMAC_IPAD_VAL;

		switch (mac->cri_alg) {
		case CRYPTO_MD5_HMAC:
			MD5Init(&md5ctx);
			MD5Update(&md5ctx, mac->cri_key, mac->cri_klen / 8);
			MD5Update(&md5ctx, hmac_ipad_buffer, HMAC_BLOCK_LEN -
			    (mac->cri_klen / 8));
			MD5Final((u_int8_t *)&ses->cvr, &md5ctx);
			break;
		case CRYPTO_SHA1_HMAC:
			SHA1Init(&sha1ctx);
			SHA1Update(&sha1ctx, mac->cri_key, mac->cri_klen / 8);
			SHA1Update(&sha1ctx, hmac_ipad_buffer, HMAC_BLOCK_LEN -
			    (mac->cri_klen / 8));
			SHA1Final((u_int8_t *)ses->cvr, &sha1ctx);
			break;
		case CRYPTO_RIPEMD160_HMAC:
		default:
			RMD160Init(&rmd160ctx);
			RMD160Update(&rmd160ctx, mac->cri_key,
			    mac->cri_klen / 8);
			RMD160Update(&rmd160ctx, hmac_ipad_buffer,
			    HMAC_BLOCK_LEN - (mac->cri_klen / 8));
			RMD160Final((u_int8_t *)ses->cvr, &rmd160ctx);
			break;
		}

		for (i = 0; i < mac->cri_klen / 8; i++)
			mac->cri_key[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);

		switch (mac->cri_alg) {
		case CRYPTO_MD5_HMAC:
			MD5Init(&md5ctx);
			MD5Update(&md5ctx, mac->cri_key, mac->cri_klen / 8);
			MD5Update(&md5ctx, hmac_opad_buffer, HMAC_BLOCK_LEN -
			    (mac->cri_klen / 8));
			MD5Update(&md5ctx, (u_int8_t *)ses->cvr,
			    sizeof(md5ctx.state));
			MD5Final((u_int8_t *)ses->cvr, &md5ctx);
			break;
		case CRYPTO_SHA1_HMAC:
			SHA1Init(&sha1ctx);
			SHA1Update(&sha1ctx, mac->cri_key, mac->cri_klen / 8);
			SHA1Update(&sha1ctx, hmac_opad_buffer, HMAC_BLOCK_LEN -
			    (mac->cri_klen / 8));
			SHA1Update(&sha1ctx, (u_int8_t *)ses->cvr,
			    sizeof(sha1ctx.state));
			SHA1Final((u_int8_t *)ses->cvr, &sha1ctx);
			break;
		case CRYPTO_RIPEMD160_HMAC:
		default:
			RMD160Init(&rmd160ctx);
			RMD160Update(&rmd160ctx, mac->cri_key,
			    mac->cri_klen / 8);
			RMD160Update(&rmd160ctx, hmac_opad_buffer,
			    HMAC_BLOCK_LEN - (mac->cri_klen / 8));
			RMD160Update(&rmd160ctx, (u_int8_t *)ses->cvr, 
			    sizeof(rmd160ctx.state));
			RMD160Final((u_int8_t *)ses->cvr, &rmd160ctx);
			break;
		}

		for (i = 0; i < mac->cri_klen / 8; i++)
			mac->cri_key[i] ^= HMAC_OPAD_VAL;
	}

	DPRINTF(("%s:ises_newsession: done\n", dv));
	*sidp = ISES_SID(sc->sc_dv.dv_unit, sesn);
	return (0);
}

/* Deallocate a session. */
int
ises_freesession(u_int64_t tsid)
{
	struct ises_softc *sc;
	int card, sesn;
	u_int32_t sid = ((u_int32_t)tsid) & 0xffffffff;

	card = ISES_CARD(sid);
	if (card >= ises_cd.cd_ndevs || ises_cd.cd_devs[card] == NULL)
		return (EINVAL);

	sc = ises_cd.cd_devs[card];
	sesn = ISES_SESSION(sid);

	DPRINTF(("%s:ises_freesession: freeing session %d\n",
	    sc->sc_dv.dv_xname, sesn));

	if (sc->sc_cursession == sesn)
		sc->sc_cursession = -1;

	bzero(&sc->sc_sessions[sesn], sizeof(sc->sc_sessions[sesn]));

	return (0);
}

/* Called by the crypto framework, crypto(9). */
int
ises_process(struct cryptop *crp)
{
	struct ises_softc *sc;
	struct ises_q *q;
	struct cryptodesc *maccrd, *enccrd, *crd;
	struct ises_session *ses;
	int card, s, err = EINVAL;
	int encoffset, macoffset, cpskip, sskip, dskip, stheend, dtheend;
	int cpoffset, coffset;
#if 0
	int nicealign;
#endif
#ifdef ISESDEBUG
	char *dv;
#endif

	if (crp == NULL || crp->crp_callback == NULL)
		return (EINVAL);

	card = ISES_CARD(crp->crp_sid);
	if (card >= ises_cd.cd_ndevs || ises_cd.cd_devs[card] == NULL)
		goto errout;

	sc = ises_cd.cd_devs[card];
#ifdef ISESDEBUG
	dv = sc->sc_dv.dv_xname;
#endif

	DPRINTF(("%s:ises_process: start (crp = %p)\n", dv, crp));

	s = splnet();
	if (sc->sc_nqueue == ISES_MAX_NQUEUE) {
		splx(s);
		goto memerr;
	}
	splx(s);

	q = (struct ises_q *)malloc(sizeof(struct ises_q), M_DEVBUF, M_NOWAIT);
	if (q == NULL)
		goto memerr;
	bzero(q, sizeof(struct ises_q));

	q->q_sesn = ISES_SESSION(crp->crp_sid);
	ses = &sc->sc_sessions[q->q_sesn];

	DPRINTF(("%s:ises_process: session %d selected\n", dv, q->q_sesn));

	q->q_sc = sc;
	q->q_crp = crp;

	if (crp->crp_flags & CRYPTO_F_IMBUF) {
		q->q_src.mbuf = (struct mbuf *)crp->crp_buf;
		q->q_dst.mbuf = (struct mbuf *)crp->crp_buf;
	} else if (crp->crp_flags & CRYPTO_F_IOV) {
		q->q_src.uio = (struct uio *)crp->crp_buf;
		q->q_dst.uio = (struct uio *)crp->crp_buf;
	} else {
		/* XXX for now... */
		goto errout;
	}

	/*
	 * Check if the crypto descriptors are sane. We accept:
	 * - just one crd; either auth or crypto
	 * - two crds; must be one auth and one crypto, although now
	 *   for encryption we only want the first to be crypto, while
	 *   for decryption the second one should be crypto.
	 */
	maccrd = enccrd = NULL;
	for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
		switch (crd->crd_alg) {
		case CRYPTO_MD5_HMAC:
		case CRYPTO_SHA1_HMAC:
		case CRYPTO_RIPEMD160_HMAC:
			if (maccrd || (enccrd &&
			    (enccrd->crd_flags & CRD_F_ENCRYPT) == 0))
				goto errout;
			maccrd = crd;
			break;
		case CRYPTO_DES_CBC:
		case CRYPTO_3DES_CBC:
			if (enccrd ||
			    (maccrd && (crd->crd_flags & CRD_F_ENCRYPT)))
				goto errout;
			enccrd = crd;
			break;
		default:
			goto errout;
		}
	}
	if (!maccrd && !enccrd)
		goto errout;

	DPRINTF(("%s:ises_process: enc=%p mac=%p\n", dv, enccrd, maccrd));

	/* Select data path through B-interface. */
	q->q_session.omr |= ISES_SELR_BCHU_DIS;

	if (enccrd) {
		encoffset = enccrd->crd_skip;

		/* Select algorithm */
		if (enccrd->crd_alg == CRYPTO_3DES_CBC)
			q->q_session.omr |= ISES_SOMR_BOMR_3DES;
		else
			q->q_session.omr |= ISES_SOMR_BOMR_DES;

		/* Set CBC mode */
		q->q_session.omr |= ISES_SOMR_FMR_CBC;

		if (enccrd->crd_flags & CRD_F_ENCRYPT) {
			/* Set encryption bit */
			q->q_session.omr |= ISES_SOMR_EDR;

			if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
				bcopy(enccrd->crd_iv, q->q_session.sccr, 8);
			else {
				q->q_session.sccr[0] = ses->sccr[0];
				q->q_session.sccr[1] = ses->sccr[1];
			}

			if ((enccrd->crd_flags & CRD_F_IV_PRESENT) == 0) {
				if (crp->crp_flags & CRYPTO_F_IMBUF)
					m_copyback(q->q_src.mbuf,
					    enccrd->crd_inject, 8,
					    (caddr_t)q->q_session.sccr);
				else if (crp->crp_flags & CRYPTO_F_IOV)
					cuio_copyback(q->q_src.uio,
					    enccrd->crd_inject, 8,
					    (caddr_t)q->q_session.sccr);
				/* XXX else ... */
			}
		} else {
			/* Clear encryption bit == decrypt mode */
			q->q_session.omr &= ~ISES_SOMR_EDR;

			if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
				bcopy(enccrd->crd_iv, q->q_session.sccr, 8);
			else if (crp->crp_flags & CRYPTO_F_IMBUF)
				m_copydata(q->q_src.mbuf, enccrd->crd_inject,
				    8, (caddr_t)q->q_session.sccr);
			else if (crp->crp_flags & CRYPTO_F_IOV)
				cuio_copydata(q->q_src.uio,
				    enccrd->crd_inject, 8,
				    (caddr_t)q->q_session.sccr);
			/* XXX else ... */
		}

		q->q_session.kr[0] = ses->kr[0];
		q->q_session.kr[1] = ses->kr[1];
		q->q_session.kr[2] = ses->kr[2];
		q->q_session.kr[3] = ses->kr[3];
		q->q_session.kr[4] = ses->kr[4];
		q->q_session.kr[5] = ses->kr[5];

		SWAP32(q->q_session.sccr[0]);
		SWAP32(q->q_session.sccr[1]);
	}

	if (maccrd) {
		macoffset = maccrd->crd_skip;

		/* Select algorithm */
		switch (crd->crd_alg) {
		case CRYPTO_MD5_HMAC:
			q->q_session.omr |= ISES_HOMR_HFR_MD5;
			break;
		case CRYPTO_SHA1_HMAC:
			q->q_session.omr |= ISES_HOMR_HFR_SHA1;
			break;
		case CRYPTO_RIPEMD160_HMAC:
		default:
			q->q_session.omr |= ISES_HOMR_HFR_RMD160;
			break;
		}

		q->q_session.cvr[0] = ses->cvr[0];
		q->q_session.cvr[1] = ses->cvr[1];
		q->q_session.cvr[2] = ses->cvr[2];
		q->q_session.cvr[3] = ses->cvr[3];
		q->q_session.cvr[4] = ses->cvr[4];
	}

	if (enccrd && maccrd) {
		/* XXX Check if ises handles differing end of auth/enc etc */
		/* XXX For now, assume not (same as ubsec). */
		if (((encoffset + enccrd->crd_len) !=
		    (macoffset + maccrd->crd_len)) ||
		    (enccrd->crd_skip < maccrd->crd_skip)) {
			goto errout;
		}

		sskip = maccrd->crd_skip;
		cpskip = dskip = enccrd->crd_skip;
		stheend = maccrd->crd_len;
		dtheend = enccrd->crd_len;
		coffset = cpskip - sskip;
		cpoffset = cpskip + dtheend;
		/* XXX DEBUG ? */
	} else {
		cpskip = dskip = sskip = macoffset + encoffset;
		dtheend = enccrd ? enccrd->crd_len : maccrd->crd_len;
		stheend = dtheend;
		cpoffset = cpskip + dtheend;
		coffset = 0;
	}
	q->q_offset = coffset >> 2;

#if 0	/* XXX not sure about this, in bus_dma context */

	if (crp->crp_flags & CRYPTO_F_IMBUF)
		q->q_src_l = mbuf2pages(q->q_src.mbuf, &q->q_src_npa,
		    q->q_src_packp, q->q_src_packl, 1, &nicealign);
	else if (crp->crp_flags & CRYPTO_F_IOV)
		q->q_src_l = iov2pages(q->q_src.uio, &q->q_src_npa,
		    q->q_src_packp, q->q_src_packl, 1, &nicealign);
	/* XXX else */

	DPRINTF(("%s:ises_process: foo2pages called!\n", dv));

	if (q->q_src_l == 0)
		goto memerr;
	else if (q->q_src_l > 0xfffc) {
		err = EIO;
		goto errout;
	}

	/* XXX ... */

	if (enccrd == NULL && maccrd != NULL) {
		/* XXX ... */
	} else {
		if (!nicealign && (crp->crp_flags & CRYPTO_F_IOV)) {
			goto errout;
		} else if (!nicealign && (crp->crp_flags & CRYPTO_F_IMBUF)) {
			int totlen, len;
			struct mbuf *m, *top, **mp;

			totlen = q->q_dst_l = q->q_src_l;
			if (q->q_src.mbuf->m_flags & M_PKTHDR) {
				MGETHDR(m, M_DONTWAIT, MT_DATA);
				M_DUP_PKTHDR(m, q->q_src.mbuf);
				len = MHLEN;
			} else {
				MGET(m, M_DONTWAIT, MT_DATA);
				len = MLEN;
			}
			if (m == NULL)
				goto memerr;
			if (totlen >= MINCLSIZE) {
				MCLGET(m, M_DONTWAIT);
				if (m->m_flags & M_EXT)
					len = MCLBYTES;
			}
			m->m_len = len;
			top = NULL;
			mp = &top;

			while (totlen > 0) {
				if (top) {
					MGET(m, M_DONTWAIT, MT_DATA);
					if (m == NULL) {
						m_freem(top);
						goto memerr;
					}
					len = MLEN;
				}
				if (top && totlen >= MINCLSIZE) {
					MCLGET(m, M_DONTWAIT);
					if (m->m_flags & M_EXT)
						len = MCLBYTES;
				}
				m->m_len = len = min(totlen, len);
				totlen -= len;
				*mp = m;

				mp = &m->m_next;
			}
			q->q_dst.mbuf = top;
#if notyet
			ubsec_mcopy(q->q_src.mbuf, q->q_dst.mbuf, cpskip, cpoffset);
#endif
		} else
			q->q_dst.mbuf = q->q_src.mbuf;

#if 0
		/* XXX ? */
		q->q_dst_l = mbuf2pages(q->q_dst.mbuf, &q->q_dst_npa,
		    &q->q_dst_packp, &q->q_dst_packl, 1, NULL);
#endif
	}

#endif /* XXX */

	DPRINTF(("%s:ises_process: queueing request\n", dv));

	s = splnet();
	SIMPLEQ_INSERT_TAIL(&sc->sc_queue, q, q_next);
	sc->sc_nqueue++;
	splx(s);
	ises_feed(sc);

	return (0);

memerr:
	err = ENOMEM;
	isesstats.nomem++;
errout:
	DPRINTF(("%s:ises_process: an error occurred, err=%d, q=%p\n", dv, 
		 err, q));

	if (err == EINVAL)
		isesstats.invalid++;

	if (q) {
		if (q->q_src.mbuf != q->q_dst.mbuf)
			m_freem(q->q_dst.mbuf);
		free(q, M_DEVBUF);
	}
	crp->crp_etype = err;
	crypto_done(crp);
	return (0);
}

void
ises_callback(struct ises_q *q)
{
	struct cryptop *crp = (struct cryptop *)q->q_crp;
	struct cryptodesc *crd;
	struct ises_softc *sc = q->q_sc;
	u_int8_t *sccr;

	if ((crp->crp_flags & CRYPTO_F_IMBUF) && 
	    (q->q_src.mbuf != q->q_dst.mbuf)) {
		m_freem(q->q_src.mbuf);
		crp->crp_buf = (caddr_t)q->q_dst.mbuf;
	}

	if (q->q_session.omr & ISES_SOMR_EDR) {
		/* Copy out IV after encryption. */
		sccr = (u_int8_t *)&sc->sc_sessions[q->q_sesn].sccr;
		for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
			if (crd->crd_alg != CRYPTO_DES_CBC &&
			    crd->crd_alg != CRYPTO_3DES_CBC)
				continue;
			if (crp->crp_flags & CRYPTO_F_IMBUF)
				m_copydata((struct mbuf *)crp->crp_buf,
				    crd->crd_skip + crd->crd_len - 8, 8, sccr);
			else if (crp->crp_flags & CRYPTO_F_IOV)
				cuio_copydata((struct uio *)crp->crp_buf,
				    crd->crd_skip + crd->crd_len - 8, 8, sccr);
		}
	}

	for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
		if (crd->crd_alg != CRYPTO_MD5_HMAC &&
		    crd->crd_alg != CRYPTO_SHA1_HMAC &&
		    crd->crd_alg != CRYPTO_RIPEMD160_HMAC)
			continue;
		if (crp->crp_flags & CRYPTO_F_IMBUF)
			m_copyback((struct mbuf *)crp->crp_buf,
			   crd->crd_inject, 12, (u_int8_t *)q->q_macbuf);
		else if (crp->crp_flags & CRYPTO_F_IOV)
			bcopy((u_int8_t *)q->q_macbuf, crp->crp_mac, 12);
		/* XXX else ... */
		break;
	}

	free(q, M_DEVBUF);
	DPRINTF(("%s:ises_callback: calling crypto_done\n",
	    sc->sc_dv.dv_xname));
	crypto_done(crp);
}

/* Initilize the ISES hardware RNG, and set up timeouts. */
void
ises_hrng_init(struct ises_softc *sc)
{
	u_int32_t cmd, r;
	int i;
#ifdef ISESDEBUG
	struct timeval tv1, tv2;
#endif

	/* Asking for random data will seed LFSR and start the RBG */
	cmd = ISES_MKCMD(ISES_CMD_HBITS, 1);
	r   = 8; /* 8 * 32 = 256 bits */

	if (ises_queue_cmd(sc, cmd, &r, NULL))
		return;

	/* Wait until response arrives. */
	for (i = 1000; i && READ_REG(sc, ISES_A_OQS) == 0; i--)
		DELAY(10);

	if (!READ_REG(sc, ISES_A_OQS))
		return;

	/* Drain cmd response and 8*32 bits data */
	for (i = 0; i <= r; i++)
		(void)READ_REG(sc, ISES_A_OQD);

	/* ACK the response */
	WRITE_REG(sc, ISES_A_OQS, 0);
	DELAY(1);
	printf(", rng active");

#ifdef ISESDEBUG
	/* Benchmark the HRNG. */

	/*
	 * XXX These values gets surprisingly large. Docs state the
	 * HNRG produces > 1 mbit/s of random data. The values I'm seeing
	 * are much higher, ca 2.7-2.8 mbit/s. AFAICT the algorithm is sound.
	 * Compiler optimization issues, perhaps?
	 */

#define ISES_WPR 250
#define ISES_ROUNDS 100
	cmd = ISES_MKCMD(ISES_CMD_HBITS, 1);
	r = ISES_WPR;

	/* Queue 100 cmds; each generate 250 32-bit words of rnd data. */
	microtime(&tv1);
	for (i = 0; i < ISES_ROUNDS; i++)
		ises_queue_cmd(sc, cmd, &r, NULL);
	for (i = 0; i < ISES_ROUNDS; i++) {
		while (READ_REG(sc, ISES_A_OQS) == 0) ; /* Wait for response */

		(void)READ_REG(sc, ISES_A_OQD);		/* read response */
		for (r = ISES_WPR; r--;)
			(void)READ_REG(sc, ISES_A_OQD);	/* read data */
		WRITE_REG(sc, ISES_A_OQS, 0);		/* ACK resp */
		DELAY(1); /* OQS needs 1us to decrement */
	}
	microtime(&tv2);

	timersub(&tv2, &tv1, &tv1);
	tv1.tv_usec += 1000000 * tv1.tv_sec;
	printf(", %dKb/sec",
	    ISES_WPR * ISES_ROUNDS * 32 / 1024 * 1000000 / tv1.tv_usec);
#endif

	timeout_set(&sc->sc_timeout, ises_hrng, sc);
#ifndef ISES_HRNG_DISABLED
	ises_hrng(sc); /* Call first update */
#endif
}

/* Called by timeout (and once by ises_init_hrng()). */
void
ises_hrng(void *v)
{
	/*
	 * Throw a HRNG read random bits command on the command queue.
	 * The normal loop will manage the result and add it to the pool.
	 */
	struct ises_softc *sc = v;
	u_int32_t cmd, n;
	extern int hz; /* from param.c */

	timeout_add(&sc->sc_timeout, hz / ISESRNGIPS);

	if (ises_assert_cmd_mode(sc) != 0)
		return;

	cmd = ISES_MKCMD(ISES_CMD_HBITS, 1);
	n   = (ISESRNGBITS >> 5) & 0xff; /* ask for N 32 bit words */

	ises_queue_cmd(sc, cmd, &n, NULL);
}

u_int32_t
ises_get_fwversion(struct ises_softc *sc)
{
	u_int32_t r;
	int i;

	r = ISES_MKCMD(ISES_CMD_CHIP_ID, 0);
	WRITE_REG(sc, ISES_A_IQD, r);
	WRITE_REG(sc, ISES_A_IQS, 0);

	for (i = 100; i > 0 && READ_REG(sc, ISES_A_OQS) == 0; i--)
		DELAY(1);

	if (i < 1)
		return (0); /* No response */

	r = READ_REG(sc, ISES_A_OQD);

	/* Check validity. On error drain reponse data. */
	if (((r >> 16) & 0xff) != ISES_CMD_CHIP_ID ||
	    ((r >> 24) & 0xff) != 3 || (r & ISES_RC_MASK) != ISES_RC_SUCCESS) {
		if ((r & ISES_RC_MASK) == ISES_RC_SUCCESS)
			for (i = ((r >> 24) & 0xff); i; i--)
				(void)READ_REG(sc, ISES_A_OQD);
		r = 0;
		goto out;
	}

	r = READ_REG(sc, ISES_A_OQD); /* read version */
	(void)READ_REG(sc, ISES_A_OQD); /* Discard 64bit "chip-id" */
	(void)READ_REG(sc, ISES_A_OQD);
 out:
	WRITE_REG(sc, ISES_A_OQS, 0); /* Ack the response */
	DELAY(1);
	return (r);
}

/*
 * ises_assert_cmd_mode() returns
 *   -1 for failure to go to cmd
 *    0 if mode already was cmd
 *   >0 if mode was other (WFC/WFR) but now is cmd (this has reset the queues)
 */
int
ises_assert_cmd_mode(struct ises_softc *sc)
{
	switch (ISES_STAT_SW_MODE(READ_REG(sc, ISES_A_STAT))) {
	case 0x0: /* Selftest. XXX This is a transient state. */
		DELAY(1000000);
		if (ISES_STAT_SW_MODE(READ_REG(sc, ISES_A_STAT)) == 0)
			return (-1);
		return (ises_assert_cmd_mode(sc));
	case 0x1: /* Command mode */
		return (0);
	case 0x2: /* Waiting For Continue / WFC */
		bus_space_write_2(sc->sc_memt, sc->sc_memh, ISES_A_CTRL,
		    ISES_A_CTRL_CONTINUE);
		DELAY(1);
		return ((ISES_STAT_SW_MODE(READ_REG(sc, ISES_A_STAT)) == 0) ?
		    1 : -1);
	case 0x3: /* Waiting For Reset / WFR */
		bus_space_write_2(sc->sc_memt, sc->sc_memh, ISES_A_CTRL,
		    ISES_A_CTRL_RESET);
		DELAY(1000000);
		return ((ISES_STAT_SW_MODE(READ_REG(sc, ISES_A_STAT)) == 0) ?
		    2 : -1);
	default:
		return (-1); /* Unknown mode */
	}
}

int
ises_bchu_switch_session (struct ises_softc *sc, struct ises_session *ss, 
			  int new_session)
{
	/* It appears that the BCHU_SWITCH_SESSION command is broken. */
	/* We have to work around it. */
	
	u_int32_t cmd;

	/* Do we have enough in-queue space? Count cmds + data, 16bit words. */
	if ((8 * 2 + sizeof (*ss) / 2) > READ_REG(sc, ISES_A_IQF))
		return (0);

	/* Mark 'switch' in progress. */
	sc->sc_switching = new_session + 1;

	/* Write the key. */
	cmd = ISES_MKCMD(ISES_CMD_BW_KR0, 2);
	ises_queue_cmd(sc, cmd, &ss->kr[4], NULL);
	cmd = ISES_MKCMD(ISES_CMD_BW_KR1, 2);
	ises_queue_cmd(sc, cmd, &ss->kr[2], NULL);
	cmd = ISES_MKCMD(ISES_CMD_BW_KR2, 2);
	ises_queue_cmd(sc, cmd, &ss->kr[0], NULL);

	/* Write OMR - Operation Method Register, clears SCCR+CVR+DBCR+HMLR */
	cmd = ISES_MKCMD(ISES_CMD_BW_OMR, 1);
	ises_queue_cmd(sc, cmd, &ss->omr, NULL);

	/* Write SCCR - Symmetric Crypto Chaining Register (IV) */
	cmd = ISES_MKCMD(ISES_CMD_BW_SCCR, 2);
	ises_queue_cmd(sc, cmd, &ss->sccr[0], NULL);

	/* Write CVR - Chaining Variables Register (hash state) */
	cmd = ISES_MKCMD(ISES_CMD_BW_CVR, 5);
	ises_queue_cmd(sc, cmd, &ss->cvr[0], NULL);

	/* Write DBCR - Data Block Count Register */
	cmd = ISES_MKCMD(ISES_CMD_BW_DBCR, 2);
	ises_queue_cmd(sc, cmd, &ss->dbcr[0], NULL);

	/* Write HMLR - Hash Message Length Register - last cmd in switch */
	cmd = ISES_MKCMD(ISES_CMD_BW_HMLR, 2);
	ises_queue_cmd(sc, cmd, &ss->hmlr[0], ises_bchu_switch_final);

	return (1);
}

u_int32_t
ises_bchu_switch_final (struct ises_softc *sc, struct ises_cmd *cmd)
{
	/* Session switch is complete. */

	DPRINTF(("%s:ises_bchu_switch_final: switch complete\n",
	    sc->sc_dv.dv_xname));

	sc->sc_cursession = sc->sc_switching - 1;
	sc->sc_switching = 0;

	/* Retry/restart feed. */
	ises_feed(sc);

	return (0);
}

/* XXX Currently unused. */
void
ises_read_dma (struct ises_softc *sc)
{
	bus_dma_segment_t *ds = &sc->sc_dmamap->dm_segs[0];
	u_int32_t dma_status;

	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0,
	    sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD);

	WRITE_REG(sc, ISES_DMA_READ_START, ds->ds_addr);
	WRITE_REG(sc, ISES_DMA_READ_START, ISES_DMA_RCOUNT(ds->ds_len));

	dma_status = READ_REG(sc, ISES_DMA_STATUS);
	dma_status |= ISES_DMA_CTRL_ILT | ISES_DMA_CTRL_WRITE;
	WRITE_REG(sc, ISES_DMA_CTRL, dma_status);
}

#ifdef ISESDEBUG
/*
 * Development code section below here.
 */

void
ises_debug_init (struct ises_softc *sc)
{
	ises_sc = sc;
	ises_db = 0;
	timeout_set (&ises_db_timeout, ises_debug_loop, sc);
	timeout_add (&ises_db_timeout, 100);
	printf ("ises0: ISESDEBUG active (ises_sc = %p)\n", ises_sc);
}

void
ises_debug_2 (void)
{
	timeout_set (&ises_db_timeout, ises_debug_loop, ises_sc);
	timeout_add (&ises_db_timeout, 100);
	printf ("ises0: another debug timeout scheduled!\n");
}

void
ises_debug_simple_cmd (struct ises_softc *sc, u_int32_t code, u_int32_t d)
{
	u_int32_t cmd, data;
	
	cmd = ISES_MKCMD(code, (d ? 1 : 0));
	data = d;
	ises_queue_cmd(sc, cmd, &d, NULL);
}

void
ises_debug_loop (void *v)
{
	struct ises_softc *sc = (struct ises_softc *)v;
	struct ises_session ses;
	u_int32_t cmd, stat;
	int i;

	if (ises_db)
		printf ("ises0: ises_db = %d  sc = %p\n", ises_db, sc);

	timeout_add (&ises_db_timeout, 300); /* Every 3 secs */

	stat = READ_REG(sc, ISES_A_OQS);
	cmd  = READ_REG(sc, ISES_A_IQS);
	if (stat || cmd)
		printf ("ises0: IQS=%d OQS=%d / IQF=%d OQF=%d\n",
		    cmd, stat, READ_REG(sc, ISES_A_IQF),
		    READ_REG(sc, ISES_A_OQF));
	
	switch (ises_db) {
	default: 
		/* 0 - do nothing (just loop) */
		break;
	case 1:
		/* Just dump register info */
		ises_showreg();
		break;
	case 2:
		/* Reset LNAU 1 registers */
		ises_debug_simple_cmd(sc, ISES_CMD_LRESET_1, 0);
		
		/* Compute R = (141 * 5623) % 117 (R should be 51 (0x33)) */
		ises_debug_simple_cmd(sc, ISES_CMD_LW_A_1, 141);
		ises_debug_simple_cmd(sc, ISES_CMD_LW_B_1, 5623);
		ises_debug_simple_cmd(sc, ISES_CMD_LW_N_1, 117);
		
		/* Launch LNAU operation. */
		ises_debug_simple_cmd(sc, ISES_CMD_LMULMOD_1, 0);
		break;
	case 3:
		/* Read result LNAU_1 R register (should not be necessary) */
		ises_debug_simple_cmd(sc, ISES_CMD_LUPLOAD_1, 0);
		break;
	case 4:
		/* Print result */
		printf ("LNAU_1 R length = %d\n", sc->sc_lnau1_rlen);
		for (i = 0; i < sc->sc_lnau1_rlen; i++)
			printf ("W%02d-[%08x]-(%u)\t%s", i, sc->sc_lnau1_r[i],
			    sc->sc_lnau1_r[i], (i%4)==3 ? "\n" : "");
		printf ("%s", (i%4) ? "\n" : "");
		break;
	case 5:
		/* Crypto. */

		/* Load BCHU session data */
		bzero(&ses, sizeof ses);
		ses.kr[0] = 0xD0;
		ses.kr[1] = 0xD1;
		ses.kr[2] = 0xD2;
		ses.kr[3] = 0xD3;
		ses.kr[4] = 0xD4;
		ses.kr[5] = 0xD5;

		/* cipher data out is hash in, SHA1, 3DES, encrypt, ECB */
		ses.omr = ISES_SELR_BCHU_HISOF | ISES_HOMR_HFR_SHA1 |
		    ISES_SOMR_BOMR_3DES | ISES_SOMR_EDR | ISES_SOMR_FMR_ECB;

#if 1
		printf ("Queueing home-cooked session switch\n");
		ises_bchu_switch_session(sc, &ses, 0);
#else /* switch session does not appear to work - it never returns */
		printf ("Queueing BCHU session switch\n");
		cmd = ISES_MKCMD(ISES_CMD_BSWITCH, sizeof ses / 4);
		printf ("session is %d 32bit words (== 18 ?), cmd = [%08x]\n", 
			sizeof ses / 4, cmd);
		ises_queue_cmd(sc, cmd, (u_int32_t *)&ses, NULL);
#endif
		
		break;
	case 96:
		printf ("Stopping HRNG data collection\n");
		timeout_del(&sc->sc_timeout);
		break;
	case 97:
		printf ("Restarting HRNG data collection\n");
		if (!timeout_pending(&sc->sc_timeout))
			timeout_add(&sc->sc_timeout, hz);
		break;
	case 98:
		printf ("Resetting (wait >1s before cont.)\n");
		stat = ISES_BO_STAT_HWRESET;
		WRITE_REG(sc, ISES_BO_STAT, stat);
		stat &= ~ISES_BO_STAT_HWRESET;
		WRITE_REG(sc, ISES_BO_STAT, stat);
		break;
	case 99:
		printf ("Resetting everything!\n");
		if (timeout_pending(&sc->sc_timeout))
			timeout_del(&sc->sc_timeout);
		timeout_set(&sc->sc_timeout, ises_initstate, sc);
		sc->sc_initstate = 0;
		ises_initstate(sc);
		break;
	}
	
	ises_db = 0; 
}

void
ises_showreg (void)
{
	struct ises_softc *sc = ises_sc;
	u_int32_t stat, cmd;
	
	/* Board register */
	
	printf ("Board register: ");
	stat = READ_REG(sc, ISES_BO_STAT);
	
	if (stat & ISES_BO_STAT_LOOP)
		printf ("LoopMode ");
	if (stat & ISES_BO_STAT_TAMPER)
		printf ("Tamper ");
	if (stat & ISES_BO_STAT_POWERDOWN)
		printf ("PowerDown ");
	if (stat & ISES_BO_STAT_ACONF)
		printf ("16bitA-IF ");
	if (stat & ISES_BO_STAT_HWRESET)
		printf ("HWReset");
	if (stat & ISES_BO_STAT_AIRQ)
		printf ("A-IFintr");
	printf("\n");
	
	/* A interface */
	
	printf ("A Interface STAT register: \n\tLNAU-[");
	stat = READ_REG(sc, ISES_A_STAT);
	if (stat & ISES_STAT_LNAU_MASKED)
		printf ("masked");
	else {
		if (stat & ISES_STAT_LNAU_BUSY_1)
			printf ("busy1 ");
		if (stat & ISES_STAT_LNAU_ERR_1)
			printf ("err1 ");
		if (stat & ISES_STAT_LNAU_BUSY_2)
			printf ("busy2 ");
		if (stat & ISES_STAT_LNAU_ERR_2)
			printf ("err2 ");
	}
	printf ("]\n\tBCHU-[");
	
	if (stat & ISES_STAT_BCHU_MASKED)
		printf ("masked");
	else {
		if (stat & ISES_STAT_BCHU_BUSY)
			printf ("busy ");
		if (stat & ISES_STAT_BCHU_ERR)
			printf ("err ");
		if (stat & ISES_STAT_BCHU_SCIF)
			printf ("cr-inop ");
		if (stat & ISES_STAT_BCHU_HIF)
			printf ("ha-inop ");
		if (stat & ISES_STAT_BCHU_DDB)
			printf ("dscd-data ");
		if (stat & ISES_STAT_BCHU_IRF)
			printf ("inp-req ");
		if (stat & ISES_STAT_BCHU_OAF)
			printf ("out-avail ");
		if (stat & ISES_STAT_BCHU_DIE)
			printf ("inp-enabled ");
		if (stat & ISES_STAT_BCHU_UE)
			printf ("ififo-empty ");
		if (stat & ISES_STAT_BCHU_IFE)
			printf ("ififo-half ");
		if (stat & ISES_STAT_BCHU_IFHE)
			printf ("ififo-full ");
		if (stat & ISES_STAT_BCHU_OFE)
			printf ("ofifo-empty ");
		if (stat & ISES_STAT_BCHU_OFHF)
			printf ("ofifo-half ");
		if (stat & ISES_STAT_BCHU_OFF)
			printf ("ofifo-full ");
	}
	printf ("] \n\tmisc-[");
	
	if (stat & ISES_STAT_HW_DA)
		printf ("downloaded-appl ");
	if (stat & ISES_STAT_HW_ACONF)
		printf ("A-IF-conf ");
	if (stat & ISES_STAT_SW_WFOQ)
		printf ("OQ-wait ");
	if (stat & ISES_STAT_SW_OQSINC)
		printf ("OQS-increased ");
	printf ("]\n\t");
	
	if (stat & ISES_STAT_HW_DA)
		printf ("SW-mode is \"%s\"", 
		    ises_sw_mode[ISES_STAT_SW_MODE(stat)]);
	else
		printf ("IDP-state is \"%s\"", 
		    ises_idp_state[ISES_STAT_IDP_STATE(stat)]);
	printf ("\n");

	printf ("\tOQS = %d  IQS = %d  OQF = %d  IQF = %d\n", 
	    READ_REG(sc, ISES_A_OQS), READ_REG(sc, ISES_A_IQS),
	    READ_REG(sc, ISES_A_OQF), READ_REG(sc, ISES_A_IQF));
	
	/* B interface */
	
	printf ("B-interface status register contains [%08x]\n", 
	    READ_REG(sc, ISES_B_STAT));
	
	/* DMA */
	
	printf ("DMA read starts at 0x%x, length %d bytes\n", 
	    READ_REG(sc, ISES_DMA_READ_START), 
	    READ_REG(sc, ISES_DMA_READ_COUNT) >> 16);
	
	printf ("DMA write starts at 0x%x, length %d bytes\n",
	    READ_REG(sc, ISES_DMA_WRITE_START),
	    READ_REG(sc, ISES_DMA_WRITE_COUNT) & 0x00ff);

	stat = READ_REG(sc, ISES_DMA_STATUS);
	printf ("DMA status register contains [%08x]\n", stat);

	if (stat & ISES_DMA_CTRL_ILT)
		printf (" -- Ignore latency timer\n");
	if (stat & 0x0C000000)
		printf (" -- PCI Read - multiple\n");
	else if (stat & 0x08000000)
		printf (" -- PCI Read - line\n");

	if (stat & ISES_DMA_STATUS_R_RUN)
		printf (" -- PCI Read running/incomplete\n");
	else
		printf (" -- PCI Read complete\n");
	if (stat & ISES_DMA_STATUS_R_ERR)
		printf (" -- PCI Read DMA Error\n");

	if (stat & ISES_DMA_STATUS_W_RUN)
		printf (" -- PCI Write running/incomplete\n");
	else
		printf (" -- PCI Write complete\n");
	if (stat & ISES_DMA_STATUS_W_ERR)
		printf (" -- PCI Write DMA Error\n");

	/* OMR / HOMR / SOMR */
	
	/*
	 * All these means throwing a cmd on to the A-interface, and then
	 * reading the result.
	 *
	 * Currently, put debug output in process_oqueue...
	 */
	
	printf ("Queueing Operation Method Register (OMR) READ cmd...\n");
	cmd = ISES_MKCMD(ISES_CMD_BR_OMR, 0);
	ises_queue_cmd(sc, cmd, NULL, NULL);
}

void
ises_debug_parse_omr (struct ises_softc *sc)
{
	u_int32_t omr = sc->sc_sessions[sc->sc_cursession].omr;
	
	printf ("SELR : ");
	if (omr & ISES_SELR_BCHU_EH)
		printf ("cont-on-error ");
	else
		printf ("stop-on-error ");
	
	if (omr & ISES_SELR_BCHU_HISOF)
		printf ("HU-input-is-SCU-output ");
	
	if (omr & ISES_SELR_BCHU_DIS)
		printf ("data-interface-select=B ");
	else
		printf ("data-interface-select=DataIn/DataOut ");
	
	printf ("\n");
	
	printf ("HOMR : ");
	if (omr & ISES_HOMR_HMTR)
		printf ("expect-padded-hash-msg ");
	else
		printf ("expect-plaintext-hash-msg ");
	
	printf ("ER=%d ", (omr & ISES_HOMR_ER) >> 20); /* ick */
	
	printf ("HFR=");
	switch (omr & ISES_HOMR_HFR) {
	case ISES_HOMR_HFR_NOP:
		printf ("inactive ");
		break;
	case ISES_HOMR_HFR_MD5:
		printf ("MD5 ");
		break;
	case ISES_HOMR_HFR_RMD160:
		printf ("RMD160 ");
		break;
	case ISES_HOMR_HFR_RMD128:
		printf ("RMD128 ");
		break;
	case ISES_HOMR_HFR_SHA1:
		printf ("SHA-1 ");
		break;
	default:
		printf ("reserved! ");
		break;
	}
	printf ("\nSOMR : ");
	
	switch (omr & ISES_SOMR_BOMR) {
	case ISES_SOMR_BOMR_NOP:
		printf ("NOP ");
		break;
	case ISES_SOMR_BOMR_TRANSPARENT:
		printf ("transparent ");
		break;
	case ISES_SOMR_BOMR_DES:
		printf ("DES ");
		break;
	case ISES_SOMR_BOMR_3DES2:
		printf ("3DES-2 ");
		break;
	case ISES_SOMR_BOMR_3DES:
		printf ("3DES-3 ");
		break;
	default:
		if (omr & ISES_SOMR_BOMR_SAFER)
			printf ("SAFER ");
		else
			printf ("reserved! ");
		break;
	}
	
	if (omr & ISES_SOMR_EDR)
		printf ("mode=encrypt ");
	else
		printf ("mode=decrypt ");
	
	switch (omr & ISES_SOMR_FMR) {
	case ISES_SOMR_FMR_ECB:
		printf ("ECB");
		break;
	case ISES_SOMR_FMR_CBC:
		printf ("CBC");
		break;
	case ISES_SOMR_FMR_CFB64:
		printf ("CFB64");
		break;
	case ISES_SOMR_FMR_OFB64:
		printf ("OFB64");
		break;
	default:
		/* Nada */
	}
	printf ("\n");
}

#endif /* ISESDEBUG */