示例#1
0
/*
 * Write a PM register
 */
int
sili_pm_write(struct sili_port *ap, int target, int which, u_int32_t data)
{
	struct ata_xfer	*xa;
	int error;

	xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]);

	xa->fis->type = ATA_FIS_TYPE_H2D;
	xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
	xa->fis->command = ATA_C_WRITE_PM;
	xa->fis->features = which;
	xa->fis->device = target | ATA_H2D_DEVICE_LBA;
	xa->fis->sector_count = (u_int8_t)data;
	xa->fis->lba_low = (u_int8_t)(data >> 8);
	xa->fis->lba_mid = (u_int8_t)(data >> 16);
	xa->fis->lba_high = (u_int8_t)(data >> 24);
	xa->fis->control = ATA_FIS_CONTROL_4BIT;

	xa->complete = sili_pm_dummy_done;
	xa->datalen = 0;
	xa->flags = ATA_F_POLL | ATA_F_EXCLUSIVE;
	xa->timeout = 1000;

	if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
		error = 0;
	else
		error = EIO;
	sili_ata_put_xfer(xa);
	return(error);
}
示例#2
0
/*
 * Read a PM register
 */
int
sili_pm_read(struct sili_port *ap, int target, int which, u_int32_t *datap)
{
	struct ata_xfer	*xa;
	int error;

	xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]);

	xa->fis->type = ATA_FIS_TYPE_H2D;
	xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
	xa->fis->command = ATA_C_READ_PM;
	xa->fis->features = which;
	xa->fis->device = target | ATA_H2D_DEVICE_LBA;
	xa->fis->control = ATA_FIS_CONTROL_4BIT;

	xa->complete = sili_pm_dummy_done;
	xa->datalen = 0;
	xa->flags = ATA_F_POLL | ATA_F_AUTOSENSE;
	xa->timeout = 1000;

	if (sili_ata_cmd(xa) == ATA_S_COMPLETE) {
		*datap = xa->rfis->sector_count | (xa->rfis->lba_low << 8) |
		       (xa->rfis->lba_mid << 16) | (xa->rfis->lba_high << 24);
		error = 0;
	} else {
		kprintf("%s.%d pm_read SCA[%d] failed\n",
			PORTNAME(ap), target, which);
		*datap = 0;
		error = EIO;
	}
	sili_ata_put_xfer(xa);
	return (error);
}
示例#3
0
int
sili_pm_set_feature(struct sili_port *ap, int feature, int enable)
{
	struct ata_xfer	*xa;
	int error;

	xa = sili_ata_get_xfer(ap, &ap->ap_ata[15]);

	xa->fis->type = ATA_FIS_TYPE_H2D;
	xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
	xa->fis->command = enable ? ATA_C_SATA_FEATURE_ENA :
				    ATA_C_SATA_FEATURE_DIS;
	xa->fis->sector_count = feature;
	xa->fis->control = ATA_FIS_CONTROL_4BIT;

	xa->complete = sili_pm_dummy_done;
	xa->datalen = 0;
	xa->flags = ATA_F_POLL | ATA_F_EXCLUSIVE;
	xa->timeout = 1000;

	if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
		error = 0;
	else
		error = EIO;
	sili_ata_put_xfer(xa);
	return(error);
}
示例#4
0
/*
 * Setting the transfer mode is irrelevant for the SATA transport
 * but some (atapi) devices seem to need it anyway.  In addition
 * if we are running through a SATA->PATA converter for some reason
 * beyond my comprehension we might have to set the mode.
 *
 * We only support DMA modes for SATA attached devices, so don't bother
 * with legacy modes.
 */
static int
sili_set_xfer(struct sili_port *ap, struct ata_port *atx)
{
	struct ata_port *at;
	struct ata_xfer	*xa;
	u_int16_t mode;
	u_int16_t mask;

	at = atx ? atx : ap->ap_ata;

	/*
	 * Figure out the supported UDMA mode.  Ignore other legacy modes.
	 */
	mask = le16toh(at->at_identify.ultradma);
	if ((mask & 0xFF) == 0 || mask == 0xFFFF)
		return(0);
	mask &= 0xFF;
	mode = 0x4F;
	while ((mask & 0x8000) == 0) {
		mask <<= 1;
		--mode;
	}

	/*
	 * SATA atapi devices often still report a dma mode, even though
	 * it is irrelevant for SATA transport.  It is also possible that
	 * we are running through a SATA->PATA converter and seeing the
	 * PATA dma mode.
	 *
	 * In this case the device may require a (dummy) SETXFER to be
	 * sent before it will work properly.
	 */
	xa = sili_ata_get_xfer(ap, atx);
	xa->complete = sili_ata_dummy_done;
	xa->fis->command = ATA_C_SET_FEATURES;
	xa->fis->features = ATA_SF_SETXFER;
	xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
	xa->fis->sector_count = mode;
	xa->flags = ATA_F_PIO | ATA_F_POLL;
	xa->timeout = 1000;
	xa->datalen = 0;
	if (sili_ata_cmd(xa) != ATA_S_COMPLETE) {
		kprintf("%s: Unable to set dummy xfer mode \n",
			ATANAME(ap, atx));
	} else if (bootverbose) {
		kprintf("%s: Set dummy xfer mode to %02x\n",
			ATANAME(ap, atx), mode);
	}
	sili_ata_put_xfer(xa);
	return(0);
}
示例#5
0
/*
 * DISK-specific probe after initial ident
 */
static int
sili_cam_probe_disk(struct sili_port *ap, struct ata_port *atx)
{
	struct ata_port *at;
	struct ata_xfer	*xa;

	at = atx ? atx : ap->ap_ata;

	/*
	 * Set dummy xfer mode
	 */
	sili_set_xfer(ap, atx);

	/*
	 * Enable write cache if supported
	 *
	 * NOTE: "WD My Book" external disk devices have a very poor
	 *	 daughter board between the the ESATA and the HD.  Sending
	 *	 any ATA_C_SET_FEATURES commands will break the hardware port
	 *	 with a fatal protocol error.  However, this device also
	 *	 indicates that WRITECACHE is already on and READAHEAD is
	 *	 not supported so we avoid the issue.
	 */
	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) &&
	    (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) {
		xa = sili_ata_get_xfer(ap, atx);
		xa->complete = sili_ata_dummy_done;
		xa->fis->command = ATA_C_SET_FEATURES;
		/*xa->fis->features = ATA_SF_WRITECACHE_EN;*/
		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
		xa->fis->device = 0;
		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
		xa->timeout = 1000;
		xa->datalen = 0;
		if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
			at->at_features |= ATA_PORT_F_WCACHE;
		else
			kprintf("%s: Unable to enable write-caching\n",
				ATANAME(ap, atx));
		sili_ata_put_xfer(xa);
	}

	/*
	 * Enable readahead if supported
	 */
	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) &&
	    (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) {
		xa = sili_ata_get_xfer(ap, atx);
		xa->complete = sili_ata_dummy_done;
		xa->fis->command = ATA_C_SET_FEATURES;
		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
		xa->fis->device = 0;
		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
		xa->timeout = 1000;
		xa->datalen = 0;
		if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
			at->at_features |= ATA_PORT_F_RAHEAD;
		else
			kprintf("%s: Unable to enable read-ahead\n",
				ATANAME(ap, atx));
		sili_ata_put_xfer(xa);
	}

	/*
	 * FREEZE LOCK the device so malicious users can't lock it on us.
	 * As there is no harm in issuing this to devices that don't
	 * support the security feature set we just send it, and don't bother
	 * checking if the device sends a command abort to tell us it doesn't
	 * support it
	 */
	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) &&
	    (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0 &&
	    (SiliNoFeatures & (1 << ap->ap_num)) == 0) {
		xa = sili_ata_get_xfer(ap, atx);
		xa->complete = sili_ata_dummy_done;
		xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
		xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
		xa->timeout = 1000;
		xa->datalen = 0;
		if (sili_ata_cmd(xa) == ATA_S_COMPLETE)
			at->at_features |= ATA_PORT_F_FRZLCK;
		else
			kprintf("%s: Unable to set security freeze\n",
				ATANAME(ap, atx));
		sili_ata_put_xfer(xa);
	}

	return (0);
}
示例#6
0
/*
 * Once the SILI port has been attached we need to probe for a device or
 * devices on the port and setup various options.
 *
 * If at is NULL we are probing the direct-attached device on the port,
 * which may or may not be a port multiplier.
 */
int
sili_cam_probe(struct sili_port *ap, struct ata_port *atx)
{
	struct ata_port	*at;
	struct ata_xfer	*xa;
	u_int64_t	capacity;
	u_int64_t	capacity_bytes;
	int		model_len;
	int		firmware_len;
	int		serial_len;
	int		error;
	int		devncqdepth;
	int		i;
	const char	*model_id;
	const char	*firmware_id;
	const char	*serial_id;
	const char	*wcstr;
	const char	*rastr;
	const char	*scstr;
	const char	*type;

	error = EIO;

	/*
	 * Delayed CAM attachment for initial probe, sim may be NULL
	 */
	if (ap->ap_sim == NULL)
		return(0);

	/*
	 * A NULL atx indicates a probe of the directly connected device.
	 * A non-NULL atx indicates a device connected via a port multiplier.
	 * We need to preserve atx for calls to sili_ata_get_xfer().
	 *
	 * at is always non-NULL.  For directly connected devices we supply
	 * an (at) pointing to target 0.
	 */
	if (atx == NULL) {
		at = ap->ap_ata;	/* direct attached - device 0 */
		if (ap->ap_type == ATA_PORT_T_PM) {
			kprintf("%s: Found Port Multiplier\n",
				ATANAME(ap, atx));
			return (0);
		}
		at->at_type = ap->ap_type;
	} else {
		at = atx;
		if (atx->at_type == ATA_PORT_T_PM) {
			kprintf("%s: Bogus device, reducing port count to %d\n",
				ATANAME(ap, atx), atx->at_target);
			if (ap->ap_pmcount > atx->at_target)
				ap->ap_pmcount = atx->at_target;
			goto err;
		}
	}
	if (ap->ap_type == ATA_PORT_T_NONE)
		goto err;
	if (at->at_type == ATA_PORT_T_NONE)
		goto err;

	/*
	 * Issue identify, saving the result
	 */
	xa = sili_ata_get_xfer(ap, atx);
	xa->complete = sili_ata_dummy_done;
	xa->data = &at->at_identify;
	xa->datalen = sizeof(at->at_identify);
	xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
	xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;

	switch(at->at_type) {
	case ATA_PORT_T_DISK:
		xa->fis->command = ATA_C_IDENTIFY;
		type = "DISK";
		break;
	case ATA_PORT_T_ATAPI:
		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
		xa->flags |= ATA_F_AUTOSENSE;
		type = "ATAPI";
		break;
	default:
		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
		type = "UNKNOWN(ATAPI?)";
		break;
	}
	xa->fis->features = 0;
	xa->fis->device = 0;
	xa->timeout = 1000;

	if (sili_ata_cmd(xa) != ATA_S_COMPLETE) {
		kprintf("%s: Detected %s device but unable to IDENTIFY\n",
			ATANAME(ap, atx), type);
		sili_ata_put_xfer(xa);
		goto err;
	}
	sili_ata_put_xfer(xa);

	ata_fix_identify(&at->at_identify);

	/*
	 * Read capacity using SATA probe info.
	 */
	if (le16toh(at->at_identify.cmdset83) & 0x0400) {
		/* LBA48 feature set supported */
		capacity = 0;
		for (i = 3; i >= 0; --i) {
			capacity <<= 16;
			capacity +=
			    le16toh(at->at_identify.addrsecxt[i]);
		}
	} else {
		capacity = le16toh(at->at_identify.addrsec[1]);
		capacity <<= 16;
		capacity += le16toh(at->at_identify.addrsec[0]);
	}
	at->at_capacity = capacity;
	if (atx == NULL)
		ap->ap_probe = ATA_PROBE_GOOD;

	capacity_bytes = capacity * 512;

	/*
	 * Negotiate NCQ, throw away any ata_xfer's beyond the negotiated
	 * number of slots and limit the number of CAM ccb's to one less
	 * so we always have a slot available for recovery.
	 *
	 * NCQ is not used if ap_ncqdepth is 1 or the host controller does
	 * not support it, and in that case the driver can handle extra
	 * ccb's.
	 *
	 * NCQ is currently used only with direct-attached disks.  It is
	 * not used with port multipliers or direct-attached ATAPI devices.
	 *
	 * Remember at least one extra CCB needs to be reserved for the
	 * error ccb.
	 */
	if ((ap->ap_sc->sc_flags & SILI_F_NCQ) &&
	    at->at_type == ATA_PORT_T_DISK &&
	    (le16toh(at->at_identify.satacap) & (1 << 8))) {
		at->at_ncqdepth = (le16toh(at->at_identify.qdepth) & 0x1F) + 1;
		devncqdepth = at->at_ncqdepth;
		if (at->at_ncqdepth > ap->ap_sc->sc_ncmds)
			at->at_ncqdepth = ap->ap_sc->sc_ncmds;
		if (at->at_ncqdepth > 1) {
			for (i = 0; i < ap->ap_sc->sc_ncmds; ++i) {
				xa = sili_ata_get_xfer(ap, atx);
				if (xa->tag < at->at_ncqdepth) {
					xa->state = ATA_S_COMPLETE;
					sili_ata_put_xfer(xa);
				}
			}
			if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) {
				cam_sim_set_max_tags(ap->ap_sim,
						     at->at_ncqdepth - 1);
			}
		}
	} else {
		devncqdepth = 0;
	}

	/*
	 * Make the model string a bit more presentable
	 */
	for (model_len = 40; model_len; --model_len) {
		if (at->at_identify.model[model_len-1] == ' ')
			continue;
		if (at->at_identify.model[model_len-1] == 0)
			continue;
		break;
	}

	model_len = sizeof(at->at_identify.model);
	model_id = at->at_identify.model;
	sili_strip_string(&model_id, &model_len);

	firmware_len = sizeof(at->at_identify.firmware);
	firmware_id = at->at_identify.firmware;
	sili_strip_string(&firmware_id, &firmware_len);

	serial_len = sizeof(at->at_identify.serial);
	serial_id = at->at_identify.serial;
	sili_strip_string(&serial_id, &serial_len);

	/*
	 * Generate informatiive strings.
	 *
	 * NOTE: We do not automatically set write caching, lookahead,
	 *	 or the security state for ATAPI devices.
	 */
	if (at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) {
		if (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE)
			wcstr = "enabled";
		else if (at->at_type == ATA_PORT_T_ATAPI)
			wcstr = "disabled";
		else
			wcstr = "enabling";
	} else {
		    wcstr = "notsupp";
	}

	if (at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) {
		if (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD)
			rastr = "enabled";
		else if (at->at_type == ATA_PORT_T_ATAPI)
			rastr = "disabled";
		else
			rastr = "enabling";
	} else {
		    rastr = "notsupp";
	}

	if (at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) {
		if (at->at_identify.securestatus & ATA_SECURE_FROZEN)
			scstr = "frozen";
		else if (at->at_type == ATA_PORT_T_ATAPI)
			scstr = "unfrozen";
		else if (SiliNoFeatures & (1 << ap->ap_num))
			scstr = "<disabled>";
		else
			scstr = "freezing";
	} else {
		    scstr = "notsupp";
	}

	kprintf("%s: Found %s \"%*.*s %*.*s\" serial=\"%*.*s\"\n"
		"%s: tags=%d/%d satacap=%04x satafea=%04x NCQ=%s "
		"capacity=%lld.%02dMB\n",

		ATANAME(ap, atx),
		type,
		model_len, model_len, model_id,
		firmware_len, firmware_len, firmware_id,
		serial_len, serial_len, serial_id,

		ATANAME(ap, atx),
		devncqdepth, ap->ap_sc->sc_ncmds,
		at->at_identify.satacap,
		at->at_identify.satafsup,
		(at->at_ncqdepth > 1 ? "YES" : "NO"),
		(long long)capacity_bytes / (1024 * 1024),
		(int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024)
	);
	kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n",
		ATANAME(ap, atx),
		at->at_identify.features85,
		at->at_identify.features86,
		at->at_identify.features87,
		wcstr,
		rastr,
		scstr
	);

	/*
	 * Additional type-specific probing
	 */
	switch(at->at_type) {
	case ATA_PORT_T_DISK:
		error = sili_cam_probe_disk(ap, atx);
		break;
	case ATA_PORT_T_ATAPI:
		error = sili_cam_probe_atapi(ap, atx);
		break;
	default:
		error = EIO;
		break;
	}
err:
	if (error) {
		at->at_probe = ATA_PROBE_FAILED;
		if (atx == NULL)
			ap->ap_probe = at->at_probe;
	} else {
		at->at_probe = ATA_PROBE_GOOD;
		if (atx == NULL)
			ap->ap_probe = at->at_probe;
	}
	return (error);
}
示例#7
0
/*
 * Convert the SCSI command in ccb to an ata_xfer command in xa
 * for ATA_PORT_T_DISK operations.  Set the completion function
 * to convert the response back, then dispatch to the OpenBSD SILI
 * layer.
 *
 * SILI DISK commands only support a limited command set, and we
 * fake additional commands to make it play nice with the CAM subsystem.
 */
static
void
sili_xpt_scsi_disk_io(struct sili_port *ap, struct ata_port *atx,
		      union ccb *ccb)
{
	struct ccb_hdr *ccbh;
	struct ccb_scsiio *csio;
	struct ata_xfer *xa;
	struct ata_port	*at;
	struct ata_fis_h2d *fis;
	struct ata_pass_12 *atp12;
	struct ata_pass_16 *atp16;
	scsi_cdb_t cdb;
	union scsi_data *rdata;
	int rdata_len;
	u_int64_t capacity;
	u_int64_t lba;
	u_int32_t count;

	ccbh = &ccb->csio.ccb_h;
	csio = &ccb->csio;
	at = atx ? atx : &ap->ap_ata[0];

	/*
	 * XXX not passing NULL at for direct attach!
	 */
	xa = sili_ata_get_xfer(ap, atx);
	rdata = (void *)csio->data_ptr;
	rdata_len = csio->dxfer_len;

	/*
	 * Build the FIS or process the csio to completion.
	 */
	cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);

	switch(cdb->generic.opcode) {
	case REQUEST_SENSE:
		/*
		 * Auto-sense everything, so explicit sense requests
		 * return no-sense.
		 */
		ccbh->status = CAM_SCSI_STATUS_ERROR;
		break;
	case INQUIRY:
		/*
		 * Inquiry supported features
		 *
		 * [opcode, byte2, page_code, length, control]
		 */
		if (cdb->inquiry.byte2 & SI_EVPD) {
			sili_xpt_page_inquiry(ap, at, ccb);
		} else {
			bzero(rdata, rdata_len);
			if (rdata_len < SHORT_INQUIRY_LENGTH) {
				ccbh->status = CAM_CCB_LEN_ERR;
				break;
			}
			if (rdata_len > sizeof(rdata->inquiry_data))
				rdata_len = sizeof(rdata->inquiry_data);
			rdata->inquiry_data.device = T_DIRECT;
			rdata->inquiry_data.version = SCSI_REV_SPC2;
			rdata->inquiry_data.response_format = 2;
			rdata->inquiry_data.additional_length = 32;
			bcopy("SATA    ", rdata->inquiry_data.vendor, 8);
			bcopy(at->at_identify.model,
			      rdata->inquiry_data.product,
			      sizeof(rdata->inquiry_data.product));
			bcopy(at->at_identify.firmware,
			      rdata->inquiry_data.revision,
			      sizeof(rdata->inquiry_data.revision));
			ccbh->status = CAM_REQ_CMP;
		}
		break;
	case READ_CAPACITY_16:
		if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) {
			ccbh->status = CAM_REQ_INVALID;
			break;
		}
		if (rdata_len < sizeof(rdata->read_capacity_data_16)) {
			ccbh->status = CAM_CCB_LEN_ERR;
			break;
		}
		/* fall through */
	case READ_CAPACITY:
		if (rdata_len < sizeof(rdata->read_capacity_data)) {
			ccbh->status = CAM_CCB_LEN_ERR;
			break;
		}

		capacity = at->at_capacity;

		bzero(rdata, rdata_len);
		if (cdb->generic.opcode == READ_CAPACITY) {
			rdata_len = sizeof(rdata->read_capacity_data);
			if (capacity > 0xFFFFFFFFU)
				capacity = 0xFFFFFFFFU;
			bzero(&rdata->read_capacity_data, rdata_len);
			scsi_ulto4b((u_int32_t)capacity - 1,
				    rdata->read_capacity_data.addr);
			scsi_ulto4b(512, rdata->read_capacity_data.length);
		} else {
			rdata_len = sizeof(rdata->read_capacity_data_16);
			bzero(&rdata->read_capacity_data_16, rdata_len);
			scsi_u64to8b(capacity - 1,
				     rdata->read_capacity_data_16.addr);
			scsi_ulto4b(512, rdata->read_capacity_data_16.length);
		}
		ccbh->status = CAM_REQ_CMP;
		break;
	case SYNCHRONIZE_CACHE:
		/*
		 * Synchronize cache.  Specification says this can take
		 * greater then 30 seconds so give it at least 45.
		 */
		fis = xa->fis;
		fis->flags = ATA_H2D_FLAGS_CMD;
		fis->command = ATA_C_FLUSH_CACHE;
		fis->device = 0;
		if (xa->timeout < 45000)
			xa->timeout = 45000;
		xa->datalen = 0;
		xa->flags = ATA_F_READ;
		xa->complete = sili_ata_complete_disk_synchronize_cache;
		break;
	case TEST_UNIT_READY:
	case START_STOP_UNIT:
	case PREVENT_ALLOW:
		/*
		 * Just silently return success
		 */
		ccbh->status = CAM_REQ_CMP;
		rdata_len = 0;
		break;
	case ATA_PASS_12:
		atp12 = &cdb->ata_pass_12;
		fis = xa->fis;
		/*
		 * Figure out the flags to be used, depending on the
		 * direction of the CAM request.
		 */
		switch (ccbh->flags & CAM_DIR_MASK) {
		case CAM_DIR_IN:
			xa->flags = ATA_F_READ;
			break;
		case CAM_DIR_OUT:
			xa->flags = ATA_F_WRITE;
			break;
		default:
			xa->flags = 0;
		}
		xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
		xa->data = csio->data_ptr;
		xa->datalen = csio->dxfer_len;
		xa->complete = sili_ata_complete_disk_rw;
		xa->timeout = ccbh->timeout;

		/*
		 * Populate the fis from the information we received through CAM
		 * ATA passthrough.
		 */
		fis->flags = ATA_H2D_FLAGS_CMD;	/* maybe also atp12->flags ? */
		fis->features = atp12->features;
		fis->sector_count = atp12->sector_count;
		fis->lba_low = atp12->lba_low;
		fis->lba_mid = atp12->lba_mid;
		fis->lba_high = atp12->lba_high;
		fis->device = atp12->device;	/* maybe always 0? */
		fis->command = atp12->command;
		fis->control = atp12->control;

		/*
		 * Mark as in progress so it is sent to the device.
		 */
		ccbh->status = CAM_REQ_INPROG;
		break;
	case ATA_PASS_16:
		atp16 = &cdb->ata_pass_16;
		fis = xa->fis;
		/*
		 * Figure out the flags to be used, depending on the direction of the
		 * CAM request.
		 */
		switch (ccbh->flags & CAM_DIR_MASK) {
		case CAM_DIR_IN:
			xa->flags = ATA_F_READ;
			break;
		case CAM_DIR_OUT:
			xa->flags = ATA_F_WRITE;
			break;
		default:
			xa->flags = 0;
		}
		xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
		xa->data = csio->data_ptr;
		xa->datalen = csio->dxfer_len;
		xa->complete = sili_ata_complete_disk_rw;
		xa->timeout = ccbh->timeout;

		/*
		 * Populate the fis from the information we received through CAM
		 * ATA passthrough.
		 */
		fis->flags = ATA_H2D_FLAGS_CMD;	/* maybe also atp16->flags ? */
		fis->features = atp16->features;
		fis->features_exp = atp16->features_ext;
		fis->sector_count = atp16->sector_count;
		fis->sector_count_exp = atp16->sector_count_ext;
		fis->lba_low = atp16->lba_low;
		fis->lba_low_exp = atp16->lba_low_ext;
		fis->lba_mid = atp16->lba_mid;
		fis->lba_mid_exp = atp16->lba_mid_ext;
		fis->lba_high = atp16->lba_high;
		fis->lba_mid_exp = atp16->lba_mid_ext;
		fis->device = atp16->device;	/* maybe always 0? */
		fis->command = atp16->command;

		/*
		 * Mark as in progress so it is sent to the device.
		 */
		ccbh->status = CAM_REQ_INPROG;
		break;
	default:
		switch(cdb->generic.opcode) {
		case READ_6:
			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
			xa->flags = ATA_F_READ;
			break;
		case READ_10:
			lba = scsi_4btoul(cdb->rw_10.addr);
			count = scsi_2btoul(cdb->rw_10.length);
			xa->flags = ATA_F_READ;
			break;
		case READ_12:
			lba = scsi_4btoul(cdb->rw_12.addr);
			count = scsi_4btoul(cdb->rw_12.length);
			xa->flags = ATA_F_READ;
			break;
		case READ_16:
			lba = scsi_8btou64(cdb->rw_16.addr);
			count = scsi_4btoul(cdb->rw_16.length);
			xa->flags = ATA_F_READ;
			break;
		case WRITE_6:
			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
			xa->flags = ATA_F_WRITE;
			break;
		case WRITE_10:
			lba = scsi_4btoul(cdb->rw_10.addr);
			count = scsi_2btoul(cdb->rw_10.length);
			xa->flags = ATA_F_WRITE;
			break;
		case WRITE_12:
			lba = scsi_4btoul(cdb->rw_12.addr);
			count = scsi_4btoul(cdb->rw_12.length);
			xa->flags = ATA_F_WRITE;
			break;
		case WRITE_16:
			lba = scsi_8btou64(cdb->rw_16.addr);
			count = scsi_4btoul(cdb->rw_16.length);
			xa->flags = ATA_F_WRITE;
			break;
		default:
			ccbh->status = CAM_REQ_INVALID;
			break;
		}
		if (ccbh->status != CAM_REQ_INPROG)
			break;

		fis = xa->fis;
		fis->flags = ATA_H2D_FLAGS_CMD;
		fis->lba_low = (u_int8_t)lba;
		fis->lba_mid = (u_int8_t)(lba >> 8);
		fis->lba_high = (u_int8_t)(lba >> 16);
		fis->device = ATA_H2D_DEVICE_LBA;

		/*
		 * NCQ only for direct-attached disks, do not currently
		 * try to use NCQ with port multipliers.
		 *
		 * XXX fixme SII chip can do NCQ w/ port multipliers.
		 */
		if (at->at_ncqdepth > 1 &&
		    at->at_type == ATA_PORT_T_DISK &&
		    (ap->ap_sc->sc_flags & SILI_F_NCQ) &&
		    (ccbh->flags & CAM_POLLED) == 0) {
			/*
			 * Use NCQ - always uses 48 bit addressing
			 */
			xa->flags |= ATA_F_NCQ;
			fis->command = (xa->flags & ATA_F_WRITE) ?
					ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA;
			fis->lba_low_exp = (u_int8_t)(lba >> 24);
			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
			fis->lba_high_exp = (u_int8_t)(lba >> 40);
			fis->sector_count = xa->tag << 3;
			fis->features = (u_int8_t)count;
			fis->features_exp = (u_int8_t)(count >> 8);
		} else if (count > 0x100 || lba > 0x0FFFFFFFU) {