diff -u /dev/null sys/dev/ata/atapi-cam.c
--- /dev/null	Sat Apr  6 20:39:00 2002
+++ sys/dev/ata/atapi-cam.c	Sat Apr  6 20:38:33 2002
@@ -0,0 +1,652 @@
+/*-
+ * Copyright (c) 2001, 2002 Thomas Quinot <thomas@cuivre.fr.eu.org>
+ * 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,
+ *    without modification, immediately at the beginning of the file.
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * 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.
+ *
+ * $FreeBSD:$
+ */
+
+/*
+ * A SIM (SCSI Interface Module) that provides access to ATAPI devices
+ * through CAM.
+ * 
+ * This module owes much to Sergey Babkin's document 'Writing a CAM SCSI
+ * controller (A.K.A. Host Bus Adapter) driver',
+ * http://www.daemonnews.org/200006/cam-driver.html
+ * 
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <dev/ata/ata-all.h>
+#include <dev/ata/atapi-all.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_all.h>
+
+/* Hardware command descriptor block */
+struct atapi_hcb {
+    struct atapi_xpt_softc *softc;
+    int             unit;
+    int             bus;
+    int             target;
+    int             lun;
+    union ccb      *ccb;
+    u_int8_t        cmd[CAM_MAX_CDBLEN];
+    int             flags;
+#define DOING_AUTOSENSE 1
+    char           *dxfer_alloc;
+
+    TAILQ_ENTRY(atapi_hcb) chain;
+    /* Link within list of pending hcbs on the same channel */
+};
+
+/* Private data associated with an ATAPI bus */
+struct atapi_xpt_softc {
+    struct ata_channel *ata_ch;
+    struct cam_path    *wpath;
+    struct cam_sim     *sim;
+    int                 flags;
+#define BUS_REGISTERED		0x01
+#define RESOURCE_SHORTAGE	0x02
+    TAILQ_HEAD(,atapi_hcb) pending_hcbs;
+    LIST_ENTRY(atapi_xpt_softc) chain;
+    /* Link within the global list of all ATAPI/CAM SIMs. */
+};
+
+static LIST_HEAD(,atapi_xpt_softc) all_buses
+    = LIST_HEAD_INITIALIZER(all_buses);
+
+/* CAM XPT methods */
+static void     atapi_action(struct cam_sim *sim, union ccb *ccb);
+static void     atapi_action1(struct cam_sim *sim, union ccb *ccb);
+static void     atapi_poll(struct cam_sim *sim);
+static void     atapi_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg);
+static void     atapi_async1(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg);
+static int      atapi_cb(struct atapi_request *);
+
+/* Internal functions */
+static void     setup_dev(struct atapi_xpt_softc *, struct ata_device *);
+static void     free_hcb_and_ccb_done(struct atapi_hcb *hcb, u_int32_t status);
+static struct atapi_hcb *allocate_hcb(struct atapi_xpt_softc *softc, int unit, int bus, union ccb *ccb);
+static void     free_hcb(struct atapi_hcb *hcb);
+static void	free_softc(struct atapi_xpt_softc *scp);
+static struct ata_device *get_ata_device(struct atapi_xpt_softc *scp, int id);
+
+static MALLOC_DEFINE(M_ATACAM, "ATA CAM transport", "ATA driver CAM-XPT layer");
+
+void 
+atapi_cam_attach_bus(struct ata_channel *ata_ch)
+{
+    struct atapi_xpt_softc *scp = NULL;
+    struct cam_devq *devq = NULL;
+    struct cam_sim *sim = NULL;
+    struct cam_path *path = NULL;
+
+    int             unit;
+    struct ccb_setasync csa;
+
+    /* Allocate our private control structure. */
+    if ((scp = malloc(sizeof(struct atapi_xpt_softc), M_ATACAM, M_NOWAIT | M_ZERO)) == NULL) {
+        goto error;
+    }
+    scp->ata_ch = ata_ch;
+    TAILQ_INIT(&scp->pending_hcbs);
+    LIST_INSERT_HEAD(&all_buses, scp, chain);
+    unit = device_get_unit(ata_ch->dev);
+
+    /* Alloc one devq (either for the whole driver or for each bus). */
+    if ((devq = cam_simq_alloc(16)) == NULL) {
+        goto error;
+    }
+
+    /* Alloc a SIM descriptor for each bus. */
+    if ((sim = cam_sim_alloc(atapi_action, atapi_poll, "atapi",
+                 (void *)scp, unit, 1, 1, devq)) == NULL) {
+	cam_simq_free(devq);
+        goto error;
+    }
+    scp->sim = sim;
+
+    /* Register the SIM. */
+    if (xpt_bus_register(sim, 0) != CAM_SUCCESS) {
+        goto error;
+    }
+    scp->flags |= BUS_REGISTERED;
+
+    if (xpt_create_path(&path, /* periph */ NULL,
+                cam_sim_path(sim), CAM_TARGET_WILDCARD,
+                CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+        goto error;
+    }
+    scp->wpath = path;
+
+    xpt_print_path(path);
+    printf("Registered SIM for ata%d\n", unit);
+
+    /* Register callback for AC_LOST_DEVICE event. */
+    xpt_setup_ccb(&csa.ccb_h, path, /* priority */ 5);
+    csa.ccb_h.func_code = XPT_SASYNC_CB;
+    csa.event_enable = AC_LOST_DEVICE;
+    csa.callback = atapi_async;
+    csa.callback_arg = sim;
+    xpt_action((union ccb *) & csa);
+
+    if (ata_ch->devices & ATA_ATAPI_MASTER)
+        setup_dev(scp, &ata_ch->device[MASTER]);
+    if (ata_ch->devices & ATA_ATAPI_SLAVE)
+        setup_dev(scp, &ata_ch->device[SLAVE]);
+
+    return;
+
+error:
+    free_softc(scp);
+}
+
+void 
+atapi_cam_detach_bus(struct ata_channel *ata_ch) {
+    struct atapi_xpt_softc *scp;
+    LIST_FOREACH(scp, &all_buses, chain) {
+	if (scp->ata_ch == ata_ch)
+	    break;
+    }
+    if (scp != NULL)
+	free_softc(scp);
+}
+
+static void 
+setup_dev(struct atapi_xpt_softc *scp, struct ata_device *atp)
+{
+    if (atp->driver == NULL) {
+        ata_set_name(atp, "atapicam",
+                 2 * device_get_unit(atp->channel->dev)
+                 + (atp->unit == ATA_MASTER) ? 0 : 1);
+	atp->driver = (void *) scp;
+    }
+}
+
+static void 
+atapi_action(struct cam_sim *sim, union ccb *ccb)
+{
+    int             s = splcam();
+    atapi_action1(sim, ccb);
+    splx(s);
+}
+
+static void 
+atapi_action1(struct cam_sim *sim, union ccb *ccb)
+{
+    struct atapi_xpt_softc *softc = (struct atapi_xpt_softc *) cam_sim_softc(sim);
+    struct ccb_hdr *ccb_h = &ccb->ccb_h;
+    int             unit = cam_sim_unit(sim);
+    int             bus = cam_sim_bus(sim);
+    int             len;
+    char           *buf;
+    struct atapi_hcb *hcb = NULL;
+
+    switch (ccb_h->func_code) {
+    case XPT_PATH_INQ:
+        {
+            struct ccb_pathinq *cpi = &ccb->cpi;
+
+            cpi->version_num = 1;
+            cpi->hba_inquiry = 0;
+            cpi->hba_misc = 0;
+            cpi->hba_eng_cnt = 0;
+            bzero(cpi->vuhba_flags, sizeof(cpi->vuhba_flags));
+            cpi->max_target = 2;
+            cpi->max_lun = 7;
+            cpi->async_flags = 0;
+            cpi->hpath_id = 0;
+            cpi->initiator_id = 7;
+            strncpy(cpi->sim_vid, "ATAPI-SIM", sizeof cpi->sim_vid);
+            strncpy(cpi->hba_vid, "ATAPI", sizeof cpi->hba_vid);
+            strncpy(cpi->dev_name, cam_sim_name(sim), sizeof cpi->dev_name);
+            cpi->unit_number = cam_sim_unit(sim);
+            cpi->bus_id = cam_sim_bus(sim);
+            cpi->base_transfer_speed = 3300;
+
+            ccb->ccb_h.status = CAM_REQ_CMP;
+            xpt_done(ccb);
+            return;
+        }
+    case XPT_RESET_DEV:
+        /* Should reset the device. For now, do nothing. */
+#ifdef CAMDEBUG
+        xpt_print_path(ccb->ccb_h.path);
+        printf("dev reset");
+#endif
+        ccb->ccb_h.status = CAM_REQ_CMP;
+        xpt_done(ccb);
+        return;
+    case XPT_RESET_BUS:
+        /* Should reset the ATA bus. For now, do nothing. */
+#ifdef CAMDEBUG
+        xpt_print_path(ccb->ccb_h.path);
+        printf("bus reset");
+#endif
+        ccb->ccb_h.status = CAM_REQ_CMP;
+        xpt_done(ccb);
+        return;
+    case XPT_SET_TRAN_SETTINGS:
+        /* Ignore these. We're not doing SCSI here. */
+        ccb->ccb_h.status = CAM_REQ_CMP;
+        xpt_done(ccb);
+        return;
+    case XPT_GET_TRAN_SETTINGS:
+        {
+            struct ccb_trans_settings *cts = &ccb->cts;
+            cts->flags |=
+                (0
+                 | CCB_TRANS_SYNC_RATE_VALID
+                 | CCB_TRANS_SYNC_OFFSET_VALID
+                 | CCB_TRANS_BUS_WIDTH_VALID
+                 | CCB_TRANS_DISC_VALID
+                 | CCB_TRANS_TQ_VALID);
+            cts->flags &= ~(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB);
+            cts->sync_period = 0;
+            cts->sync_offset = 0;
+            cts->bus_width = 8;
+
+            ccb->ccb_h.status = CAM_REQ_CMP;
+            xpt_done(ccb);
+            return;
+        }
+    case XPT_CALC_GEOMETRY:
+    /* Stolen from sym_hipd.c */
+	{
+	    struct ccb_calc_geometry *ccg;
+	    unsigned int size_mb;
+	    unsigned int secs_per_cylinder;
+	    int extended;
+
+	    /*
+	     *  Silly DOS geometry.
+	     */
+	    ccg = &ccb->ccg;
+	    size_mb = ccg->volume_size
+		/ ((1024L * 1024L) / ccg->block_size);
+	    extended = 1;
+
+	    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);
+	    return;
+	}
+    case XPT_SCSI_IO:
+        {
+            struct ccb_scsiio *csio = &ccb->csio;
+            int tid = ccb_h->target_id, lid = ccb_h->target_lun;
+            struct ata_device *dev = get_ata_device(softc, tid);
+
+#ifdef CAMDEBUG
+            char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1];
+
+            printf("XPT_SCSI_IO (b%d u%d t%d l%d)\n", bus, unit, tid, lid);
+#endif
+            /* Check that this request was not aborted already. */
+            if ((ccb_h->status & CAM_STATUS_MASK) != CAM_REQ_INPROG) {
+#ifdef CAMDEBUG
+                printf("Already in progress.\n");
+#endif
+                xpt_done(ccb);
+                return;
+            }
+            if (dev == NULL) {
+#ifdef CAMDEBUG
+                printf("Invalid target %d.\n", tid);
+#endif
+                ccb_h->status = CAM_TID_INVALID;
+                xpt_done(ccb);
+                return;
+            }
+#if 0
+            /* XXX how to determine that??? */
+            if (lid > OUR_MAX_SUPPORTED_LUN) {
+                ccb_h->status = CAM_LUN_INVALID;
+                xpt_done(ccb);
+                return;
+            }
+#else
+            /* -> while we dunno, be conservative. */
+            if (lid > 0) {
+#ifdef CAMDEBUG
+                printf("Invalid LUN %d.\n", lid);
+#endif
+                ccb_h->status = CAM_LUN_INVALID;
+                xpt_done(ccb);
+                return;
+            }
+#endif
+            if ((ccb_h->flags & CAM_SCATTER_VALID)) {
+                /* Scatter-gather not supported. */
+                xpt_print_path(ccb_h->path);
+                printf("ATAPI-CAM does not support scatter-gather yet!\n");
+                break;
+            }
+            if ((hcb = allocate_hcb(softc, unit, bus, ccb)) == NULL) {
+                goto action_oom;
+            }
+            ccb_h->status |= CAM_SIM_QUEUED;
+
+            bcopy((ccb_h->flags & CAM_CDB_POINTER) ?
+                  csio->cdb_io.cdb_ptr :
+                  csio->cdb_io.cdb_bytes,
+                  hcb->cmd,
+                  csio->cdb_len);
+#ifdef CAMDEBUG
+            printf("hcb@%p: %s\n",
+                   hcb,
+            scsi_cdb_string(hcb->cmd, cdb_str, sizeof(cdb_str)));
+#endif
+
+            len = csio->dxfer_len;
+            buf = csio->data_ptr;
+
+            /* Some SCSI commands require special processing.    */
+
+            switch (hcb->cmd[0]) {
+	    case INQUIRY:
+		/*
+		 * Many ATAPI devices seem to report more than
+		 * SHORT_INQUIRY_LENGTH bytes of available INQUIRY
+		 * information, but respond with some incorrect condition
+		 * when actually asked for it, so we are going to pretend
+		 * that only SHORT_INQUIRY_LENGTH are expected, anyway.
+		 */
+		{
+		    struct scsi_inquiry *inq = (struct scsi_inquiry *) &hcb->cmd[0];
+		    if (inq->byte2 == 0 && inq->page_code == 0
+			&& inq->length > SHORT_INQUIRY_LENGTH) {
+			bzero(buf, len);
+			len = inq->length = SHORT_INQUIRY_LENGTH;
+		    }
+		    break;
+		}
+            case MODE_SELECT_6:
+                /* FALLTHROUGH */
+            case MODE_SENSE_6:
+                /*
+                 * Not supported by ATAPI/MMC devices (per
+                 * SCSI MMC spec): translate to _10 equivalent.
+                 * (actually we should do this only if we
+                 * have tried MODE_foo_6 and received
+                 *  ILLEGAL_REQUEST/INVALID COMMAND OPERATION CODE)
+                 */
+
+                /*
+                 * Alternative fix: behave like a honest CAM
+                 * transport, do not muck with CDB contents, and change
+                 * scsi_cd to * always use MODE_SENSE_10 in cdgetmode(),
+                 * or let * scsi_cd know that this specific unit is an
+                 * ATAPI/MMC one, and in /that case/ use MODE_SENSE_10.
+                 */
+
+#ifdef CAMDEBUG
+                xpt_print_path(ccb_h->path);
+                printf("Translating %s into _10 equivalent.\n",
+                       (hcb->cmd[0] == MODE_SELECT_6) ? "MODE_SELECT_6" : "MODE_SENSE_6");
+#endif
+                hcb->cmd[0] |= 0x40;
+
+                hcb->cmd[6] = 0;
+                hcb->cmd[7] = 0;
+                hcb->cmd[8] = hcb->cmd[4];
+                hcb->cmd[9] = hcb->cmd[5];
+                hcb->cmd[4] = 0;
+                hcb->cmd[5] = 0;
+                break;
+
+            case READ_6:
+                /* FALLTHROUGH */
+            case WRITE_6:
+#ifdef CAMDEBUG
+                xpt_print_path(ccb_h->path);
+                printf("Translating %s into _10 equivalent.\n",
+                       (hcb->cmd[0] == READ_6) ? "READ_6" : "WRITE_6");
+#endif
+		hcb->cmd[0] |= 0x20;
+
+                hcb->cmd[9] = hcb->cmd[5];
+                hcb->cmd[8] = hcb->cmd[4];
+                hcb->cmd[7] = 0;
+                hcb->cmd[6] = 0;
+                hcb->cmd[5] = hcb->cmd[3];
+                hcb->cmd[4] = hcb->cmd[2];
+                hcb->cmd[3] = hcb->cmd[1] & 0x1f;
+                hcb->cmd[2] = 0;
+                hcb->cmd[1] = 0;
+		break;
+            }
+
+            if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN && (len & 1)) {
+                /*
+                 * ATA always transfers an even number of
+                 * bytes.
+                 */
+                if ((buf = hcb->dxfer_alloc = malloc
+                     (++len, M_ATACAM, M_NOWAIT | M_ZERO)) == NULL)
+                    goto action_oom;
+            }
+	    TAILQ_INSERT_TAIL(&softc->pending_hcbs, hcb, chain);
+            if (atapi_queue_cmd
+                (dev, hcb->cmd, buf, len,
+                 ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) ?
+		 ATPR_F_READ : 0,
+                 ccb_h->timeout, atapi_cb, (void *)hcb) == 0)
+		return;
+	    break; /* Fall trhough to action_oom */
+        }
+    default:
+        printf("atapi-cam: unsupported function code 0x%02x\n", ccb_h->func_code);
+	ccb_h->status = CAM_REQ_INVALID;
+	xpt_done(ccb);
+	return;
+    }
+
+action_oom:
+    if (hcb != NULL)
+        free_hcb(hcb);
+    xpt_print_path(ccb_h->path);
+    printf("Out of memory: freezing queue.");
+    softc->flags |= RESOURCE_SHORTAGE;
+    xpt_freeze_simq(sim, /* count */ 1);
+    ccb_h->status = CAM_REQUEUE_REQ;
+    xpt_done(ccb);
+}
+
+static void 
+atapi_poll(struct cam_sim *sim)
+{
+    /*
+     * Do nothing - we do not actually service any interrupts.
+     */
+    printf("atapi_poll called!\n");
+};
+
+int 
+atapi_cb(struct atapi_request *req)
+{
+    int             s = splcam();
+    struct atapi_hcb *hcb = (struct atapi_hcb *) req->driver;
+    int             hcb_status = req->result;
+    struct ccb_scsiio *csio = &hcb->ccb->csio;
+
+#ifdef CAMDEBUG
+    printf("atapi_cb: hcb@%p status = %02x: (sk = %02x%s%s%s)\n",
+           hcb, hcb_status, hcb_status >> 4,
+           (hcb_status & 4) ? " ABRT" : "",
+           (hcb_status & 2) ? " EOM" : "",
+           (hcb_status & 1) ? " ILI" : "");
+    printf("  %s: cmd %02x - sk=%02x asc=%02x ascq=%02x\n",
+           req->device->name, req->ccb[0], req->sense.sense_key,
+           req->sense.asc, req->sense.ascq);
+#endif
+    if (hcb_status != 0) {
+
+        hcb->ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+
+        if ((hcb->ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) {
+            hcb->ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
+            bcopy((void *)&req->sense, (void *)&csio->sense_data,
+                  sizeof(struct atapi_reqsense));
+        }
+        free_hcb_and_ccb_done(hcb, CAM_SCSI_STATUS_ERROR);
+    } else {
+        if (((hcb->ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
+            && hcb->dxfer_alloc != NULL)
+            bcopy(hcb->dxfer_alloc, csio->data_ptr, csio->dxfer_len);
+        hcb->ccb->csio.scsi_status = SCSI_STATUS_OK;
+        free_hcb_and_ccb_done(hcb, CAM_REQ_CMP);
+    }
+    splx(s);
+    return 0;
+};
+
+static void
+free_hcb_and_ccb_done(struct atapi_hcb *hcb, u_int32_t status)
+{
+    struct atapi_xpt_softc *softc = hcb->softc;
+    union ccb *ccb = hcb->ccb;
+
+    if (hcb != NULL) {
+#if 0
+        untimeout(xxx_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch);
+#endif
+        /* we're about to free a hcb, so the shortage has ended */
+        if (softc->flags & RESOURCE_SHORTAGE) {
+            softc->flags &= ~RESOURCE_SHORTAGE;
+            status |= CAM_RELEASE_SIMQ;
+        }
+        free_hcb(hcb);    /* also removes hcb from any internal lists */
+    }
+    ccb->ccb_h.status = status | (ccb->ccb_h.status & ~(CAM_STATUS_MASK | CAM_SIM_QUEUED));
+    xpt_done(ccb);
+}
+
+static void 
+atapi_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
+{
+    int             s = splcam();
+    atapi_async1(callback_arg, code, path, arg);
+    splx(s);
+}
+
+static void 
+atapi_async1(void *callback_arg, u_int32_t code, struct cam_path* path, void *arg)
+{
+    struct atapi_xpt_softc *softc;
+    struct cam_sim *sim;
+    int             targ;
+
+    sim = (struct cam_sim *) callback_arg;
+    softc = (struct atapi_xpt_softc *) cam_sim_softc(sim);
+    switch (code) {
+    case AC_LOST_DEVICE:
+        targ = xpt_path_target_id(path);
+        printf("Lost target %d ???\n", targ);
+        break;
+    default:
+        break;
+    }
+}
+
+static struct atapi_hcb *
+allocate_hcb(struct atapi_xpt_softc *softc, int unit, int bus, union ccb *ccb)
+{
+    struct atapi_hcb *hcb = (struct atapi_hcb *)
+    malloc(sizeof(struct atapi_hcb), M_ATACAM, M_NOWAIT | M_ZERO);
+
+    if (hcb != NULL) {
+        hcb->softc = softc;
+        hcb->unit = unit;
+        hcb->bus = bus;
+	hcb->ccb = ccb;
+    }
+    return hcb;
+}
+
+static void 
+free_hcb(struct atapi_hcb *hcb)
+{
+    TAILQ_REMOVE(&hcb->softc->pending_hcbs, hcb, chain);
+    if (hcb->dxfer_alloc != NULL)
+        free(hcb->dxfer_alloc, M_ATACAM);
+    free(hcb, M_ATACAM);
+}
+
+static void
+free_softc(struct atapi_xpt_softc *scp) {
+    struct atapi_hcb *hcb;
+
+    if (scp != NULL) {
+	TAILQ_FOREACH(hcb, &scp->pending_hcbs, chain) {
+	    free_hcb_and_ccb_done(hcb, CAM_UNREC_HBA_ERROR);
+	}
+        if (scp->wpath != NULL)
+	    xpt_free_path(scp->wpath);
+	if ((scp->flags & BUS_REGISTERED) != 0)
+	    xpt_bus_deregister(cam_sim_path(scp->sim));
+	if (scp->sim != NULL)
+	    cam_sim_free(scp->sim, /*free_devq*/TRUE);
+	LIST_REMOVE(scp, chain);
+	free(scp, M_ATACAM);
+    }
+}
+
+static struct ata_device *
+get_ata_device(struct atapi_xpt_softc *scp, int id)
+{
+    int             role = ATA_ATAPI_MASTER;
+
+    switch (id) {
+    case 1:
+        role = ATA_ATAPI_SLAVE;
+        /* Fallthrough */
+    case 0:
+        if (scp->ata_ch->devices & role)
+            return &scp->ata_ch->device[id];
+        /* Fallthrough */
+    default:
+        return NULL;
+    }
+}
diff -u sys/dev/ata/ata-all.c.dist sys/dev/ata/ata-all.c
--- sys/dev/ata/ata-all.c.dist	Thu Apr  4 00:52:49 2002
+++ sys/dev/ata/ata-all.c	Fri Apr  5 13:46:55 2002
@@ -212,6 +212,10 @@
 	    atapi_attach(&ch->device[MASTER]);
 	if (ch->devices & ATA_ATAPI_SLAVE)
 	    atapi_attach(&ch->device[SLAVE]);
+#ifdef ATAPICAM
+	if (ch->devices & (ATA_ATAPI_MASTER | ATA_ATAPI_SLAVE))
+	    atapi_cam_attach_bus (ch);
+#endif
 #endif
 	splx(s);
     }
@@ -242,6 +246,10 @@
 	atapi_detach(&ch->device[MASTER]);
     if (ch->devices & ATA_ATAPI_SLAVE && ch->device[SLAVE].driver)
 	atapi_detach(&ch->device[SLAVE]);
+#ifdef ATAPICAM
+    if (ch->devices & (ATA_ATAPI_SLAVE|ATA_ATAPI_MASTER))
+	atapi_cam_detach_bus(ch);
+#endif
 #endif
     splx(s);
 
@@ -572,6 +580,10 @@
 	    atapi_attach(&ch->device[MASTER]);
 	if (ch->devices & ATA_ATAPI_SLAVE)
 	    atapi_attach(&ch->device[SLAVE]);
+#ifdef ATAPICAM
+	if (ch->devices & (ATA_ATAPI_MASTER | ATA_ATAPI_SLAVE))
+	    atapi_cam_attach_bus (ch);
+#endif
     }
 #endif
     splx(s);
@@ -849,6 +861,10 @@
     ata_printf(ch, -1, "resetting devices .. ");
     ata_reset(ch);
 
+#ifdef ATAPICAM
+    if (devices & (ATA_ATAPI_SLAVE|ATA_ATAPI_MASTER))
+	atapi_cam_detach_bus(ch);
+#endif
     if ((misdev = devices & ~ch->devices)) {
 	if (misdev)
 	    printf("\n");
@@ -918,6 +934,10 @@
 	ata_getparam(&ch->device[SLAVE], ATA_C_ATAPI_IDENTIFY);
 	atapi_reinit(&ch->device[SLAVE]);
     }
+#ifdef ATAPICAM
+    if (ch->devices & (ATA_ATAPI_MASTER | ATA_ATAPI_SLAVE))
+	atapi_cam_attach_bus (ch);
+#endif
 #endif
     printf("done\n");
     ATA_UNLOCK_CH(ch);
diff -u sys/dev/ata/atapi-all.c.dist sys/dev/ata/atapi-all.c
--- sys/dev/ata/atapi-all.c.dist	Tue Mar 26 10:36:43 2002
+++ sys/dev/ata/atapi-all.c	Fri Apr  5 13:46:55 2002
@@ -51,7 +51,9 @@
 static void atapi_write(struct atapi_request *, int);
 static void atapi_finish(struct atapi_request *);
 static void atapi_timeout(struct atapi_request *);
+#ifndef ATAPICAM
 static char *atapi_type(int);
+#endif
 static char *atapi_cmd2str(u_int8_t);
 static char *atapi_skey2str(u_int8_t);
 
@@ -115,10 +117,12 @@
 	break; 
 #endif
     }
+#ifndef ATAPICAM
     ata_prtdev(atadev, "<%.40s/%.8s> %s device - NO DRIVER!\n",
 	       atadev->param->model, atadev->param->revision, 
 	       atapi_type(atadev->param->type));
     free(atadev->result, M_ATAPI);
+#endif
     atadev->driver = NULL;
 }
 
@@ -636,6 +640,7 @@
     ata_reinit(atadev->channel);
 }
 
+#ifndef ATAPICAM
 static char *
 atapi_type(int type)
 {
@@ -652,6 +657,7 @@
 	return "Unknown";
     }
 }
+#endif
 
 static char *
 atapi_cmd2str(u_int8_t cmd)
diff -u sys/dev/ata/atapi-all.h.dist sys/dev/ata/atapi-all.h
--- sys/dev/ata/atapi-all.h.dist	Mon Mar 18 09:37:34 2002
+++ sys/dev/ata/atapi-all.h	Fri Apr  5 13:46:55 2002
@@ -172,7 +172,9 @@
 };
 
 void atapi_attach(struct ata_device *);
+void atapi_cam_attach_bus(struct ata_channel *);
 void atapi_detach(struct ata_device *);
+void atapi_cam_detach_bus(struct ata_channel *);
 void atapi_reinit(struct ata_device *);
 void atapi_start(struct ata_device *);
 int atapi_transfer(struct atapi_request *);
@@ -191,3 +193,4 @@
 int astattach(struct ata_device *);
 void astdetach(struct ata_device *);
 void ast_start(struct ata_device *);
+
diff -u sys/cam/scsi/scsi_da.c.dist sys/cam/scsi/scsi_da.c
--- sys/cam/scsi/scsi_da.c.dist	Sat Feb 23 03:37:02 2002
+++ sys/cam/scsi/scsi_da.c	Fri Apr  5 13:46:55 2002
@@ -123,6 +123,7 @@
 	da_state state;
 	da_flags flags;	
 	da_quirks quirks;
+	int	 max_transfer_size;
 	int	 minimum_cmd_size;
 	int	 ordered_tag_count;
 	struct	 disk_params params;
@@ -133,6 +134,7 @@
 struct da_quirk_entry {
 	struct scsi_inquiry_pattern inq_pat;
 	da_quirks quirks;
+	int max_transfer_size;
 };
 
 static const char quantum[] = "QUANTUM";
@@ -148,12 +150,12 @@
 		 * Reported by: W.Scholten <whs@xs4all.nl>
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "FUJITSU", "M2513A", "*"},
-		/*quirks*/ DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/* See above. */
 		{T_OPTICAL, SIP_MEDIA_REMOVABLE, "FUJITSU", "M2513A", "*"},
-		/*quirks*/ DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
@@ -162,7 +164,7 @@
 		 * Reported by: Tom Jackson <toj@gorilla.net>
 		 */
 		{T_DIRECT, SIP_MEDIA_FIXED, "FUJITSU", "M2954*", "*"},
-		/*quirks*/ DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	
 	},
 	{
@@ -172,7 +174,7 @@
 		 * in NetBSD PR kern/6027, August 24, 1998.
 		 */
 		{T_DIRECT, SIP_MEDIA_FIXED, microp, "2217*", "*"},
-		/*quirks*/ DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
@@ -181,7 +183,7 @@
 		 * (PR 8882).
 		 */
 		{T_DIRECT, SIP_MEDIA_FIXED, microp, "2112*", "*"},
-		/*quirks*/ DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
@@ -189,21 +191,21 @@
 		 * Reported by: Blaz Zupan <blaz@gold.amis.net>
 		 */
 		{T_DIRECT, SIP_MEDIA_FIXED, "NEC", "D3847*", "*"},
-		/*quirks*/ DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
 		 * Doesn't like the synchronize cache command.
 		 */
 		{T_DIRECT, SIP_MEDIA_FIXED, quantum, "MAVERICK 540S", "*"},
-		/*quirks*/ DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
 		 * Doesn't like the synchronize cache command.
 		 */
 		{T_DIRECT, SIP_MEDIA_FIXED, quantum, "LPS525S", "*"},
-		/*quirks*/ DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
@@ -213,14 +215,14 @@
 		 * Reported by:  Adam McDougall <bsdx@spawnet.com>
 		 */
 		{T_DIRECT, SIP_MEDIA_FIXED, quantum, "VIKING 4*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
 		 * See above.
 		 */
 		{T_DIRECT, SIP_MEDIA_FIXED, quantum, "VIKING 2*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
 	},
 
 	/* Below a list of quirks for USB devices supported by umass. */
@@ -232,12 +234,12 @@
 		 * not support sync cache (0x35).
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "Y-E DATA", "USB-FDU", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/* Another USB floppy */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "MATSHITA", "FDD CF-VFDU*","*"},
-		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
@@ -246,39 +248,39 @@
 		 * Make all sony MS* products use this quirk.
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "Sony", "MS*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
 		 * Sony DSC cameras (DSC-S30, DSC-S50, DSC-S70)
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "Sony", "Sony DSC", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
     {
 		/*
 		 * Maxtor 3000LE USB Drive
 		 */
 		{T_DIRECT, SIP_MEDIA_FIXED, "MAXTOR*", "K040H2*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
 	},
     {
 		/*
 		 * LaCie USB drive, among others
 		 */
 		{T_DIRECT, SIP_MEDIA_FIXED, "Maxtor*", "D080H4*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
 	},
 	{
 		{T_OPTICAL, SIP_MEDIA_REMOVABLE, "FUJITSU", "MCF3064AP", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
 		 * Microtech USB CameraMate
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "eUSB    Compact*", "Compact Flash*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
@@ -287,63 +289,63 @@
 		 * spaces. The trailing wildcard character '*' is required.
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "SMSC*", "USB FDC*","*"},
-		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
         {
 		/*
 		 * Olympus digital cameras (C-3040ZOOM, C-2040ZOOM, C-1)
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "OLYMPUS", "C-*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
 		 * Olympus digital cameras (D-370)
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "OLYMPUS", "D-*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
 		 * Olympus digital cameras (E-100RS, E-10).
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "OLYMPUS", "E-*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
 	},
         {
 		/*
 		 * KingByte Pen Drives
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "NO BRAND", "PEN DRIVE", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE
+		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
  	},
  	{
 		/*
 		 * FujiFilm Camera
 		 */
  		{T_DIRECT, SIP_MEDIA_REMOVABLE, "FUJIFILMUSB-DRIVEUNIT", "USB-DRIVEUNIT", "*"},
- 		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE
+ 		/*quirks*/ DA_Q_NO_6_BYTE|DA_Q_NO_SYNC_CACHE, /*max_transfer_size*/ 0
  	},
 	{
 		/*
 		 * Nikon Coolpix E775/E995 Cameras 
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "NIKON", "NIKON DSC E*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
 		 * Nikon Coolpix E885 Camera
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "Nikon", "Digital Camera", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
 		 * Minolta Dimage 2330
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "MINOLTA", "DIMAGE 2330*", "*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
@@ -351,15 +353,21 @@
 		 * Doesn't work correctly with 6 byte reads/writes.
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "DIVA USB", "Media Reader","*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
 	},
 	{
 		/*
 		 * Daisy Technology PhotoClip USB Camera
 		 */
 		{T_DIRECT, SIP_MEDIA_REMOVABLE, "Digital", "World   DMC","*"},
-		/*quirks*/ DA_Q_NO_6_BYTE
-	}
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 0
+	},
+	/* Below a list of quirks for ATAPI devices supported by ATAPI/CAM. */
+	{
+		/* Iomega ZIP */
+		{T_DIRECT, SIP_MEDIA_REMOVABLE, "IOMEGA", "ZIP*","*"},
+		/*quirks*/ DA_Q_NO_6_BYTE, /*max_transfer_size*/ 64
+	},
 };
 
 static	d_open_t	daopen;
@@ -1132,10 +1140,13 @@
 			       sizeof(da_quirk_table)/sizeof(*da_quirk_table),
 			       sizeof(*da_quirk_table), scsi_inquiry_match);
 
-	if (match != NULL)
+	if (match != NULL) {
 		softc->quirks = ((struct da_quirk_entry *)match)->quirks;
-	else
+		softc->max_transfer_size = ((struct da_quirk_entry *)match)->max_transfer_size;
+	} else {
 		softc->quirks = DA_Q_NONE;
+		softc->max_transfer_size = 0;
+	}
 
 	if (softc->quirks & DA_Q_NO_6_BYTE)
 		softc->minimum_cmd_size = 10;
@@ -1628,6 +1639,10 @@
 	dp = &softc->params;
 	dp->secsize = scsi_4btoul(rdcap->length);
 	dp->sectors = scsi_4btoul(rdcap->addr) + 1;
+	if (softc->max_transfer_size != 0)
+		softc->disk.d_dev->si_iosize_max
+		  = softc->max_transfer_size * dp->secsize;
+
 	/*
 	 * Have the controller provide us with a geometry
 	 * for this disk.  The only time the geometry
