diff -u /dev/null sys/dev/ata/atapi-cam.c
--- /dev/null	Thu Mar 21 14:49:38 2002
+++ sys/dev/ata/atapi-cam.c	Thu Mar 21 14:47:38 2002
@@ -0,0 +1,567 @@
+/*-
+ * 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
+ * 
+ */
+
+/* ATA headers used in atapi-cam.h */
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/systm.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/atapi-all.h>
+#include <dev/ata/atapi-cam.h>
+
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+#include <cam/scsi/scsi_all.h>
+
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+/* 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 RESOURCE_SHORTAGE 1
+};
+
+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;
+	char           *dxfer_alloc;
+};
+
+#define DOING_AUTOSENSE 1
+
+static          MALLOC_DEFINE(M_ATACAM, "ATA CAM transport", "ATA driver CAM-XPT layer");
+
+/* Internal functions */
+static void     atapi_cam_attach_dev(struct ata_device * atp);
+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 *);
+static void     free_hcb_and_ccb_done(struct atapi_hcb * hcb, union ccb * ccb, u_int32_t status);
+static struct atapi_hcb *allocate_hcb(struct atapi_xpt_softc * softc, int unit, int bus);
+static void     free_hcb(struct atapi_hcb * hcb);
+static struct ata_device *get_ata_device(struct atapi_xpt_softc * scp, int id);
+
+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;
+	int             bus_registered = 0;
+	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;
+	scp->flags = 0;
+	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) {
+		goto error;
+	}
+	/* Register the SIM. */
+	if (xpt_bus_register(sim, 0) != CAM_SUCCESS) {
+		goto error;
+	}
+	bus_registered = 1;
+
+	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;
+	scp->sim = sim;
+
+	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)
+		atapi_cam_attach_dev(&ata_ch->device[MASTER]);
+	if (ata_ch->devices & ATA_ATAPI_SLAVE)
+		atapi_cam_attach_dev(&ata_ch->device[SLAVE]);
+
+	return;
+
+error:
+	if (bus_registered == 1)
+		xpt_bus_deregister(cam_sim_path(sim));
+	if (sim != NULL)
+		cam_sim_free(sim, /* free_devq */ FALSE);
+	if (devq != NULL)
+		cam_simq_free(devq);
+	if (scp != NULL)
+		free(scp, M_ATACAM);
+}
+
+static void 
+atapi_cam_attach_dev(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);
+}
+
+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 = 7;
+			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_SCSI_IO:
+		{
+			struct ccb_scsiio *csio = &ccb->csio;
+			int             error, 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)) == NULL) {
+				goto action_oom;
+			}
+			hcb->ccb = ccb;
+			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
+
+			/* Some SCSI commands require special processing.    */
+
+			switch (hcb->cmd[0]) {
+			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
+
+				if (hcb->cmd[0] == MODE_SELECT_6) {
+					hcb->cmd[0] = MODE_SELECT_10;
+				} else {
+					hcb->cmd[0] = MODE_SENSE_10;
+				}
+				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;
+			}
+
+			len = csio->dxfer_len;
+			buf = csio->data_ptr;
+
+			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;
+			}
+			error = 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);
+
+			if (error != 0) {
+				free_hcb(hcb);
+				break;
+			}
+			return;
+		}
+	default:
+		printf("atapi-cam: unsupported function code %02x\n", ccb_h->func_code);
+		break;
+	}
+	ccb_h->status = CAM_REQ_INVALID;
+	xpt_done(ccb);
+
+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 targ = hcb->target; */
+	/* int ccb_status; */
+	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, hcb->ccb, 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, hcb->ccb, CAM_REQ_CMP);
+	}
+	splx(s);
+	return 0;
+};
+
+static void
+free_hcb_and_ccb_done(struct atapi_hcb * hcb, union ccb * ccb, u_int32_t status)
+{
+	struct atapi_xpt_softc *softc = hcb->softc;
+
+	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 %u ???\n", targ);
+		break;
+	default:
+		break;
+	}
+}
+
+static struct atapi_hcb *
+allocate_hcb(struct atapi_xpt_softc * softc, int unit, int bus)
+{
+	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;
+	}
+	return hcb;
+}
+
+static void 
+free_hcb(struct atapi_hcb * hcb)
+{
+	if (hcb->dxfer_alloc != NULL)
+		free(hcb->dxfer_alloc, M_ATACAM);
+	free(hcb, 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 /dev/null sys/dev/ata/atapi-cam.h
--- /dev/null	Thu Mar 21 14:49:38 2002
+++ sys/dev/ata/atapi-cam.h	Thu Mar 21 14:49:34 2002
@@ -0,0 +1,35 @@
+/*-
+ * 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:$
+ */
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+
+void            atapi_cam_attach_bus(struct ata_channel * ata_scp);
diff -u sys/cam/cam_xpt.c.dist sys/cam/cam_xpt.c
--- sys/cam/cam_xpt.c.dist	Wed Dec 19 06:27:51 2001
+++ sys/cam/cam_xpt.c	Thu Mar 21 14:16:41 2002
@@ -194,6 +194,7 @@
 #define	CAM_QUIRK_NOLUNS	0x01
 #define	CAM_QUIRK_NOSERIAL	0x02
 #define	CAM_QUIRK_HILUNS	0x04
+#define	CAM_QUIRK_NOFULLINQ	0x08
 	u_int mintags;
 	u_int maxtags;
 };
@@ -214,6 +215,7 @@
 static const char samsung[] = "SAMSUNG";
 static const char seagate[] = "SEAGATE";
 static const char microp[] = "MICROP";
+static const char toshiba[] = "TOSHIBA";
 
 static struct xpt_quirk_entry xpt_quirk_table[] = 
 {
@@ -411,6 +413,14 @@
 	},
 	{
 		/*
+		 * Many ATAPI Toshiba drives incorrectly generate a
+		 * UNIT ATTENTION condition when asked for full inquiry info.
+		 */
+		{ T_CDROM, SIP_MEDIA_REMOVABLE, toshiba, "DVD-ROM SD-M*", "*" },
+		CAM_QUIRK_NOFULLINQ, /*mintags*/0, /*maxtags*/0
+	},
+	{
+		/*
 		 * This drive doesn't like multiple LUN probing.
 		 * Submitted by:  Parag Patel <parag@cgt.com>
 		 */
@@ -5504,16 +5514,17 @@
 				 * the amount of information the device
 				 * is willing to give.
 				 */
+				xpt_find_quirk(path->device);
 				alen = inq_buf->additional_length;
 				if (softc->action == PROBE_INQUIRY
-				 && alen > (SHORT_INQUIRY_LENGTH - 4)) {
+				 && alen > (SHORT_INQUIRY_LENGTH - 4)
+				 && (path->device->quirk->quirks
+				     & CAM_QUIRK_NOFULLINQ) == 0) {
 					softc->action = PROBE_FULL_INQUIRY;
 					xpt_release_ccb(done_ccb);
 					xpt_schedule(periph, priority);
 					return;
 				}
-
-				xpt_find_quirk(path->device);
 
 				if ((inq_buf->flags & SID_CmdQue) != 0)
 					softc->action = PROBE_MODE_SENSE;
diff -u sys/dev/ata/ata-all.c.dist sys/dev/ata/ata-all.c
--- sys/dev/ata/ata-all.c.dist	Mon Mar 18 09:37:33 2002
+++ sys/dev/ata/ata-all.c	Thu Mar 21 14:24:58 2002
@@ -58,6 +58,9 @@
 #include <dev/ata/ata-disk.h>
 #include <dev/ata/ata-raid.h>
 #include <dev/ata/atapi-all.h>
+#ifdef ATAPICAM
+#include <dev/ata/atapi-cam.h>
+#endif
 
 /* device structures */
 static	d_ioctl_t	ataioctl;
@@ -209,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
     }
     return 0;
@@ -520,6 +527,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
 }
diff -u sys/dev/ata/atapi-all.c.dist sys/dev/ata/atapi-all.c
--- sys/dev/ata/atapi-all.c.dist	Thu Mar 21 14:10:33 2002
+++ sys/dev/ata/atapi-all.c	Thu Mar 21 14:16:41 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);
 
@@ -116,10 +118,12 @@
 #endif
 notfound:
     default:
+#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;
     }
 }
@@ -638,6 +642,7 @@
     ata_reinit(atadev->channel);
 }
 
+#ifndef ATAPICAM
 static char *
 atapi_type(int type)
 {
@@ -654,6 +659,7 @@
 	return "Unknown";
     }
 }
+#endif
 
 static char *
 atapi_cmd2str(u_int8_t cmd)
