diff -u /dev/null sys/dev/ata/atapi-cam.c --- /dev/null Tue Mar 19 13:19:40 2002 +++ sys/dev/ata/atapi-cam.c Tue Mar 19 13:16:21 2002 @@ -0,0 +1,508 @@ +/*- + * Copyright (c) 2001, 2002 Thomas Quinot + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* Private data associated with an ATAPI bus */ +struct atapi_xpt_softc { + struct ata_channel *ata_scp; + struct ata_device *devices[2]; + 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_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); + +struct cam_sim *atapi_cam_attach_bus (struct ata_channel *ata_scp) { + 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_scp = ata_scp; + scp->devices[ATA_DEV(ATA_MASTER)] = NULL; + scp->devices[ATA_DEV(ATA_SLAVE)] = NULL; + scp->flags = 0; + unit = device_get_unit (ata_scp->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); + + return sim; + + 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); + return NULL; +} + +void atapi_cam_attach_dev (struct cam_sim *sim, struct ata_device *atp) { + struct atapi_xpt_softc *softc = (struct atapi_xpt_softc *) cam_sim_softc (sim); + + ata_set_name (atp, "atapicam", + 2 * cam_sim_unit (sim) + (atp->unit == ATA_MASTER) ? 0 : 1); + softc->devices[ATA_DEV(atp->unit)] = atp; +} + +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; +#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 (tid > 1 || softc->devices[tid] == 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 + (softc->devices[tid], + 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); +} diff -u /dev/null sys/dev/ata/atapi-cam.h --- /dev/null Tue Mar 19 13:19:40 2002 +++ sys/dev/ata/atapi-cam.h Tue Mar 19 13:16:21 2002 @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2001, 2002 Thomas Quinot + * 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 +#include +#include + +struct cam_sim *atapi_cam_attach_bus (struct ata_channel *ata_scp); +void atapi_cam_attach_dev (struct cam_sim *sim, struct ata_device *atp); diff -u sys/cam/cam_xpt.c.dist sys/cam/cam_xpt.c --- sys/cam/cam_xpt.c.dist Thu Mar 7 00:33:38 2002 +++ sys/cam/cam_xpt.c Sun Mar 17 18:35:55 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 */ @@ -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; Index: sys/dev/ata/ata-all.c =================================================================== RCS file: /home/ncvs/src/sys/dev/ata/ata-all.c,v retrieving revision 1.130 diff -u -r1.130 ata-all.c --- sys/dev/ata/ata-all.c 12 Feb 2002 13:21:51 -0000 1.130 +++ sys/dev/ata/ata-all.c 28 Feb 2002 13:24:47 -0000 @@ -53,6 +53,9 @@ #include #include #include +#ifdef ATAPICAM +#include +#endif /* device structures */ static d_ioctl_t ataioctl; @@ -202,6 +205,17 @@ 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)) { + struct cam_sim *sim; + + sim = atapi_cam_attach_bus (ch); + if (ch->devices & ATA_ATAPI_MASTER) + atapi_cam_attach_dev (sim, &ch->device[MASTER]); + if (ch->devices & ATA_ATAPI_SLAVE) + atapi_cam_attach_dev (sim, &ch->device[SLAVE]); + } +#endif #endif /* we should probe & attach RAID's here as well SOS XXX */ } @@ -542,6 +556,17 @@ 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)) { + struct cam_sim *sim; + + sim = atapi_cam_attach_bus (ch); + if (ch->devices & ATA_ATAPI_MASTER) + atapi_cam_attach_dev (sim, &ch->device[MASTER]); + if (ch->devices & ATA_ATAPI_SLAVE) + atapi_cam_attach_dev (sim, &ch->device[SLAVE]); + } +#endif } #endif }