From 0dae8a405b3fba6239032a94e113bceed3ade950 Mon Sep 17 00:00:00 2001
From: Alexander Yurchenko <grange@cvs.openbsd.org>
Date: Mon, 9 Mar 2009 20:21:51 +0000
Subject: Completely rework command processing:

- use separate callbacks to finish scsi and raid management commands;
- for raid management commands use dedicated dma-safe buffer;
- remove unused run queue;

The main goal of these changes is to fix a bug showing up on the lpinto's
machine where READCONF command fails because it needs more than 1-page
buffer and the buffer was not contiguos and controller doesn't support
scatter-gather for raid management commands.
---
 sys/dev/pci/ips.c | 560 +++++++++++++++++++++++++++++++++---------------------
 1 file changed, 342 insertions(+), 218 deletions(-)

(limited to 'sys/dev')

diff --git a/sys/dev/pci/ips.c b/sys/dev/pci/ips.c
index 013ccf9a623..09dc169f84c 100644
--- a/sys/dev/pci/ips.c
+++ b/sys/dev/pci/ips.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: ips.c,v 1.51 2009/03/01 19:54:23 grange Exp $	*/
+/*	$OpenBSD: ips.c,v 1.52 2009/03/09 20:21:50 grange Exp $	*/
 
 /*
  * Copyright (c) 2006, 2007, 2009 Alexander Yurchenko <grange@openbsd.org>
@@ -17,7 +17,7 @@
  */
 
 /*
- * IBM (Adaptec) ServeRAID controller driver.
+ * IBM (Adaptec) ServeRAID controllers driver.
  */
 
 #include "bio.h"
@@ -192,21 +192,6 @@ struct ips_driveinfo {
 	}		drive[IPS_MAXDRIVES];
 };
 
-struct ips_pg5 {
-	u_int32_t	signature;
-	u_int8_t	__reserved1;
-	u_int8_t	slot;
-	u_int16_t	type;
-	u_int8_t	bioshi[4];
-	u_int8_t	bioslo[4];
-	u_int16_t	__reserved2;
-	u_int8_t	__reserved3;
-	u_int8_t	os;
-	u_int8_t	driverhi[4];
-	u_int8_t	driverlo[4];
-	u_int8_t	__reserved4[100];
-};
-
 struct ips_conf {
 	u_int8_t	ldcnt;
 	u_int8_t	day;
@@ -266,7 +251,30 @@ struct ips_conf {
 	u_int8_t	reserved[512];
 };
 
+struct ips_pg5 {
+	u_int32_t	signature;
+	u_int8_t	__reserved1;
+	u_int8_t	slot;
+	u_int16_t	type;
+	u_int8_t	bioshi[4];
+	u_int8_t	bioslo[4];
+	u_int16_t	__reserved2;
+	u_int8_t	__reserved3;
+	u_int8_t	os;
+	u_int8_t	driverhi[4];
+	u_int8_t	driverlo[4];
+	u_int8_t	__reserved4[100];
+};
+
+struct ips_info {
+	struct ips_adapterinfo	adapter;
+	struct ips_driveinfo	drive;
+	struct ips_conf		conf;
+	struct ips_pg5		pg5;
+};
+
 /* Command control block */
+struct ips_softc;
 struct ips_ccb {
 	int			c_id;		/* command id */
 	int			c_flags;	/* flags */
@@ -282,6 +290,9 @@ struct ips_ccb {
 	int			c_stat;		/* status word copy */
 	int			c_estat;	/* ext status word copy */
 
+	void			(*c_done)(struct ips_softc *,	/* cmd done */
+				    struct ips_ccb *);		/* callback */
+
 	TAILQ_ENTRY(ips_ccb)	c_link;		/* queue link */
 };
 
@@ -313,8 +324,9 @@ struct ips_softc {
 
 	const struct ips_chipset *sc_chip;
 
-	struct ips_conf		sc_conf;
-	struct ips_driveinfo	sc_di;
+	struct ips_info *	sc_info;
+	struct dmamem		sc_infom;
+
 	int			sc_nunits;
 
 	struct dmamem		sc_cmdm;
@@ -322,7 +334,6 @@ struct ips_softc {
 	struct ips_ccb *	sc_ccb;
 	int			sc_nccbs;
 	struct ips_ccbq		sc_ccbq_free;
-	struct ips_ccbq		sc_ccbq_run;
 
 	struct dmamem		sc_sqm;
 	paddr_t			sc_sqtail;
@@ -344,18 +355,19 @@ int	ips_ioctl_disk(struct ips_softc *, struct bioc_disk *);
 
 void	ips_sensors(void *);
 
-int	ips_cmd(struct ips_softc *, int, int, u_int32_t, void *, size_t, int,
-	    struct scsi_xfer *);
+int	ips_load(struct ips_softc *, struct ips_ccb *, struct scsi_xfer *);
+int	ips_cmd(struct ips_softc *, struct ips_ccb *);
 int	ips_poll(struct ips_softc *, struct ips_ccb *);
-void	ips_done(struct ips_softc *, struct ips_ccb *);
+void	ips_done_xs(struct ips_softc *, struct ips_ccb *);
+void	ips_done_mgmt(struct ips_softc *, struct ips_ccb *);
 int	ips_intr(void *);
 void	ips_timeout(void *);
 
-int	ips_getadapterinfo(struct ips_softc *, struct ips_adapterinfo *);
-int	ips_getconf(struct ips_softc *, struct ips_conf *);
-int	ips_getdriveinfo(struct ips_softc *, struct ips_driveinfo *);
+int	ips_getadapterinfo(struct ips_softc *);
+int	ips_getdriveinfo(struct ips_softc *);
+int	ips_getconf(struct ips_softc *);
+int	ips_getpg5(struct ips_softc *);
 int	ips_flush(struct ips_softc *);
-int	ips_readnvram(struct ips_softc *, void *, int);
 
 void	ips_copperhead_exec(struct ips_softc *, struct ips_ccb *);
 void	ips_copperhead_init(struct ips_softc *);
@@ -490,8 +502,9 @@ ips_attach(struct device *parent, struct device *self, void *aux)
 	struct pci_attach_args *pa = aux;
 	struct ips_ccb ccb0;
 	struct scsibus_attach_args saa;
-	struct ips_adapterinfo ai;
-	struct ips_pg5 pg5;
+	struct ips_adapterinfo *ai;
+	struct ips_driveinfo *di;
+	struct ips_pg5 *pg5;
 	pcireg_t maptype;
 	bus_size_t iosize;
 	pci_intr_handle_t ih;
@@ -524,11 +537,22 @@ ips_attach(struct device *parent, struct device *self, void *aux)
 		goto fail1;
 	}
 
+	/* Allocate info buffer */
+	if (ips_dmamem_alloc(&sc->sc_infom, sc->sc_dmat,
+	    sizeof(struct ips_info))) {
+		printf(": can't alloc info buffer\n");
+		goto fail2;
+	}
+	sc->sc_info = sc->sc_infom.dm_vaddr;
+	ai = &sc->sc_info->adapter;
+	di = &sc->sc_info->drive;
+	pg5 = &sc->sc_info->pg5;
+
 	/* Allocate status queue for the Copperhead chipset */
 	if (sc->sc_chip->ic_id == IPS_CHIP_COPPERHEAD) {
 		if (ips_dmamem_alloc(&sc->sc_sqm, sc->sc_dmat, IPS_SQSZ)) {
 			printf(": can't alloc status queue\n");
-			goto fail2;
+			goto fail3;
 		}
 		sc->sc_sqtail = sc->sc_sqm.dm_paddr;
 		sc->sc_sqbuf = sc->sc_sqm.dm_vaddr;
@@ -549,52 +573,38 @@ ips_attach(struct device *parent, struct device *self, void *aux)
 	bzero(&ccb0, sizeof(ccb0));
 	ccb0.c_cmdva = sc->sc_cmdm.dm_vaddr;
 	ccb0.c_cmdpa = sc->sc_cmdm.dm_paddr;
-	if (bus_dmamap_create(sc->sc_dmat, IPS_MAXFER, IPS_MAXSGS,
-	    IPS_MAXFER, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
-	    &ccb0.c_dmam)) {
-		printf(": can't bootstrap ccb queue\n");
-		goto fail3;
-	}
 	TAILQ_INIT(&sc->sc_ccbq_free);
-	TAILQ_INIT(&sc->sc_ccbq_run);
 	TAILQ_INSERT_TAIL(&sc->sc_ccbq_free, &ccb0, c_link);
 
 	/* Get adapter info */
-	if (ips_getadapterinfo(sc, &ai)) {
+	if (ips_getadapterinfo(sc)) {
 		printf(": can't get adapter info\n");
-		bus_dmamap_destroy(sc->sc_dmat, ccb0.c_dmam);
-		goto fail3;
-	}
-
-	/* Get configuration */
-	if (ips_getconf(sc, &sc->sc_conf)) {
-		printf(": can't get config\n");
-		bus_dmamap_destroy(sc->sc_dmat, ccb0.c_dmam);
-		goto fail3;
+		goto fail4;
 	}
 
 	/* Get logical drives info */
-	if (ips_getdriveinfo(sc, &sc->sc_di)) {
+	if (ips_getdriveinfo(sc)) {
 		printf(": can't get ld info\n");
-		bus_dmamap_destroy(sc->sc_dmat, ccb0.c_dmam);
-		goto fail3;
+		goto fail4;
 	}
-	sc->sc_nunits = sc->sc_di.drivecnt;
+	sc->sc_nunits = di->drivecnt;
 
-	/* Read NVRAM page 5 for additional info */
-	bzero(&pg5, sizeof(pg5));
-	ips_readnvram(sc, &pg5, 5);
+	/* Get configuration */
+	if (ips_getconf(sc)) {
+		printf(": can't get config\n");
+		goto fail4;
+	}
 
-	bus_dmamap_destroy(sc->sc_dmat, ccb0.c_dmam);
+	/* Read NVRAM page 5 for additional info */
+	(void)ips_getpg5(sc);
 
 	/* Initialize CCB queue */
-	sc->sc_nccbs = ai.cmdcnt;
+	sc->sc_nccbs = ai->cmdcnt;
 	if ((sc->sc_ccb = ips_ccb_alloc(sc, sc->sc_nccbs)) == NULL) {
 		printf(": can't alloc ccb queue\n");
-		goto fail3;
+		goto fail4;
 	}
 	TAILQ_INIT(&sc->sc_ccbq_free);
-	TAILQ_INIT(&sc->sc_ccbq_run);
 	for (i = 0; i < sc->sc_nccbs; i++)
 		TAILQ_INSERT_TAIL(&sc->sc_ccbq_free,
 		    &sc->sc_ccb[i], c_link);
@@ -602,7 +612,7 @@ ips_attach(struct device *parent, struct device *self, void *aux)
 	/* Install interrupt handler */
 	if (pci_intr_map(pa, &ih)) {
 		printf(": can't map interrupt\n");
-		goto fail4;
+		goto fail5;
 	}
 	intrstr = pci_intr_string(pa->pa_pc, ih);
 	if (pci_intr_establish(pa->pa_pc, ih, IPL_BIO, ips_intr, sc,
@@ -611,20 +621,20 @@ ips_attach(struct device *parent, struct device *self, void *aux)
 		if (intrstr != NULL)
 			printf(" at %s", intrstr);
 		printf("\n");
-		goto fail4;
+		goto fail5;
 	}
 	printf(": %s\n", intrstr);
 
 	/* Display adapter info */
 	printf("%s: ServeRAID", sc->sc_dev.dv_xname);
-	type = letoh16(pg5.type);
+	type = letoh16(pg5->type);
 	if (type < sizeof(ips_names) / sizeof(ips_names[0]) && ips_names[type])
 		printf(" %s", ips_names[type]);
-	printf(", FW %c%c%c%c%c%c%c", ai.firmware[0], ai.firmware[1],
-	    ai.firmware[2], ai.firmware[3], ai.firmware[4], ai.firmware[5],
-	    ai.firmware[6]);
-	printf(", BIOS %c%c%c%c%c%c%c", ai.bios[0], ai.bios[1], ai.bios[2],
-	    ai.bios[3], ai.bios[4], ai.bios[5], ai.bios[6]);
+	printf(", FW %c%c%c%c%c%c%c", ai->firmware[0], ai->firmware[1],
+	    ai->firmware[2], ai->firmware[3], ai->firmware[4], ai->firmware[5],
+	    ai->firmware[6]);
+	printf(", BIOS %c%c%c%c%c%c%c", ai->bios[0], ai->bios[1], ai->bios[2],
+	    ai->bios[3], ai->bios[4], ai->bios[5], ai->bios[6]);
 	printf(", %d cmds, %d LD%s", sc->sc_nccbs, sc->sc_nunits,
 	    (sc->sc_nunits == 1 ? "" : "s"));
 	printf("\n");
@@ -678,11 +688,13 @@ ips_attach(struct device *parent, struct device *self, void *aux)
 #endif	/* !SMALL_KERNEL */
 
 	return;
-fail4:
+fail5:
 	ips_ccb_free(sc, sc->sc_ccb, sc->sc_nccbs);
-fail3:
+fail4:
 	if (sc->sc_chip->ic_id == IPS_CHIP_COPPERHEAD)
 		ips_dmamem_free(&sc->sc_sqm);
+fail3:
+	ips_dmamem_free(&sc->sc_infom);
 fail2:
 	ips_dmamem_free(&sc->sc_cmdm);
 fail1:
@@ -694,15 +706,18 @@ ips_scsi_cmd(struct scsi_xfer *xs)
 {
 	struct scsi_link *link = xs->sc_link;
 	struct ips_softc *sc = link->adapter_softc;
+	struct ips_driveinfo *di = &sc->sc_info->drive;
 	struct ips_drive *drive;
 	struct scsi_inquiry_data inq;
 	struct scsi_read_cap_data rcd;
 	struct scsi_sense_data sd;
 	struct scsi_rw *rw;
 	struct scsi_rw_big *rwb;
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
 	int target = link->target;
 	u_int32_t blkno, blkcnt;
-	int cmd, error, flags, s;
+	int code, error, flags, s;
 
 	if (target >= sc->sc_nunits || link->lun != 0) {
 		DPRINTF(IPS_D_INFO, ("%s: invalid scsi command, "
@@ -715,8 +730,7 @@ ips_scsi_cmd(struct scsi_xfer *xs)
 		return (COMPLETE);
 	}
 
-	s = splbio();
-	drive = &sc->sc_di.drive[target];
+	drive = &di->drive[target];
 	xs->error = XS_NOERROR;
 
 	/* Fake SCSI commands */
@@ -742,36 +756,65 @@ ips_scsi_cmd(struct scsi_xfer *xs)
 			    "blkno %u, blkcnt %u\n", sc->sc_dev.dv_xname,
 			    blkno, blkcnt));
 			xs->error = XS_DRIVER_STUFFUP;
+			s = splbio();
 			scsi_done(xs);
+			splx(s);
 			break;
 		}
 
 		if (xs->flags & SCSI_DATA_IN) {
-			cmd = IPS_CMD_READ;
+			code = IPS_CMD_READ;
 			flags = IPS_CCB_READ;
 		} else {
-			cmd = IPS_CMD_WRITE;
+			code = IPS_CMD_WRITE;
 			flags = IPS_CCB_WRITE;
 		}
 		if (xs->flags & SCSI_POLL)
 			flags |= IPS_CCB_POLL;
 
-		if ((error = ips_cmd(sc, cmd, target, blkno, xs->data,
-		    blkcnt * IPS_SECSZ, flags, xs))) {
-			if (error == ENOMEM) {
-				splx(s);
-				return (NO_CCB);
-			} else if (flags & IPS_CCB_POLL) {
-				splx(s);
-				return (TRY_AGAIN_LATER);
-			} else {
+		s = splbio();
+		ccb = ips_ccb_get(sc);
+		splx(s);
+		if (ccb == NULL)
+			return (NO_CCB);
+
+		ccb->c_flags = flags;
+		ccb->c_xfer = xs;
+		ccb->c_done = ips_done_xs;
+
+		cmd = ccb->c_cmdva;
+		cmd->code = code;
+		cmd->drive = target;
+		cmd->lba = htole32(blkno);
+		cmd->seccnt = htole16(blkcnt);
+
+		if (ips_load(sc, ccb, xs)) {
+			s = splbio();
+			ips_ccb_put(sc, ccb);
+			xs->error = XS_DRIVER_STUFFUP;
+			scsi_done(xs);
+			splx(s);
+			return (COMPLETE);
+		}
+
+		if (cmd->sgcnt > 0)
+			cmd->code |= IPS_CMD_SG;
+
+		timeout_set(&xs->stimeout, ips_timeout, ccb);
+		timeout_add_sec(&xs->stimeout, IPS_TIMEOUT);
+
+		if ((error = ips_cmd(sc, ccb))) {
+			if (error == ETIMEDOUT)
+				xs->error = XS_TIMEOUT;
+			else
 				xs->error = XS_DRIVER_STUFFUP;
-				scsi_done(xs);
-				break;
-			}
+
+			s = splbio();
+			scsi_done(xs);
+			splx(s);
+			return (COMPLETE);
 		}
 
-		splx(s);
 		if (flags & IPS_CCB_POLL)
 			return (COMPLETE);
 		else
@@ -813,6 +856,8 @@ ips_scsi_cmd(struct scsi_xfer *xs)
 		    sc->sc_dev.dv_xname, xs->cmd->opcode));
 		xs->error = XS_DRIVER_STUFFUP;
 	}
+
+	s = splbio();
 	scsi_done(xs);
 	splx(s);
 
@@ -849,14 +894,13 @@ ips_ioctl(struct device *dev, u_long cmd, caddr_t addr)
 int
 ips_ioctl_inq(struct ips_softc *sc, struct bioc_inq *bi)
 {
-	struct ips_adapterinfo ai;
-
-	if (ips_getadapterinfo(sc, &ai))
-		return (ENOTTY);
+	struct ips_conf *conf = &sc->sc_info->conf;
+	int i;
 
 	strlcpy(bi->bi_dev, sc->sc_dev.dv_xname, sizeof(bi->bi_dev));
 	bi->bi_novol = sc->sc_nunits;
-	bi->bi_nodisk = ai.drivecnt;
+	for (i = 0, bi->bi_nodisk = 0; i < sc->sc_nunits; i++)
+		bi->bi_nodisk += conf->ld[i].chunkcnt;
 
 	return (0);
 }
@@ -864,19 +908,17 @@ ips_ioctl_inq(struct ips_softc *sc, struct bioc_inq *bi)
 int
 ips_ioctl_vol(struct ips_softc *sc, struct bioc_vol *bv)
 {
-	struct ips_driveinfo di;
-	struct ips_drive *drive;
+	struct ips_driveinfo *di = &sc->sc_info->drive;
+	struct ips_conf *conf = &sc->sc_info->conf;
+	struct ips_ld *ld;
 	int vid = bv->bv_volid;
 	struct device *dev;
 
 	if (vid >= sc->sc_nunits)
 		return (EINVAL);
+	ld = &conf->ld[vid];
 
-	if (ips_getdriveinfo(sc, &di))
-		return (ENOTTY);
-	drive = &di.drive[vid];
-
-	switch (drive->state) {
+	switch (ld->state) {
 	case IPS_DS_ONLINE:
 		bv->bv_status = BIOC_SVONLINE;
 		break;
@@ -890,9 +932,9 @@ ips_ioctl_vol(struct ips_softc *sc, struct bioc_vol *bv)
 		bv->bv_status = BIOC_SVINVALID;
 	}
 
-	bv->bv_size = (u_quad_t)letoh32(drive->seccnt) * IPS_SECSZ;
-	bv->bv_level = drive->raid;
-	bv->bv_nodisk = sc->sc_conf.ld[vid].chunkcnt;
+	bv->bv_size = (u_quad_t)letoh32(ld->size) * IPS_SECSZ;
+	bv->bv_level = di->drive[vid].raid;
+	bv->bv_nodisk = ld->chunkcnt;
 
 	dev = sc->sc_scsibus->sc_link[vid][0]->device_softc;
 	strlcpy(bv->bv_dev, dev->dv_xname, sizeof(bv->bv_dev));
@@ -904,25 +946,23 @@ ips_ioctl_vol(struct ips_softc *sc, struct bioc_vol *bv)
 int
 ips_ioctl_disk(struct ips_softc *sc, struct bioc_disk *bd)
 {
-	int vid = bd->bd_volid, did = bd->bd_diskid;
+	struct ips_conf *conf = &sc->sc_info->conf;
 	struct ips_ld *ld;
 	struct ips_chunk *chunk;
 	struct ips_dev *dev;
+	int vid = bd->bd_volid, did = bd->bd_diskid;
 
 	if (vid >= sc->sc_nunits)
 		return (EINVAL);
-	ld = &sc->sc_conf.ld[vid];
+	ld = &conf->ld[vid];
 
 	if (did >= ld->chunkcnt)
 		return (EINVAL);
 	chunk = &ld->chunk[did];
 
 	if (chunk->channel >= IPS_MAXCHANS || chunk->target >= IPS_MAXTARGETS)
-		return (ENOTTY);
-	dev = &sc->sc_conf.dev[chunk->channel][chunk->target];
-
-	if (ips_getconf(sc, &sc->sc_conf))
-		return (ENOTTY);
+		return (EINVAL);
+	dev = &conf->dev[chunk->channel][chunk->target];
 
 	bd->bd_channel = chunk->channel;
 	bd->bd_target = chunk->target;
@@ -953,10 +993,11 @@ void
 ips_sensors(void *arg)
 {
 	struct ips_softc *sc = arg;
-	struct ips_drive *drive;
+	struct ips_conf *conf = &sc->sc_info->conf;
+	struct ips_ld *ld;
 	int i;
 
-	if (ips_getdriveinfo(sc, &sc->sc_di)) {
+	if (ips_getconf(sc)) {
 		for (i = 0; i < sc->sc_nunits; i++) {
 			sc->sc_sensors[i].value = 0;
 			sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
@@ -965,8 +1006,8 @@ ips_sensors(void *arg)
 	}
 
 	for (i = 0; i < sc->sc_nunits; i++) {
-		drive = &sc->sc_di.drive[i];
-		switch (drive->state) {
+		ld = &conf->ld[i];
+		switch (ld->state) {
 		case IPS_DS_ONLINE:
 			sc->sc_sensors[i].value = SENSOR_DRIVE_ONLINE;
 			sc->sc_sensors[i].status = SENSOR_S_OK;
@@ -988,92 +1029,67 @@ ips_sensors(void *arg)
 #endif
 
 int
-ips_cmd(struct ips_softc *sc, int code, int drive, u_int32_t lba, void *data,
-    size_t size, int flags, struct scsi_xfer *xs)
+ips_load(struct ips_softc *sc, struct ips_ccb *ccb, struct scsi_xfer *xs)
 {
-	struct ips_cmd *cmd;
+	struct ips_cmd *cmd = ccb->c_cmdva;
 	struct ips_sg *sg;
-	struct ips_ccb *ccb;
-	int nsegs, i, s, error = 0;
+	int nsegs, i;
 
-	DPRINTF(IPS_D_XFER, ("%s: cmd code 0x%02x, drive %d, lba %u, "
-	    "size %lu, flags 0x%02x\n", sc->sc_dev.dv_xname, code, drive, lba,
-	    (u_long)size, flags));
+	if (xs->datalen == 0)
+		return (0);
 
-	/* Grab free CCB */
-	if ((ccb = ips_ccb_get(sc)) == NULL) {
-		DPRINTF(IPS_D_ERR, ("%s: no free CCB\n", sc->sc_dev.dv_xname));
-		return (ENOMEM);
-	}
+	/* Map data buffer into DMA segments */
+	if (bus_dmamap_load(sc->sc_dmat, ccb->c_dmam, xs->data, xs->datalen,
+	    NULL, (xs->flags & SCSI_NOSLEEP ? BUS_DMA_NOWAIT : 0)))
+		return (1);
+	bus_dmamap_sync(sc->sc_dmat, ccb->c_dmam, 0,ccb->c_dmam->dm_mapsize,
+	    xs->flags & SCSI_DATA_IN ? BUS_DMASYNC_PREREAD :
+	    BUS_DMASYNC_PREWRITE);
 
-	ccb->c_flags = flags;
-	ccb->c_xfer = xs;
+	if ((nsegs = ccb->c_dmam->dm_nsegs) > IPS_MAXSGS)
+		return (1);
 
-	/* Fill in command frame */
-	cmd = ccb->c_cmdva;
-	bzero(cmd, sizeof(*cmd));
-	cmd->code = code;
-	cmd->id = ccb->c_id;
-	cmd->drive = drive;
-	cmd->lba = htole32(lba);
-	cmd->seccnt = htole16(howmany(size, IPS_SECSZ));
-
-	if (size > 0) {
-		/* Map data buffer into DMA segments */
-		if (bus_dmamap_load(sc->sc_dmat, ccb->c_dmam, data, size,
-		    NULL, BUS_DMA_NOWAIT)) {
-			printf("%s: can't load dma map\n",
-			    sc->sc_dev.dv_xname);
-			return (1);	/* XXX: return code */
-		}
-		bus_dmamap_sync(sc->sc_dmat, ccb->c_dmam, 0,
-		    ccb->c_dmam->dm_mapsize,
-		    flags & IPS_CCB_READ ? BUS_DMASYNC_PREREAD :
-		    BUS_DMASYNC_PREWRITE);
-
-		if ((nsegs = ccb->c_dmam->dm_nsegs) > IPS_MAXSGS) {
-			printf("%s: too many dma segs\n",
-			    sc->sc_dev.dv_xname);
-			return (1);	/* XXX: return code */
-		}
+	if (nsegs > 1) { 
+		cmd->sgcnt = nsegs;
+		cmd->sgaddr = htole32(ccb->c_cmdpa + IPS_CMDSZ);
 
-		if (nsegs > 1) {
-			cmd->code |= IPS_CMD_SG;
-			cmd->sgcnt = nsegs;
-			cmd->sgaddr = htole32(ccb->c_cmdpa + IPS_CMDSZ);
-
-			/* Fill in scatter-gather array */
-			sg = (void *)(cmd + 1);
-			for (i = 0; i < nsegs; i++) {
-				sg[i].addr =
-				    htole32(ccb->c_dmam->dm_segs[i].ds_addr);
-				sg[i].size =
-				    htole32(ccb->c_dmam->dm_segs[i].ds_len);
-			}
-		} else {
-			cmd->sgcnt = 0;
-			cmd->sgaddr = htole32(ccb->c_dmam->dm_segs[0].ds_addr);
+		/* Fill in scatter-gather array */
+		sg = (void *)(cmd + 1);
+		for (i = 0; i < nsegs; i++) {
+			sg[i].addr = htole32(ccb->c_dmam->dm_segs[i].ds_addr);
+			sg[i].size = htole32(ccb->c_dmam->dm_segs[i].ds_len);
 		}
+	} else {
+		cmd->sgcnt = 0;
+		cmd->sgaddr = htole32(ccb->c_dmam->dm_segs[0].ds_addr);
 	}
 
+	return (0);
+}
+
+int
+ips_cmd(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	struct ips_cmd *cmd = ccb->c_cmdva;
+	int s, error = 0;
+
+	DPRINTF(IPS_D_XFER, ("%s: cmd id %d, flags 0x%02x, code 0x%02x, "
+	    "drive %d, sgcnt %d, lba %d, sgaddr 0x%08x, seccnt %d\n",
+	    sc->sc_dev.dv_xname, ccb->c_id, ccb->c_flags, cmd->code,
+	    cmd->drive, cmd->sgcnt, cmd->lba, cmd->sgaddr, cmd->seccnt));
+
 	/* Pass command to hardware */
-	DPRINTF(IPS_D_XFER, ("%s: run command 0x%02x\n", sc->sc_dev.dv_xname,
-	    ccb->c_id));
+	cmd->id = ccb->c_id;
 	ccb->c_flags |= IPS_CCB_RUN;
-	TAILQ_INSERT_TAIL(&sc->sc_ccbq_run, ccb, c_link);
+	s = splbio();
 	ips_exec(sc, ccb);
+	splx(s);
 
-	if (flags & IPS_CCB_POLL) {
+	if (ccb->c_flags & IPS_CCB_POLL) {
 		/* Wait for command to complete */
 		s = splbio();
 		error = ips_poll(sc, ccb);
 		splx(s);
-	} else {
-		/* Set watchdog timer */
-		if (xs != NULL) {
-			timeout_set(&xs->stimeout, ips_timeout, ccb);
-			timeout_add_sec(&xs->stimeout, IPS_TIMEOUT);
-		}
 	}
 
 	return (error);
@@ -1100,19 +1116,19 @@ ips_poll(struct ips_softc *sc, struct ips_ccb *c)
 		}
 		if (timeout < 0) {
 			printf("%s: poll timeout\n", sc->sc_dev.dv_xname);
-			return (EBUSY);
+			return (ETIMEDOUT);
 		}
 		ccb = &sc->sc_ccb[id];
 		ccb->c_stat = IPS_REG_STAT_GSC(status);
 		ccb->c_estat = IPS_REG_STAT_EXT(status);
-		ips_done(sc, ccb);
+		ccb->c_done(sc, ccb);
 	}
 
 	return (0);
 }
 
 void
-ips_done(struct ips_softc *sc, struct ips_ccb *ccb)
+ips_done_xs(struct ips_softc *sc, struct ips_ccb *ccb)
 {
 	struct scsi_xfer *xs = ccb->c_xfer;
 	int flags = ccb->c_flags;
@@ -1121,15 +1137,10 @@ ips_done(struct ips_softc *sc, struct ips_ccb *ccb)
 	if ((flags & IPS_CCB_RUN) == 0) {
 		printf("%s: cmd 0x%02x not run\n", sc->sc_dev.dv_xname,
 		    ccb->c_id);
-		if (xs != NULL) {
-			xs->error = XS_DRIVER_STUFFUP;
-			scsi_done(xs);
-		}
 		return;
 	}
 
-	if (xs != NULL)
-		timeout_del(&xs->stimeout);
+	timeout_del(&xs->stimeout);
 
 	if (flags & (IPS_CCB_READ | IPS_CCB_WRITE)) {
 		bus_dmamap_sync(sc->sc_dmat, ccb->c_dmam, 0,
@@ -1139,10 +1150,7 @@ ips_done(struct ips_softc *sc, struct ips_ccb *ccb)
 	}
 
 	if (ccb->c_stat) {
-		if (xs != NULL)
-			sc_print_addr(xs->sc_link);
-		else
-			printf("%s: ", sc->sc_dev.dv_xname);
+		sc_print_addr(xs->sc_link);
 		if (ccb->c_stat == 1) {
 			printf("recovered error\n");
 		} else {
@@ -1152,17 +1160,45 @@ ips_done(struct ips_softc *sc, struct ips_ccb *ccb)
 	}
 
 	/* Release CCB */
-	TAILQ_REMOVE(&sc->sc_ccbq_run, ccb, c_link);
 	ips_ccb_put(sc, ccb);
 
-	if (xs != NULL) {
-		if (error)
-			xs->error = XS_DRIVER_STUFFUP;
-		else
-			xs->resid = 0;
-		xs->flags |= ITSDONE;
-		scsi_done(xs);
+	if (error)
+		xs->error = XS_DRIVER_STUFFUP;
+	else
+		xs->resid = 0;
+	xs->flags |= ITSDONE;
+	scsi_done(xs);
+}
+
+void
+ips_done_mgmt(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	int flags = ccb->c_flags;
+	int error = 0;
+
+	if ((flags & IPS_CCB_RUN) == 0) {
+		printf("%s: cmd 0x%02x not run\n", sc->sc_dev.dv_xname,
+		    ccb->c_id);
+		return;
 	}
+
+	if (flags & (IPS_CCB_READ | IPS_CCB_WRITE))
+		bus_dmamap_sync(sc->sc_dmat, sc->sc_infom.dm_map, 0,
+		    sc->sc_infom.dm_map->dm_mapsize, flags & IPS_CCB_READ ?
+		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+
+	if (ccb->c_stat) {
+		printf("%s: ", sc->sc_dev.dv_xname);
+		if (ccb->c_stat == 1) {
+			printf("recovered error\n");
+		} else {
+			printf("error\n");
+			error = 1;
+		}
+	}
+
+	/* Release CCB */
+	ips_ccb_put(sc, ccb);
 }
 
 int
@@ -1190,7 +1226,7 @@ ips_intr(void *arg)
 		ccb = &sc->sc_ccb[id];
 		ccb->c_stat = IPS_REG_STAT_GSC(status);
 		ccb->c_estat = IPS_REG_STAT_EXT(status);
-		ips_done(sc, ccb);
+		ccb->c_done(sc, ccb);
 	}
 
 	return (1);
@@ -1213,7 +1249,6 @@ ips_timeout(void *arg)
 	DPRINTF(IPS_D_ERR, (", command 0x%02x", ccb->c_id));
 	printf("\n");
 
-	TAILQ_REMOVE(&sc->sc_ccbq_run, ccb, c_link);
 	ips_ccb_put(sc, ccb);
 
 	xs->error = XS_TIMEOUT;
@@ -1225,37 +1260,122 @@ ips_timeout(void *arg)
 }
 
 int
-ips_getadapterinfo(struct ips_softc *sc, struct ips_adapterinfo *ai)
+ips_getadapterinfo(struct ips_softc *sc)
 {
-	return (ips_cmd(sc, IPS_CMD_GETADAPTERINFO, 0, 0, ai, sizeof(*ai),
-	    IPS_CCB_READ | IPS_CCB_POLL, NULL));
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+	int s;
+
+	s = splbio();
+	ccb = ips_ccb_get(sc);
+	splx(s);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = IPS_CCB_READ | IPS_CCB_POLL;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdva;
+	cmd->code = IPS_CMD_GETADAPTERINFO;
+	cmd->sgaddr = htole32(sc->sc_infom.dm_paddr + offsetof(struct ips_info,
+	    adapter));
+
+	return (ips_cmd(sc, ccb));
 }
 
 int
-ips_getconf(struct ips_softc *sc, struct ips_conf *conf)
+ips_getdriveinfo(struct ips_softc *sc)
 {
-	return (ips_cmd(sc, IPS_CMD_READCONF, 0, 0, conf, sizeof(*conf),
-	    IPS_CCB_READ | IPS_CCB_POLL, NULL));
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+	int s;
+
+	s = splbio();
+	ccb = ips_ccb_get(sc);
+	splx(s);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = IPS_CCB_READ | IPS_CCB_POLL;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdva;
+	cmd->code = IPS_CMD_GETDRIVEINFO;
+	cmd->sgaddr = htole32(sc->sc_infom.dm_paddr + offsetof(struct ips_info,
+	    drive));
+
+	return (ips_cmd(sc, ccb));
 }
 
 int
-ips_getdriveinfo(struct ips_softc *sc, struct ips_driveinfo *di)
+ips_getconf(struct ips_softc *sc)
 {
-	return (ips_cmd(sc, IPS_CMD_GETDRIVEINFO, 0, 0, di, sizeof(*di),
-	    IPS_CCB_READ | IPS_CCB_POLL, NULL));
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+	int s;
+
+	s = splbio();
+	ccb = ips_ccb_get(sc);
+	splx(s);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = IPS_CCB_READ | IPS_CCB_POLL;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdva;
+	cmd->code = IPS_CMD_READCONF;
+	cmd->sgaddr = htole32(sc->sc_infom.dm_paddr + offsetof(struct ips_info,
+	    conf));
+
+	return (ips_cmd(sc, ccb));
 }
 
 int
-ips_flush(struct ips_softc *sc)
+ips_getpg5(struct ips_softc *sc)
 {
-	return (ips_cmd(sc, IPS_CMD_FLUSH, 0, 0, NULL, 0, IPS_CCB_POLL, NULL));
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+	int s;
+
+	s = splbio();
+	ccb = ips_ccb_get(sc);
+	splx(s);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = IPS_CCB_READ | IPS_CCB_POLL;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdva;
+	cmd->code = IPS_CMD_RWNVRAM;
+	cmd->drive = 5;
+	cmd->sgaddr = htole32(sc->sc_infom.dm_paddr + offsetof(struct ips_info,
+	    pg5));
+
+	return (ips_cmd(sc, ccb));
 }
 
 int
-ips_readnvram(struct ips_softc *sc, void *buf, int page)
+ips_flush(struct ips_softc *sc)
 {
-	return (ips_cmd(sc, IPS_CMD_RWNVRAM, page, 0, buf, IPS_NVRAMPGSZ,
-	    IPS_CCB_READ | IPS_CCB_POLL, NULL));
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+	int s;
+
+	s = splbio();
+	ccb = ips_ccb_get(sc);
+	splx(s);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = IPS_CCB_POLL;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdva;
+	cmd->code = IPS_CMD_FLUSH;
+
+	return (ips_cmd(sc, ccb));
 }
 
 void
@@ -1429,8 +1549,12 @@ ips_ccb_get(struct ips_softc *sc)
 {
 	struct ips_ccb *ccb;
 
-	if ((ccb = TAILQ_FIRST(&sc->sc_ccbq_free)) != NULL)
+	if ((ccb = TAILQ_FIRST(&sc->sc_ccbq_free)) != NULL) {
 		TAILQ_REMOVE(&sc->sc_ccbq_free, ccb, c_link);
+		ccb->c_flags = 0;
+		ccb->c_xfer = NULL;
+		bzero(ccb->c_cmdva, sizeof(struct ips_cmd));
+	}
 
 	return (ccb);
 }
-- 
cgit v1.2.3