16bb01f71SMichael Lotz/*
26bb01f71SMichael Lotz * Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
36bb01f71SMichael Lotz * Copyright 2008, Marcus Overhagen.
46bb01f71SMichael Lotz * Copyright 2004-2008, Axel D��rfler, axeld@pinc-software.de.
56bb01f71SMichael Lotz * Copyright 2002-2003, Thomas Kurschel.
66bb01f71SMichael Lotz *
76bb01f71SMichael Lotz * Distributed under the terms of the MIT License.
86bb01f71SMichael Lotz */
96bb01f71SMichael Lotz
10bf9a3835SMichael Lotz#include "ATAPrivate.h"
11bf9a3835SMichael Lotz
12bf9a3835SMichael LotzATAPIDevice::ATAPIDevice(ATAChannel *channel, uint8 index)
1322805aaeSMichael Lotz	:	ATADevice(channel, index)
14bf9a3835SMichael Lotz{
15bf9a3835SMichael Lotz}
16bf9a3835SMichael Lotz
17bf9a3835SMichael Lotz
18bf9a3835SMichael LotzATAPIDevice::~ATAPIDevice()
19bf9a3835SMichael Lotz{
20bf9a3835SMichael Lotz}
21bf9a3835SMichael Lotz
22bf9a3835SMichael Lotz
2322805aaeSMichael Lotzstatus_t
2422805aaeSMichael LotzATAPIDevice::SendPacket(ATARequest *request)
2522805aaeSMichael Lotz{
2622805aaeSMichael Lotz	TRACE_FUNCTION("%p\n", request);
2722805aaeSMichael Lotz
2822805aaeSMichael Lotz	// only READ/WRITE commands can use DMA
2922805aaeSMichael Lotz	// (the device may support it always, but IDE controllers don't
3022805aaeSMichael Lotz	// report how much data is transmitted, and this information is
3122805aaeSMichael Lotz	// crucial for the SCSI protocol)
3222805aaeSMichael Lotz	// special offer: let READ_CD commands use DMA too
3322805aaeSMichael Lotz	uint8 command = fPacket[0];
341ce4890bSJérôme Duval	request->SetUseDMA(UseDMA() && request->CCB()->sg_list != NULL
3522805aaeSMichael Lotz		&& (command == SCSI_OP_READ_6 || command == SCSI_OP_WRITE_6
3622805aaeSMichael Lotz		|| command == SCSI_OP_READ_10 || command == SCSI_OP_WRITE_10
3722805aaeSMichael Lotz		|| command == SCSI_OP_READ_12 || command == SCSI_OP_WRITE_12
3822805aaeSMichael Lotz		|| command == SCSI_OP_READ_CD)
3922805aaeSMichael Lotz		&& fChannel->PrepareDMA(request) == B_OK);
4022805aaeSMichael Lotz	TRACE("using dma: %s\n", request->UseDMA() ? "yes" : "no");
4122805aaeSMichael Lotz
4222805aaeSMichael Lotz	if (!request->UseDMA())
4322805aaeSMichael Lotz		request->PrepareSGInfo();
4422805aaeSMichael Lotz
4522805aaeSMichael Lotz	if (_FillTaskFilePacket(request) != B_OK) {
4622805aaeSMichael Lotz		TRACE_ERROR("failed to setup transfer request\n");
4722805aaeSMichael Lotz		if (request->UseDMA())
4822805aaeSMichael Lotz			fChannel->FinishDMA();
4922805aaeSMichael Lotz		return B_ERROR;
5022805aaeSMichael Lotz	}
5122805aaeSMichael Lotz
52827f849aSMichael Lotz	status_t result = fChannel->SendRequest(request, 0);
5322805aaeSMichael Lotz	if (result != B_OK) {
5422805aaeSMichael Lotz		TRACE_ERROR("failed to send packet request\n");
5522805aaeSMichael Lotz		if (request->UseDMA())
5622805aaeSMichael Lotz			fChannel->FinishDMA();
5722805aaeSMichael Lotz		return result;
5822805aaeSMichael Lotz	}
5922805aaeSMichael Lotz
6022805aaeSMichael Lotz	// wait for device to get ready for packet transmission
6133d80ba2SMichael Lotz	if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY,
62827f849aSMichael Lotz		ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT, 100 * 1000) != B_OK) {
6322805aaeSMichael Lotz		TRACE_ERROR("timeout waiting for data request\n");
6422805aaeSMichael Lotz		if (request->UseDMA())
6522805aaeSMichael Lotz			fChannel->FinishDMA();
6622805aaeSMichael Lotz
6722805aaeSMichael Lotz		request->SetStatus(SCSI_SEQUENCE_FAIL);
6822805aaeSMichael Lotz		return B_TIMED_OUT;
6922805aaeSMichael Lotz	}
7022805aaeSMichael Lotz
7122805aaeSMichael Lotz	// make sure device really asks for command packet
72827f849aSMichael Lotz	fRegisterMask = ATA_MASK_INTERRUPT_REASON;
7322805aaeSMichael Lotz	fChannel->ReadRegs(this);
7422805aaeSMichael Lotz
7522805aaeSMichael Lotz	if (!fTaskFile.packet_res.cmd_or_data
7622805aaeSMichael Lotz		|| fTaskFile.packet_res.input_or_output) {
7722805aaeSMichael Lotz		TRACE_ERROR("device doesn't ask for packet\n");
7822805aaeSMichael Lotz		if (request->UseDMA())
7922805aaeSMichael Lotz			fChannel->FinishDMA();
8022805aaeSMichael Lotz
8122805aaeSMichael Lotz		request->SetStatus(SCSI_SEQUENCE_FAIL);
8222805aaeSMichael Lotz		return B_ERROR;
8322805aaeSMichael Lotz	}
8422805aaeSMichael Lotz
8522805aaeSMichael Lotz	// some old drives need a delay before submitting the packet
8622805aaeSMichael Lotz	spin(10);
8722805aaeSMichael Lotz
8822805aaeSMichael Lotz	// write packet
8922805aaeSMichael Lotz	if (fChannel->WritePIO(fPacket, sizeof(fPacket)) != B_OK) {
9022805aaeSMichael Lotz		TRACE_ERROR("failed to write packet\n");
9122805aaeSMichael Lotz		if (request->UseDMA())
9222805aaeSMichael Lotz			fChannel->FinishDMA();
9322805aaeSMichael Lotz
9422805aaeSMichael Lotz		request->SetStatus(SCSI_HBA_ERR);
9522805aaeSMichael Lotz		return B_ERROR;
9622805aaeSMichael Lotz	}
9722805aaeSMichael Lotz
986a2bc102SMichael Lotz	if (!request->HasData())
996a2bc102SMichael Lotz		return _FinishRequest(request, ATA_WAIT_FINISH);
100827f849aSMichael Lotz
10122805aaeSMichael Lotz	if (request->UseDMA()) {
10233d80ba2SMichael Lotz		fChannel->PrepareWaitingForInterrupt();
10322805aaeSMichael Lotz		fChannel->StartDMA();
10433d80ba2SMichael Lotz
10522805aaeSMichael Lotz		result = fChannel->WaitForInterrupt(request->Timeout());
10622805aaeSMichael Lotz		status_t dmaResult = fChannel->FinishDMA();
10722805aaeSMichael Lotz		if (result != B_OK) {
10833d80ba2SMichael Lotz			request->SetStatus(SCSI_CMD_TIMEOUT);
10922805aaeSMichael Lotz			return B_TIMED_OUT;
11022805aaeSMichael Lotz		}
11122805aaeSMichael Lotz
1126a2bc102SMichael Lotz		result = _FinishRequest(request, ATA_WAIT_FINISH);
11333d80ba2SMichael Lotz		if (result != B_OK) {
11433d80ba2SMichael Lotz			TRACE_ERROR("device indicates transfer error after dma\n");
11533d80ba2SMichael Lotz			return result;
11633d80ba2SMichael Lotz		}
11733d80ba2SMichael Lotz
11833d80ba2SMichael Lotz		// for ATAPI it's ok for the device to send too much
11933d80ba2SMichael Lotz		if (dmaResult == B_OK || dmaResult == B_DEV_DATA_OVERRUN) {
12033d80ba2SMichael Lotz			fDMAFailures = 0;
12133d80ba2SMichael Lotz			request->CCB()->data_resid = 0;
12233d80ba2SMichael Lotz			return B_OK;
12322805aaeSMichael Lotz		}
12433d80ba2SMichael Lotz
12533d80ba2SMichael Lotz		TRACE_ERROR("dma transfer failed\n");
12633d80ba2SMichael Lotz		request->SetSense(SCSIS_KEY_HARDWARE_ERROR,
12733d80ba2SMichael Lotz			SCSIS_ASC_LUN_COM_FAILURE);
12833d80ba2SMichael Lotz		fDMAFailures++;
12933d80ba2SMichael Lotz		if (fDMAFailures >= ATA_MAX_DMA_FAILURES) {
13033d80ba2SMichael Lotz			TRACE_ALWAYS("disabling DMA after %u failures\n", fDMAFailures);
13133d80ba2SMichael Lotz			fUseDMA = false;
13233d80ba2SMichael Lotz		}
13333d80ba2SMichael Lotz
13433d80ba2SMichael Lotz		return B_ERROR;
13533d80ba2SMichael Lotz	}
13633d80ba2SMichael Lotz
13793239012SMichael Lotz	result = fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY,
13893239012SMichael Lotz		ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT, request->Timeout());
13993239012SMichael Lotz	if (result != B_OK) {
14093239012SMichael Lotz		if (result == B_TIMED_OUT) {
14193239012SMichael Lotz			TRACE_ERROR("timeout waiting for device to request data\n");
14293239012SMichael Lotz			request->SetStatus(SCSI_CMD_TIMEOUT);
14393239012SMichael Lotz			return B_TIMED_OUT;
14493239012SMichael Lotz		} else
1456a2bc102SMichael Lotz			return _FinishRequest(request, 0);
14633d80ba2SMichael Lotz	}
14733d80ba2SMichael Lotz
148827f849aSMichael Lotz	// PIO data transfer
149827f849aSMichael Lotz	while (true) {
150827f849aSMichael Lotz		fRegisterMask = ATA_MASK_INTERRUPT_REASON | ATA_MASK_BYTE_COUNT;
151827f849aSMichael Lotz		fChannel->ReadRegs(this);
152827f849aSMichael Lotz
153827f849aSMichael Lotz		if (fTaskFile.packet_res.cmd_or_data) {
154827f849aSMichael Lotz			TRACE_ERROR("device expecting command instead of data\n");
155827f849aSMichael Lotz			request->SetStatus(SCSI_SEQUENCE_FAIL);
156827f849aSMichael Lotz			return B_ERROR;
157827f849aSMichael Lotz		}
15833d80ba2SMichael Lotz
159827f849aSMichael Lotz		size_t length = fTaskFile.packet_res.byte_count_0_7
160827f849aSMichael Lotz			| ((size_t)fTaskFile.packet_res.byte_count_8_15 << 8);
161827f849aSMichael Lotz		TRACE("about to transfer %lu bytes\n", length);
16222805aaeSMichael Lotz
163827f849aSMichael Lotz		request->SetBytesLeft(length);
164827f849aSMichael Lotz		fChannel->ExecutePIOTransfer(request);
165827f849aSMichael Lotz
1666a2bc102SMichael Lotz		result = fChannel->Wait(0, ATA_STATUS_BUSY, 0, request->Timeout());
1676a2bc102SMichael Lotz		if (result != B_OK) {
1686a2bc102SMichael Lotz			if (result == B_TIMED_OUT) {
1696a2bc102SMichael Lotz				TRACE_ERROR("timeout waiting for device to finish transfer\n");
1706a2bc102SMichael Lotz				request->SetStatus(SCSI_CMD_TIMEOUT);
1716a2bc102SMichael Lotz				return B_TIMED_OUT;
1726a2bc102SMichael Lotz			} else
1736a2bc102SMichael Lotz				return _FinishRequest(request, 0);
174827f849aSMichael Lotz		}
17533d80ba2SMichael Lotz
176827f849aSMichael Lotz		if ((fChannel->AltStatus() & ATA_STATUS_DATA_REQUEST) == 0) {
177827f849aSMichael Lotz			// transfer complete
178827f849aSMichael Lotz			TRACE("pio transfer complete\n");
179827f849aSMichael Lotz			break;
180827f849aSMichael Lotz		}
181827f849aSMichael Lotz	}
18233d80ba2SMichael Lotz
1836a2bc102SMichael Lotz	return _FinishRequest(request, ATA_WAIT_FINISH);
18422805aaeSMichael Lotz}
18522805aaeSMichael Lotz
18622805aaeSMichael Lotz
187bf9a3835SMichael Lotzstatus_t
188bf9a3835SMichael LotzATAPIDevice::ExecuteIO(ATARequest *request)
189bf9a3835SMichael Lotz{
19022805aaeSMichael Lotz	scsi_ccb *ccb = request->CCB();
191ca68245eSMichael Lotz	if (ccb->target_lun != 0) {
192ca68245eSMichael Lotz		TRACE_ERROR("invalid target lun %d\n", ccb->target_lun);
19322805aaeSMichael Lotz		request->SetStatus(SCSI_SEL_TIMEOUT);
19422805aaeSMichael Lotz		return B_BAD_INDEX;
19522805aaeSMichael Lotz	}
19622805aaeSMichael Lotz
19722805aaeSMichael Lotz	// ATAPI command packets are 12 bytes long;
19822805aaeSMichael Lotz	// if the command is shorter, remaining bytes must be padded with zeros
19922805aaeSMichael Lotz	memset(fPacket, 0, sizeof(fPacket));
20022805aaeSMichael Lotz	memcpy(fPacket, ccb->cdb, ccb->cdb_length);
20122805aaeSMichael Lotz
20222805aaeSMichael Lotz	request->SetDevice(this);
20322805aaeSMichael Lotz	request->SetIsWrite((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_OUT);
20422805aaeSMichael Lotz	return SendPacket(request);
20522805aaeSMichael Lotz}
20622805aaeSMichael Lotz
20722805aaeSMichael Lotz
20822805aaeSMichael Lotzstatus_t
20922805aaeSMichael LotzATAPIDevice::Configure()
21022805aaeSMichael Lotz{
211ca68245eSMichael Lotz	if (fInfoBlock.word_0.atapi.atapi_device != ATA_WORD_0_ATAPI_DEVICE) {
212ca68245eSMichael Lotz		TRACE_ERROR("infoblock indicates non-atapi device\n");
21322805aaeSMichael Lotz		return B_ERROR;
214ca68245eSMichael Lotz	}
21522805aaeSMichael Lotz
21622805aaeSMichael Lotz	fTaskFile.packet.lun = 0;
21722805aaeSMichael Lotz
21822805aaeSMichael Lotz	status_t result = ConfigureDMA();
21922805aaeSMichael Lotz	if (result != B_OK)
22022805aaeSMichael Lotz		return result;
22122805aaeSMichael Lotz
222ca68245eSMichael Lotz	result = DisableCommandQueueing();
223ca68245eSMichael Lotz	if (result != B_OK)
224ca68245eSMichael Lotz		return result;
225ca68245eSMichael Lotz
22622805aaeSMichael Lotz	return B_OK;
22722805aaeSMichael Lotz}
22822805aaeSMichael Lotz
22922805aaeSMichael Lotz
23022805aaeSMichael Lotzstatus_t
23122805aaeSMichael LotzATAPIDevice::_FillTaskFilePacket(ATARequest *request)
23222805aaeSMichael Lotz{
23322805aaeSMichael Lotz	scsi_ccb *ccb = request->CCB();
23422805aaeSMichael Lotz	fRegisterMask = ATA_MASK_FEATURES | ATA_MASK_BYTE_COUNT;
235827f849aSMichael Lotz	fTaskFile.packet.dma = request->UseDMA() ? 1 : 0;
23622805aaeSMichael Lotz	fTaskFile.packet.ovl = 0;
23722805aaeSMichael Lotz	fTaskFile.packet.byte_count_0_7 = ccb->data_length & 0xff;
23822805aaeSMichael Lotz	fTaskFile.packet.byte_count_8_15 = ccb->data_length >> 8;
23922805aaeSMichael Lotz	fTaskFile.packet.command = ATA_COMMAND_PACKET;
24022805aaeSMichael Lotz	return B_OK;
241bf9a3835SMichael Lotz}
2426a2bc102SMichael Lotz
2436a2bc102SMichael Lotz
2446a2bc102SMichael Lotzstatus_t
2456a2bc102SMichael LotzATAPIDevice::_FinishRequest(ATARequest *request, uint32 flags)
2466a2bc102SMichael Lotz{
2476a2bc102SMichael Lotz	if (fChannel->FinishRequest(request, flags
2486a2bc102SMichael Lotz		| ATA_CHECK_DEVICE_FAULT, 0) != B_OK) {
2496a2bc102SMichael Lotz		// when we get an error from a packet device, we instruct the
2506a2bc102SMichael Lotz		// scsi layer to do a request sense. but since we don't want to
2516a2bc102SMichael Lotz		// return an emulated sense coming from ata, we clear our sense
2526a2bc102SMichael Lotz		// key first so that the next request sense will go to the packet
2536a2bc102SMichael Lotz		// device directly (as a packet command).
2546a2bc102SMichael Lotz		request->ClearSense();
2556a2bc102SMichael Lotz		request->SetStatus(SCSI_REQ_CMP_ERR);
2566a2bc102SMichael Lotz		request->CCB()->device_status = SCSI_STATUS_CHECK_CONDITION;
2576a2bc102SMichael Lotz		return B_ERROR;
2586a2bc102SMichael Lotz	}
2596a2bc102SMichael Lotz
2606a2bc102SMichael Lotz	return B_OK;
2616a2bc102SMichael Lotz}