⛏️ index : haiku.git

/*
 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
 * Copyright 2008, Marcus Overhagen.
 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
 * Copyright 2002-2003, Thomas Kurschel.
 *
 * Distributed under the terms of the MIT License.
 */

#include "ATAPrivate.h"


ATADevice::ATADevice(ATAChannel *channel, uint8 index)
	:
	fChannel(channel),
	fRegisterMask(0),
	fUseDMA(channel->UseDMA()),
	fDMAMode(0),
	fDMAFailures(0),
	fTotalSectors(0),
	fBlockSize(512),
	fPhysicalBlockSize(512),
	fBlockOffset(0),
	fIndex(index),
	fUse48Bits(false)
{
	memset(&fInfoBlock, 0, sizeof(fInfoBlock));
	memset(&fTaskFile, 0, sizeof(fTaskFile));
}


ATADevice::~ATADevice()
{
}


status_t
ATADevice::TestUnitReady(ATARequest *request)
{
	TRACE_FUNCTION("%p\n", request);

	fRegisterMask = 0;
	fTaskFile.write.command = ATA_COMMAND_GET_MEDIA_STATUS;

	request->SetTimeout(15 * 1000 * 1000);
	status_t result = fChannel->SendRequest(request, ATA_DEVICE_READY_REQUIRED);
	if (result != B_OK) {
		TRACE_ERROR("failed to send test unit ready request\n");
		return result;
	}

	return fChannel->FinishRequest(request, ATA_WAIT_FINISH
		| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_NO_MEDIA | ATA_ERROR_ABORTED
		| ATA_ERROR_MEDIA_CHANGE_REQUESTED | ATA_ERROR_MEDIUM_CHANGED);
}


status_t
ATADevice::SynchronizeCache(ATARequest *request)
{
	TRACE_FUNCTION("%p\n", request);

	// we should also ask for FLUSH CACHE support, but everyone denies it
	// (looks like they cheat to gain some performance advantage, but
	//  that's pretty useless: everyone does it...)
	if (!fInfoBlock.write_cache_supported)
		return B_OK;

	fRegisterMask = 0;
	fTaskFile.lba.command
		= fUse48Bits ? ATA_COMMAND_FLUSH_CACHE_EXT : ATA_COMMAND_FLUSH_CACHE;

	request->SetTimeout(60 * 1000 * 1000);
	status_t result = fChannel->SendRequest(request, ATA_DEVICE_READY_REQUIRED);
	if (result != B_OK) {
		TRACE_ERROR("failed to send synchronize cache request\n");
		return result;
	}

	return fChannel->FinishRequest(request, ATA_WAIT_FINISH
		| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED);
}


status_t
ATADevice::Eject(ATARequest *request)
{
	TRACE_FUNCTION("%p\n", request);

	fRegisterMask = 0;
	fTaskFile.lba.command = ATA_COMMAND_MEDIA_EJECT;

	request->SetTimeout(15 * 1000 * 1000);
	status_t result = fChannel->SendRequest(request, ATA_DEVICE_READY_REQUIRED);
	if (result != B_OK) {
		TRACE_ERROR("failed to send eject request\n");
		return result;
	}

	return fChannel->FinishRequest(request, ATA_WAIT_FINISH
		| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED | ATA_ERROR_NO_MEDIA);
}


status_t
ATADevice::Inquiry(ATARequest *request)
{
	TRACE_FUNCTION("%p\n", request);

	scsi_ccb *ccb = request->CCB();
	scsi_cmd_inquiry *command = (scsi_cmd_inquiry *)ccb->cdb;
	if (command->evpd || command->page_code) {
		request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
		return B_ERROR;
	}

	scsi_res_inquiry data;
	memset(&data, 0, sizeof(data));

	data.device_type = IsATAPI()
		? fInfoBlock.word_0.atapi.command_packet_set : scsi_dev_direct_access;
	data.device_qualifier = scsi_periph_qual_connected;

	data.device_type_modifier = 0;
	data.removable_medium = fInfoBlock.word_0.ata.removable_media_device;

	data.ansi_version = 2;
	data.ecma_version = 0;
	data.iso_version = 0;

	data.response_data_format = 2;
	data.term_iop = false;
		// to be changed if we support TERM I/O

	data.additional_length = sizeof(scsi_res_inquiry) - 4;

	data.soft_reset = false;
	data.cmd_queue = 0;
	data.linked = false;

	// these values are free-style
	data.sync = false;
	data.write_bus16 = true;
	data.write_bus32 = false;

	data.relative_address = false;

	// the following fields are *much* to small, sigh...
	memcpy(data.vendor_ident, fInfoBlock.model_number,
		sizeof(data.vendor_ident));
	swap_words(data.vendor_ident, sizeof(data.vendor_ident));

	memcpy(data.product_ident, fInfoBlock.model_number + 8,
		sizeof(data.product_ident));
	swap_words(data.product_ident, sizeof(data.product_ident));

	memcpy(data.product_rev, "    ", sizeof(data.product_rev));

	uint32 allocationLength = command->allocation_length;
	copy_sg_data(ccb, 0, allocationLength, &data, sizeof(data), false);
	ccb->data_resid = ccb->data_length - MIN(MIN(sizeof(data),
		allocationLength), ccb->data_length);
	return B_OK;
}


status_t
ATADevice::ReadCapacity(ATARequest *request)
{
	TRACE_FUNCTION("%p\n", request);

	scsi_ccb *ccb = request->CCB();
	scsi_cmd_read_capacity *command = (scsi_cmd_read_capacity *)ccb->cdb;
	if (command->pmi || command->lba) {
		request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
		return B_ERROR;
	}

	scsi_res_read_capacity data;
	memset(&data, 0, sizeof(data));

	data.block_size = B_HOST_TO_BENDIAN_INT32(fBlockSize);

	if (fTotalSectors <= UINT_MAX) {
		uint32 lastBlock = fTotalSectors - 1;
		data.lba = B_HOST_TO_BENDIAN_INT32(lastBlock);
	} else
		data.lba = UINT_MAX;
	TRACE("returning last block: %" B_PRIu32 "\n",
		B_BENDIAN_TO_HOST_INT32(data.lba));

	copy_sg_data(ccb, 0, ccb->data_length, &data, sizeof(data), false);
	ccb->data_resid = MAX(ccb->data_length - sizeof(data), 0);
	return B_OK;
}


status_t
ATADevice::ReadCapacity16(ATARequest *request)
{
	TRACE_FUNCTION("%p\n", request);

	scsi_ccb *ccb = request->CCB();
	scsi_cmd_read_capacity_long *command
		= (scsi_cmd_read_capacity_long *)ccb->cdb;
	if (command->pmi || command->lba) {
		request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD);
		return B_ERROR;
	}

	uint32 allocationLength = B_BENDIAN_TO_HOST_INT32(command->alloc_length);

	scsi_res_read_capacity_long data;
	memset(&data, 0, sizeof(data));

	data.block_size = B_HOST_TO_BENDIAN_INT32(fBlockSize);

	uint64 lastBlock = fTotalSectors - 1;
	data.lba = B_HOST_TO_BENDIAN_INT64(lastBlock);
	TRACE("returning last block: %" B_PRIu64 "\n",
		B_BENDIAN_TO_HOST_INT64(data.lba));

	size_t copySize = min_c(allocationLength, sizeof(data));

	copy_sg_data(ccb, 0, ccb->data_length, &data, copySize, false);
	ccb->data_resid = MAX(ccb->data_length - copySize, 0);
	return B_OK;
}


status_t
ATADevice::ExecuteIO(ATARequest *request)
{
	TRACE_FUNCTION("%p\n", request);

	scsi_ccb *ccb = request->CCB();
	request->SetDevice(this);

	// ATA devices have one LUN only
	if (ccb->target_lun != 0) {
		TRACE_ERROR("invalid target lun %d for ATA device\n", ccb->target_lun);
		request->SetStatus(SCSI_SEL_TIMEOUT);
		return B_BAD_INDEX;
	}

	TRACE("request: 0x%02x\n", ccb->cdb[0]);

	switch (ccb->cdb[0]) {
		case SCSI_OP_TEST_UNIT_READY:
			return TestUnitReady(request);

		case SCSI_OP_FORMAT: /* FORMAT UNIT */
			// we could forward ccb to disk, but modern disks cannot
			// be formatted anyway, so we just refuse ccb
			// (exceptions are removable media devices, but to my knowledge
			// they don't have to be formatted as well)
			request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
			return B_ERROR;

		case SCSI_OP_INQUIRY:
 			return Inquiry(request);

		case SCSI_OP_START_STOP:
		{
			scsi_cmd_ssu *command = (scsi_cmd_ssu *)ccb->cdb;

			// with no LoEj bit set, we should only allow/deny further access
			// we ignore that (unsupported for ATA)
			// with LoEj bit set, we should additionally either load or eject
			// the medium (start = 0 - eject; start = 1 - load)

			if (!command->start) {
				// we must always flush cache if start = 0
				SynchronizeCache(request);
			}

			if (command->load_eject) {
				if (!command->start)
					return Eject(request);
				else {
					request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST,
						SCSIS_ASC_PARAM_NOT_SUPPORTED);
					return B_ERROR;
				}
			}

			return B_OK;
		}

		case SCSI_OP_READ_CAPACITY:
			return ReadCapacity(request);

		case SCSI_OP_SERVICE_ACTION_IN:
			if ((ccb->cdb[1] & 0x1f) == SCSI_SAI_READ_CAPACITY_16)
				return ReadCapacity16(request);
			break;

		case SCSI_OP_SYNCHRONIZE_CACHE:
			// we ignore range and immediate bit, we always immediately
			// flush everything
			return SynchronizeCache(request);

		// sadly, there are two possible read/write operation codes;
		// at least, the third one, read/write(12), is not valid for DAS
		case SCSI_OP_READ_6:
		case SCSI_OP_WRITE_6:
		{
			scsi_cmd_rw_6 *command = (scsi_cmd_rw_6 *)ccb->cdb;
			uint32 address = ((uint32)command->high_lba << 16)
				| ((uint32)command->mid_lba << 8) | (uint32)command->low_lba;

			request->SetIsWrite(command->opcode == SCSI_OP_WRITE_6);
			return ExecuteReadWrite(request, address, command->length != 0
				? command->length : 256);
		}

		case SCSI_OP_READ_10:
		case SCSI_OP_WRITE_10:
		{
			scsi_cmd_rw_10 *command = (scsi_cmd_rw_10 *)ccb->cdb;
			uint32 address = B_BENDIAN_TO_HOST_INT32(command->lba);
			uint32 sectorCount = B_BENDIAN_TO_HOST_INT16(command->length);

			request->SetIsWrite(command->opcode == SCSI_OP_WRITE_10);
			if (sectorCount > 0)
				return ExecuteReadWrite(request, address, sectorCount);
			else {
				// we cannot transfer zero blocks (apart from LBA48)
				request->SetStatus(SCSI_REQ_CMP);
				return B_OK;
			}
		}

		case SCSI_OP_READ_12:
		case SCSI_OP_WRITE_12:
		{
			scsi_cmd_rw_12 *command = (scsi_cmd_rw_12 *)ccb->cdb;
			uint32 address = B_BENDIAN_TO_HOST_INT32(command->lba);
			uint32 sectorCount = B_BENDIAN_TO_HOST_INT32(command->length);

			request->SetIsWrite(command->opcode == SCSI_OP_WRITE_12);
			if (sectorCount > 0)
				return ExecuteReadWrite(request, address, sectorCount);
			else {
				// we cannot transfer zero blocks (apart from LBA48)
				request->SetStatus(SCSI_REQ_CMP);
				return B_OK;
			}
		}

		case SCSI_OP_READ_16:
		case SCSI_OP_WRITE_16:
		{
			scsi_cmd_rw_16 *command = (scsi_cmd_rw_16 *)ccb->cdb;
			uint64 address = B_BENDIAN_TO_HOST_INT64(command->lba);
			uint32 sectorCount = B_BENDIAN_TO_HOST_INT32(command->length);

			request->SetIsWrite(command->opcode == SCSI_OP_WRITE_16);
			if (sectorCount > 0)
				return ExecuteReadWrite(request, address, sectorCount);
			else {
				// we cannot transfer zero blocks (apart from LBA48)
				request->SetStatus(SCSI_REQ_CMP);
				return B_OK;
			}
		}
	}

	TRACE("command not implemented\n");
	request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_OPCODE);
	return B_ERROR;
}


void
ATADevice::GetRestrictions(bool *noAutoSense, uint32 *maxBlocks)
{
	if (IsATAPI())
		*noAutoSense = true;
	else {
		if (fUse48Bits)
			*maxBlocks = 0xffff;
		else
			*maxBlocks = 0x100;
	}
}


status_t
ATADevice::Control(uint32 opcode, void *buffer, size_t length)
{
	if (opcode == B_GET_DEVICE_NAME) {
		// Swap words
		char name[sizeof(fInfoBlock.model_number)];
		memcpy(name, fInfoBlock.model_number, sizeof(name));
		swap_words(name, sizeof(name));

		// Remove trailing spaces
		int32 nameLength = sizeof(name) - 2;
		while (nameLength > 0 && name[nameLength - 1] == ' ')
			nameLength--;

		// TODO: make string prettier, ie. "WDC" -> "Western Digital", ...
		return user_strlcpy((char*)buffer, name,
			min_c((size_t)nameLength + 1, length)) >= 0 ? B_OK : B_BAD_ADDRESS;
	}
	return B_BAD_VALUE;
}


status_t
ATADevice::Select()
{
	status_t err = fChannel->SelectDevice(fIndex);
#if 1
    // for debugging only
	if (fChannel->SelectedDevice() != fIndex) {
		TRACE_ERROR("device %d not selected!\n", fIndex);
		return B_ERROR;
	}
#endif
	return err;
}


status_t
ATADevice::SetFeature(int feature)
{
	TRACE("device_set_feature: feature %d\n", feature);

	ATARequest request(false);
	request.SetDevice(this);
	request.SetTimeout(1 * 1000 * 1000);

	fTaskFile.write.features = feature;
	fTaskFile.write.command = ATA_COMMAND_SET_FEATURES;
	fRegisterMask = ATA_MASK_FEATURES;

	status_t result = fChannel->SendRequest(&request, ATA_DEVICE_READY_REQUIRED);
	if (result != B_OK) {
		TRACE_ERROR("sending set feature request failed\n");
		return result;
	}

	result = fChannel->FinishRequest(&request,
		ATA_WAIT_FINISH | ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED);
	if (result != B_OK) {
		TRACE_ERROR("set feature request failed\n");
		return result;
	}

	return B_OK;
}


status_t
ATADevice::DisableCommandQueueing()
{
	if (!fInfoBlock.read_write_dma_queued_supported)
		return B_OK;

	if (fInfoBlock.release_interrupt_supported) {
		status_t result = SetFeature(
			ATA_COMMAND_SET_FEATURES_DISABLE_RELEASE_INT);
		if (result != B_OK) {
			TRACE_ERROR("failed to disable release interrupt\n");
			return result;
		}
	}

	if (fInfoBlock.service_interrupt_supported) {
		status_t result = SetFeature(
			ATA_COMMAND_SET_FEATURES_DISABLE_SERVICE_INT);
		if (result != B_OK) {
			TRACE_ERROR("failed to disable service interrupt\n");
			return result;
		}
	}

	return B_OK;
}


status_t
ATADevice::ConfigureDMA()
{
	if (!fUseDMA)
		return B_OK;

	if (!fInfoBlock.dma_supported) {
		TRACE_ALWAYS("DMA not supported by device\n");
		fUseDMA = false;
		return B_OK;
	}

	#define CHECK_DMA_MODE(element, mode) \
		if (fInfoBlock.element) { \
			fDMAMode = mode; \
			modeCount++; \
		}

	uint32 modeCount = 0;

	CHECK_DMA_MODE(multiword_dma_0_selected, 0x00);
	CHECK_DMA_MODE(multiword_dma_1_selected, 0x01);
	CHECK_DMA_MODE(multiword_dma_2_selected, 0x02);

	if (fInfoBlock.word_88_valid) {
		CHECK_DMA_MODE(ultra_dma_0_selected, 0x10);
		CHECK_DMA_MODE(ultra_dma_1_selected, 0x11);
		CHECK_DMA_MODE(ultra_dma_2_selected, 0x12);
		CHECK_DMA_MODE(ultra_dma_3_selected, 0x13);
		CHECK_DMA_MODE(ultra_dma_4_selected, 0x14);
		CHECK_DMA_MODE(ultra_dma_5_selected, 0x15);
		CHECK_DMA_MODE(ultra_dma_6_selected, 0x16);
	}

	#undef CHECK_DMA_MODE

	if (modeCount != 1) {
		TRACE_ERROR("more than one DMA mode selected, not using DMA\n");
		fUseDMA = false;
		return B_OK;
	}

	TRACE_ALWAYS("using DMA mode 0x%02x\n", fDMAMode);
	return B_OK;
}


status_t
ATADevice::Configure()
{
	// warning: ata == 0 means "this is ata"...
	if (fInfoBlock.word_0.ata.ata_device != ATA_WORD_0_ATA_DEVICE) {
		// CF has either magic header or CFA bit set
		// we merge it to "CFA bit set" for easier (later) testing
		if (fInfoBlock.word_0.raw == ATA_WORD_0_CFA_MAGIC)
			fInfoBlock.compact_flash_assoc_supported = true;
		else {
			TRACE_ERROR("infoblock indicates non-ata device\n");
			return B_ERROR;
		}
	}

	if (!fInfoBlock.lba_supported || (fInfoBlock.lba_sector_count == 0
		&& fInfoBlock.lba48_sector_count == 0)) {
		TRACE_ERROR("non-lba devices not supported\n");
		return B_ERROR;
	}

	fTotalSectors = fInfoBlock.SectorCount(fUse48Bits, false);
	fBlockSize = fInfoBlock.SectorSize();
	fPhysicalBlockSize = fInfoBlock.PhysicalSectorSize();
	fBlockOffset = fInfoBlock.BlockOffset();

	fTaskFile.lba.mode = ATA_MODE_LBA;
	fTaskFile.lba.device = fIndex;

	status_t result = ConfigureDMA();
	if (result != B_OK)
		return result;

	result = DisableCommandQueueing();
	if (result != B_OK)
		return result;

	return B_OK;
}


status_t
ATADevice::Identify()
{
	snprintf(fDebugContext, sizeof(fDebugContext), "%s %" B_PRIu32 "-%u",
		IsATAPI() ? "pi" : "", fChannel->ChannelID(), fIndex);

	ATARequest request(false);
	request.SetDevice(this);
	request.SetTimeout(20 * 1000 * 1000);

	fRegisterMask = 0;
	fTaskFile.write.command = IsATAPI() ? ATA_COMMAND_IDENTIFY_PACKET_DEVICE
		: ATA_COMMAND_IDENTIFY_DEVICE;

	if (fChannel->SendRequest(&request,
			IsATAPI() ? 0 : ATA_DEVICE_READY_REQUIRED) != B_OK) {
		TRACE_ERROR("sending identify request failed\n");
		return B_ERROR;
	}

	if (fChannel->Wait(ATA_STATUS_BUSY | ATA_STATUS_DATA_REQUEST, 0,
			ATA_WAIT_ANY_BIT, 100 * 1000) != B_OK) {
		TRACE_ALWAYS("no data request and not busy within 100ms, assuming "
			"no device present\n");
		return B_TIMED_OUT;
	}

	if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY,
			ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT,
			IsATAPI() ? 20 * 1000 * 1000 : 500 * 1000) != B_OK) {
		TRACE_ERROR("timeout waiting for identify request\n");
		return B_TIMED_OUT;
	}

	// get the infoblock
	fChannel->ReadPIO((uint8 *)&fInfoBlock, sizeof(fInfoBlock));

	if (fChannel->WaitDataRequest(false) != B_OK) {
		TRACE_ERROR("device disagrees on info block length\n");
		return B_ERROR;
	}

	if (fChannel->FinishRequest(&request,
			ATA_WAIT_FINISH | (IsATAPI() ? 0 : ATA_DEVICE_READY_REQUIRED),
			ATA_ERROR_ABORTED) != B_OK) {
		TRACE_ERROR("failed to finish identify request\n");
		return B_ERROR;
	}

	if (1) {
		// print device information
		char modelNumber[sizeof(fInfoBlock.model_number) + 1];
		char serialNumber[sizeof(fInfoBlock.serial_number) + 1];
		char firmwareRev[sizeof(fInfoBlock.firmware_revision) + 1];
		strlcpy(modelNumber, fInfoBlock.model_number, sizeof(modelNumber));
		strlcpy(serialNumber, fInfoBlock.serial_number, sizeof(serialNumber));
		strlcpy(firmwareRev, fInfoBlock.firmware_revision, sizeof(firmwareRev));
		swap_words(modelNumber, sizeof(modelNumber) - 1);
		swap_words(serialNumber, sizeof(serialNumber) - 1);
		swap_words(firmwareRev, sizeof(firmwareRev) - 1);
		TRACE_ALWAYS("model number: %s\n", modelNumber);
		TRACE_ALWAYS("serial number: %s\n", serialNumber);
  		TRACE_ALWAYS("firmware rev.: %s\n", firmwareRev);
	}

	return B_OK;
}


status_t
ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address,
	uint32 sectorCount)
{
	request->SetUseDMA(fUseDMA && fChannel->PrepareDMA(request) == B_OK);
	if (!request->UseDMA())
		request->PrepareSGInfo();

	request->SetBytesLeft(sectorCount * fBlockSize);
	if (_FillTaskFile(request, address) != B_OK) {
		TRACE_ERROR("failed to setup transfer request\n");
		if (request->UseDMA())
			fChannel->FinishDMA();
		return B_ERROR;
	}

	status_t result = fChannel->SendRequest(request,
		IsATAPI() ? 0 : ATA_DEVICE_READY_REQUIRED);
	if (result != B_OK) {
		TRACE_ERROR("failed to send transfer request\n");
		if (request->UseDMA())
			fChannel->FinishDMA();
		return result;
	}

	if (request->UseDMA()) {
		fChannel->PrepareWaitingForInterrupt();
		fChannel->StartDMA();

		result = fChannel->WaitForInterrupt(request->Timeout());
		status_t dmaResult = fChannel->FinishDMA();
		if (result == B_OK && dmaResult == B_OK) {
			fDMAFailures = 0;
			request->CCB()->data_resid = 0;
		} else {
			if (dmaResult != B_OK) {
				request->SetSense(SCSIS_KEY_HARDWARE_ERROR,
					SCSIS_ASC_LUN_COM_FAILURE);
				fDMAFailures++;
				if (fDMAFailures >= ATA_MAX_DMA_FAILURES) {
					TRACE_ALWAYS("disabling DMA after %u failures\n",
						fDMAFailures);
					fUseDMA = false;
				}
			} else {
				// timeout
				request->SetStatus(SCSI_CMD_TIMEOUT);
			}
		}
	} else {
		if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, 0, ATA_CHECK_ERROR_BIT
				| ATA_CHECK_DEVICE_FAULT, request->Timeout()) != B_OK) {
			TRACE_ERROR("timeout waiting for device to request data\n");
			request->SetStatus(SCSI_CMD_TIMEOUT);
			return B_TIMED_OUT;
		}

		if (fChannel->ExecutePIOTransfer(request) != B_OK) {
			TRACE_ERROR("executing pio transfer failed\n");
			request->SetStatus(SCSI_SEQUENCE_FAIL);
		}
	}

	return fChannel->FinishRequest(request, ATA_WAIT_FINISH
		| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ALL);
}


status_t
ATADevice::_FillTaskFile(ATARequest *request, uint64 address)
{
	// list of LBA48 opcodes
	static const uint8 s48BitCommands[2][2] = {
		{ ATA_COMMAND_READ_SECTORS_EXT, ATA_COMMAND_WRITE_SECTORS_EXT },
		{ ATA_COMMAND_READ_DMA_EXT, ATA_COMMAND_WRITE_DMA_EXT }
	};

	// list of normal LBA opcodes
	static const uint8 s28BitCommands[2][2] = {
		{ ATA_COMMAND_READ_SECTORS, ATA_COMMAND_WRITE_SECTORS },
		{ ATA_COMMAND_READ_DMA, ATA_COMMAND_WRITE_DMA }
	};

	uint32 sectorCount = *request->BytesLeft() / fBlockSize;
	TRACE("about to transfer %lu sectors\n", sectorCount);

	if (fUse48Bits
		&& (address + sectorCount > 0xfffffff || sectorCount > 0x100)) {
		// use LBA48 only if necessary
		if (sectorCount > 0xffff) {
			TRACE_ERROR("invalid sector count %lu\n", sectorCount);
			request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST,
				SCSIS_ASC_INV_CDB_FIELD);
			return B_ERROR;
		}

		fRegisterMask = ATA_MASK_SECTOR_COUNT_48
			| ATA_MASK_LBA_LOW_48
			| ATA_MASK_LBA_MID_48
			| ATA_MASK_LBA_HIGH_48;

		fTaskFile.lba48.sector_count_0_7 = sectorCount & 0xff;
		fTaskFile.lba48.sector_count_8_15 = (sectorCount >> 8) & 0xff;
		fTaskFile.lba48.lba_0_7 = address & 0xff;
		fTaskFile.lba48.lba_8_15 = (address >> 8) & 0xff;
		fTaskFile.lba48.lba_16_23 = (address >> 16) & 0xff;
		fTaskFile.lba48.lba_24_31 = (address >> 24) & 0xff;
		fTaskFile.lba48.lba_32_39 = (address >> 32) & 0xff;
		fTaskFile.lba48.lba_40_47 = (address >> 40) & 0xff;
		fTaskFile.lba48.command = s48BitCommands[request->UseDMA()
			? 1 : 0][request->IsWrite() ? 1 : 0];
	} else {
		// normal LBA
		if (sectorCount > 0x100) {
			TRACE_ERROR("invalid sector count %lu\n", sectorCount);
			request->SetSense(SCSIS_KEY_ILLEGAL_REQUEST,
				SCSIS_ASC_INV_CDB_FIELD);
			return B_ERROR;
		}

		fRegisterMask = ATA_MASK_SECTOR_COUNT
			| ATA_MASK_LBA_LOW
			| ATA_MASK_LBA_MID
			| ATA_MASK_LBA_HIGH
			| ATA_MASK_DEVICE_HEAD;

		fTaskFile.lba.sector_count = sectorCount & 0xff;
		fTaskFile.lba.lba_0_7 = address & 0xff;
		fTaskFile.lba.lba_8_15 = (address >> 8) & 0xff;
		fTaskFile.lba.lba_16_23 = (address >> 16) & 0xff;
		fTaskFile.lba.lba_24_27 = (address >> 24) & 0xf;
		fTaskFile.lba.command = s28BitCommands[request->UseDMA()
			? 1 : 0][request->IsWrite() ? 1 : 0];
	}

	return B_OK;
}