diff options
author | sos <sos@FreeBSD.org> | 2002-08-09 20:54:06 +0000 |
---|---|---|
committer | sos <sos@FreeBSD.org> | 2002-08-09 20:54:06 +0000 |
commit | baa2e98857c911a36071c6162fad2f7b67206e6d (patch) | |
tree | 297a0ff3fecfeb293a7a7ae5d22b8c0e5db874cd /sys/dev/ata | |
parent | 1f1f9bd75babfbf493a82d48e87ddad29c66ff2e (diff) | |
download | FreeBSD-src-baa2e98857c911a36071c6162fad2f7b67206e6d.zip FreeBSD-src-baa2e98857c911a36071c6162fad2f7b67206e6d.tar.gz |
Add the ability to use ATAPI devices via CAM.
The CAM<>ATAPI layer was submitted by "Thomas Quinot <thomas@cuivre.fr.eu.org>"
changes form the version on the net by me (formatting, ability to be used
alone without the ATAPI native device driver, proper speed reporting...)
See /sys/conf/NOTES for usage.
Submitted by: Thomas Quinot <thomas@cuivre.fr.eu.org>
Diffstat (limited to 'sys/dev/ata')
-rw-r--r-- | sys/dev/ata/ata-all.c | 44 | ||||
-rw-r--r-- | sys/dev/ata/atapi-all.c | 25 | ||||
-rw-r--r-- | sys/dev/ata/atapi-all.h | 2 | ||||
-rw-r--r-- | sys/dev/ata/atapi-cam.c | 712 |
4 files changed, 752 insertions, 31 deletions
diff --git a/sys/dev/ata/ata-all.c b/sys/dev/ata/ata-all.c index c6230c7..a7a7628 100644 --- a/sys/dev/ata/ata-all.c +++ b/sys/dev/ata/ata-all.c @@ -92,6 +92,10 @@ devclass_t ata_devclass; static struct intr_config_hook *ata_delayed_attach = NULL; static MALLOC_DEFINE(M_ATA, "ATA generic", "ATA driver generic layer"); +/* misc defines */ +#define DEV_ATAPIALL defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || \ + defined(DEV_ATAPIST) || defined(DEV_ATAPICAM) + int ata_probe(device_t dev) { @@ -199,12 +203,16 @@ ata_attach(device_t dev) if (ch->devices & ATA_ATA_SLAVE) ad_attach(&ch->device[SLAVE]); #endif -#if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) +#if DEV_ATAPIALL if (ch->devices & ATA_ATAPI_MASTER) atapi_attach(&ch->device[MASTER]); if (ch->devices & ATA_ATAPI_SLAVE) atapi_attach(&ch->device[SLAVE]); #endif +#ifdef DEV_ATAPICAM + if (ch->devices & (ATA_ATAPI_MASTER | ATA_ATAPI_SLAVE)) + atapi_cam_attach_bus(ch); +#endif } return 0; } @@ -229,12 +237,16 @@ ata_detach(device_t dev) if (ch->devices & ATA_ATA_SLAVE && ch->device[SLAVE].driver) ad_detach(&ch->device[SLAVE], 1); #endif -#if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) +#if DEV_ATAPIALL if (ch->devices & ATA_ATAPI_MASTER && ch->device[MASTER].driver) atapi_detach(&ch->device[MASTER]); if (ch->devices & ATA_ATAPI_SLAVE && ch->device[SLAVE].driver) atapi_detach(&ch->device[SLAVE]); #endif +#ifdef DEV_ATAPICAM + if (ch->devices & (ATA_ATAPI_SLAVE|ATA_ATAPI_MASTER)) + atapi_cam_detach_bus(ch); +#endif splx(s); if (ch->device[MASTER].param) { @@ -424,7 +436,7 @@ ataioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td) case ATARAIDSTATUS: return ata_raid_status(iocmd->channel, &iocmd->u.raid_status); #endif -#if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) +#if DEV_ATAPIALL case ATAPICMD: { struct ata_device *atadev; caddr_t buf; @@ -562,17 +574,21 @@ ata_boot_attach(void) } ata_raid_attach(); #endif -#if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) /* then the atapi devices */ for (ctlr=0; ctlr<devclass_get_maxunit(ata_devclass); ctlr++) { if (!(ch = devclass_get_softc(ata_devclass, ctlr))) continue; +#if DEV_ATAPIALL if (ch->devices & ATA_ATAPI_MASTER) atapi_attach(&ch->device[MASTER]); if (ch->devices & ATA_ATAPI_SLAVE) atapi_attach(&ch->device[SLAVE]); - } #endif +#ifdef DEV_ATAPICAM + if (ch->devices & (ATA_ATAPI_MASTER | ATA_ATAPI_SLAVE)) + atapi_cam_attach_bus(ch); +#endif + } } static void @@ -608,7 +624,7 @@ ata_intr(void *data) return; break; #endif -#if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) +#if DEV_ATAPIALL case ATA_ACTIVE_ATAPI: if (!ch->running || atapi_interrupt(ch->running) == ATA_OP_CONTINUES) return; @@ -642,7 +658,7 @@ ata_start(struct ata_channel *ch) #ifdef DEV_ATADISK struct ad_request *ad_request; #endif -#if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) +#if DEV_ATAPIALL struct atapi_request *atapi_request; #endif int s; @@ -670,7 +686,7 @@ ata_start(struct ata_channel *ch) } #endif -#if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) +#if DEV_ATAPIALL /* find & call the responsible driver if anything on the ATAPI queue */ if (TAILQ_EMPTY(&ch->atapi_queue)) { if (ch->devices & (ATA_ATAPI_MASTER) && ch->device[MASTER].driver) @@ -834,6 +850,10 @@ ata_reinit(struct ata_channel *ch) ata_printf(ch, -1, "resetting devices ..\n"); ata_reset(ch); +#ifdef DEV_ATAPICAM + if (devices & (ATA_ATAPI_SLAVE|ATA_ATAPI_MASTER)) + atapi_cam_detach_bus(ch); +#endif if ((misdev = devices & ~ch->devices)) { #ifdef DEV_ATADISK if (misdev & ATA_ATA_MASTER && ch->device[MASTER].driver) @@ -841,7 +861,7 @@ ata_reinit(struct ata_channel *ch) if (misdev & ATA_ATA_SLAVE && ch->device[SLAVE].driver) ad_detach(&ch->device[SLAVE], 0); #endif -#if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) +#if DEV_ATAPIALL if (misdev & ATA_ATAPI_MASTER && ch->device[MASTER].driver) atapi_detach(&ch->device[MASTER]); if (misdev & ATA_ATAPI_SLAVE && ch->device[SLAVE].driver) @@ -886,7 +906,7 @@ ata_reinit(struct ata_channel *ch) ad_reinit(&ch->device[SLAVE]); } #endif -#if defined(DEV_ATAPICD) || defined(DEV_ATAPIFD) || defined(DEV_ATAPIST) +#if DEV_ATAPIALL if (newdev & ATA_ATAPI_MASTER && !ch->device[MASTER].driver) atapi_attach(&ch->device[MASTER]); else if (ch->devices & (ATA_ATAPI_MASTER) && ch->device[MASTER].driver) { @@ -900,6 +920,10 @@ ata_reinit(struct ata_channel *ch) atapi_reinit(&ch->device[SLAVE]); } #endif +#ifdef DEV_ATAPICAM + if (ch->devices & (ATA_ATAPI_MASTER | ATA_ATAPI_SLAVE)) + atapi_cam_attach_bus(ch); +#endif printf("done\n"); ATA_UNLOCK_CH(ch); ata_start(ch); diff --git a/sys/dev/ata/atapi-all.c b/sys/dev/ata/atapi-all.c index 36e3199..3dc920a 100644 --- a/sys/dev/ata/atapi-all.c +++ b/sys/dev/ata/atapi-all.c @@ -47,7 +47,6 @@ static void atapi_read(struct atapi_request *, int); static void atapi_write(struct atapi_request *, int); static void atapi_finish(struct atapi_request *); static void atapi_timeout(struct atapi_request *); -static char *atapi_type(int); static char *atapi_cmd2str(u_int8_t); static char *atapi_skey2str(u_int8_t); @@ -111,11 +110,12 @@ atapi_attach(struct ata_device *atadev) break; #endif } - ata_prtdev(atadev, "<%.40s/%.8s> %s device - NO DRIVER!\n", - atadev->param->model, atadev->param->revision, - atapi_type(atadev->param->type)); +#ifndef DEV_ATAPICAM + ata_prtdev(atadev, "<%.40s/%.8s> - NO DRIVER!\n", + atadev->param->model, atadev->param->revision); free(atadev->result, M_ATAPI); atadev->driver = NULL; +#endif } void @@ -632,23 +632,6 @@ atapi_timeout(struct atapi_request *request) } static char * -atapi_type(int type) -{ - switch (type) { - case ATAPI_TYPE_CDROM: - return "CDROM"; - case ATAPI_TYPE_DIRECT: - return "floppy"; - case ATAPI_TYPE_TAPE: - return "tape"; - case ATAPI_TYPE_OPTICAL: - return "optical"; - default: - return "Unknown"; - } -} - -static char * atapi_cmd2str(u_int8_t cmd) { switch (cmd) { diff --git a/sys/dev/ata/atapi-all.h b/sys/dev/ata/atapi-all.h index 92f123e..af91564 100644 --- a/sys/dev/ata/atapi-all.h +++ b/sys/dev/ata/atapi-all.h @@ -172,7 +172,9 @@ struct atapi_request { }; 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 *); diff --git a/sys/dev/ata/atapi-cam.c b/sys/dev/ata/atapi-cam.c new file mode 100644 index 0000000..eb14d41 --- /dev/null +++ b/sys/dev/ata/atapi-cam.c @@ -0,0 +1,712 @@ +/*- + * 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/devicestat.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/ata.h> +#include <machine/bus.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_periph.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_debug.h> +#include <cam/scsi/scsi_all.h> + +#include <dev/ata/ata-all.h> +#include <dev/ata/atapi-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 lags; +#define DOING_AUTOSENSE 1 + + char *dxfer_alloc; + TAILQ_ENTRY(atapi_hcb) chain; +}; + +/* private data associated with an ATA bus */ +struct atapi_xpt_softc { + struct ata_channel *ata_ch; + struct cam_path *path; + 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; +}; + +static LIST_HEAD(,atapi_xpt_softc) all_buses = LIST_HEAD_INITIALIZER(all_buses); + +/* CAM XPT methods */ +static void atapi_action(struct cam_sim *, union ccb *); +static void atapi_action1(struct cam_sim *, union ccb *); +static void atapi_poll(struct cam_sim *); +static void atapi_async(void *, u_int32_t, struct cam_path *, void *); +static void atapi_async1(void *, u_int32_t, struct cam_path *, void *); +static int atapi_cb(struct atapi_request *); + +/* internal functions */ +static void setup_dev(struct atapi_xpt_softc *, struct ata_device *); +static void setup_async_cb(struct atapi_xpt_softc *, uint32_t); +static void cam_rescan_callback(struct cam_periph *, union ccb *); +static void cam_rescan(struct cam_sim *); +static void free_hcb_and_ccb_done(struct atapi_hcb *, u_int32_t); +static struct atapi_hcb *allocate_hcb(struct atapi_xpt_softc *, int, int, union 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; + + LIST_FOREACH(scp, &all_buses, chain) { + if (scp->ata_ch == ata_ch) + return; + } + + 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); + + if ((devq = cam_simq_alloc(16)) == NULL) + goto error; + + if ((sim = cam_sim_alloc(atapi_action, atapi_poll, "ata", + (void *)scp, unit, 1, 1, devq)) == NULL) { + cam_simq_free(devq); + goto error; + } + scp->sim = 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->path = path; + +#ifdef CAMDEBUG + xpt_print_path(path); + printf("Registered SIM for ata%d\n", unit); +#endif + + setup_async_cb(scp, AC_LOST_DEVICE); + + 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]); + + cam_rescan(sim); + 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) + 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 +setup_async_cb(struct atapi_xpt_softc *scp, uint32_t events) +{ + struct ccb_setasync csa; + + xpt_setup_ccb(&csa.ccb_h, scp->path, /*priority*/ 5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = events; + csa.callback = &atapi_async; + csa.callback_arg = scp->sim; + xpt_action((union ccb *) &csa); +} + +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; + struct atapi_hcb *hcb = NULL; + int unit = cam_sim_unit(sim); + int bus = cam_sim_bus(sim); + int len; + char *buf; + + 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 = 1; + 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); + if (softc->ata_ch && ccb_h->target_id >= 0) { + switch (softc->ata_ch->device[ccb_h->target_id].mode) { + case ATA_PIO1: + cpi->base_transfer_speed = 5200; + break; + case ATA_PIO2: + cpi->base_transfer_speed = 7000; + break; + case ATA_PIO3: + cpi->base_transfer_speed = 11000; + break; + case ATA_PIO4: + case ATA_DMA: + case ATA_WDMA2: + cpi->base_transfer_speed = 16000; + break; + case ATA_UDMA2: + cpi->base_transfer_speed = 33000; + break; + case ATA_UDMA4: + cpi->base_transfer_speed = 66000; + break; + case ATA_UDMA5: + cpi->base_transfer_speed = 100000; + break; + case ATA_UDMA6: + cpi->base_transfer_speed = 133000; + break; + default: cpi->base_transfer_speed = 3300; + } + } + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + return; + } + + case XPT_RESET_DEV: + /* should reset the device */ +#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 */ +#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 |= + (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: { + struct ccb_calc_geometry *ccg; + unsigned int size_mb; + unsigned int secs_per_cylinder; + int extended; + + 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 (lid > 0) { +#ifdef CAMDEBUG + printf("Invalid LUN %d\n", lid); +#endif + ccb_h->status = CAM_LUN_INVALID; + xpt_done(ccb); + return; + } + 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 or + * 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))) + 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) | ATPR_F_QUIET, + ccb_h->timeout, atapi_cb, (void *)hcb) == 0) + return; + break; + } + + 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; + struct ccb_scsiio *csio = &hcb->ccb->csio; + int hcb_status = req->result; + +#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) { + /* 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); + } + 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); + xpt_print_path(path); + if (targ == -1) + printf("Lost host adapter\n"); + else + printf("Lost target %d???\n", targ); + break; + + default: + break; + } +} + +static void +cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) +{ +#ifdef CAMDEBUG + xpt_print_path(ccb->ccb_h.path); + if (ccb->ccb_h.status != CAM_REQ_CMP) + printf("Rescan failed, 0x%04x\n", ccb->ccb_h.status); + else + printf("rescan succeeded\n"); +#endif + xpt_free_path(ccb->ccb_h.path); + free(ccb, M_ATACAM); +} + +static void +cam_rescan(struct cam_sim *sim) +{ + struct cam_path *path; + union ccb *ccb = malloc(sizeof(union ccb), M_ATACAM, M_WAITOK | M_ZERO); + +#ifdef CAMDEBUG + xpt_print_path(cam_sim_path(sim)); + printf ("rescanning ATAPI bus.\n"); +#endif + if (xpt_create_path(&path, xpt_periph, cam_sim_path(sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) + return; + + xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/); + ccb->ccb_h.func_code = XPT_SCAN_BUS; + ccb->ccb_h.cbfcnp = cam_rescan_callback; + ccb->crcn.flags = CAM_FLAG_NONE; + xpt_action(ccb); + /* scan is in progress now */ +} + +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->path != NULL) { + setup_async_cb(scp, 0); + xpt_free_path(scp->path); + } + if ((scp->flags & BUS_REGISTERED) != 0) { + if (xpt_bus_deregister(cam_sim_path(scp->sim)) == CAM_REQ_CMP) + scp->flags &= ~BUS_REGISTERED; + } + if (scp->sim != NULL) { + if ((scp->flags & BUS_REGISTERED) == 0) + cam_sim_free(scp->sim, /*free_devq*/TRUE); + else + printf("Can't free %s SIM (still registered)\n", + cam_sim_name(scp->sim)); + } + 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; + } +} |