1/*
2 * Copyright 2008, Marcus Overhagen. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <string.h>
8
9#include "sata_request.h"
10#include "scsi_cmds.h"
11
12
13#define FIS_TYPE_REGISTER_HOST_TO_DEVICE 0x27
14
15
16sata_request::sata_request()
17	:
18	fCcb(NULL),
19	fIsATAPI(false),
20	fCompletionSem(create_sem(0, "sata completion")),
21	fCompletionStatus(0),
22	fData(NULL),
23	fDataSize(0)
24{
25}
26
27
28sata_request::sata_request(scsi_ccb* ccb)
29	:
30	fCcb(ccb),
31	fIsATAPI(false),
32	fCompletionSem(-1),
33	fCompletionStatus(0),
34	fData(NULL),
35	fDataSize(0)
36{
37}
38
39
40sata_request::~sata_request()
41{
42	if (fCompletionSem >= 0)
43		delete_sem(fCompletionSem);
44}
45
46
47void
48sata_request::SetData(void* data, size_t dataSize)
49{
50	ASSERT(fCcb == NULL);
51	fData = data;
52	fDataSize = dataSize;
53}
54
55
56void
57sata_request::SetATACommand(uint8 command)
58{
59	memset(fFis, 0, sizeof(fFis));
60	fFis[0] = FIS_TYPE_REGISTER_HOST_TO_DEVICE;
61	fFis[1] = 0x80;
62		// This is a command
63	fFis[2] = command;
64}
65
66
67void
68sata_request::SetATA28Command(uint8 command, uint32 lba, uint8 sectorCount)
69{
70	SetATACommand(command);
71	fFis[4] = lba & 0xff;
72	fFis[5] = (lba >> 8) & 0xff;
73	fFis[6] = (lba >> 16) & 0xff;
74	fFis[7] = 0x40 | ((lba >> 24) & 0x0f);
75		// device
76	fFis[12] = sectorCount & 0xff;
77}
78
79
80void
81sata_request::SetATA48Command(uint8 command, uint64 lba, uint16 sectorCount)
82{
83	SetATACommand(command);
84	fFis[4] = lba & 0xff;
85	fFis[5] = (lba >> 8) & 0xff;
86	fFis[6] = (lba >> 16) & 0xff;
87	fFis[7] = 0x40;
88		// device
89	fFis[8] = (lba >> 24) & 0xff;
90	fFis[9] = (lba >> 32) & 0xff;
91	fFis[10] = (lba >> 40) & 0xff;
92	fFis[12] = sectorCount & 0xff;
93	fFis[13] = (sectorCount >> 8) & 0xff;
94}
95
96
97void
98sata_request::SetFeature(uint16 feature)
99{
100	fFis[3] = (uint8)(feature & 0xff);
101	fFis[11] = (uint8)(feature >> 8);
102}
103
104
105void
106sata_request::SetATAPICommand(size_t transferLength)
107{
108	fIsATAPI = true;
109	SetATACommand(0xa0);
110	if (1 /* isPIO */) {
111		if (transferLength == 0)
112			transferLength = 2;
113		else if (transferLength > 0xfffe)
114			transferLength = 0xfffe;
115		fFis[5] = transferLength & 0xff;
116		fFis[6] = (transferLength >> 8) & 0xff;
117	}
118}
119
120
121void
122sata_request::Finish(int tfd, size_t bytesTransfered)
123{
124	if ((tfd & (ATA_STATUS_ERROR | ATA_STATUS_DEVICE_FAULT)) != 0) {
125		uint8 status = tfd & 0xff;
126		uint8 error = (tfd >> 8) & 0xff;
127
128		if (!IsTestUnitReady()) {
129			dprintf("ahci: sata_request::finish ATA command 0x%02x failed:"
130				" status 0x%02x, error 0x%02x\n", fFis[2], status, error);
131		}
132	}
133
134	if (fCcb) {
135		fCcb->data_resid = fCcb->data_length - bytesTransfered;
136		fCcb->device_status = SCSI_STATUS_GOOD;
137		fCcb->subsys_status = SCSI_REQ_CMP;
138		if (tfd & (ATA_STATUS_ERROR | ATA_STATUS_DEVICE_FAULT)) {
139			fCcb->subsys_status = SCSI_REQ_CMP_ERR;
140			if (fIsATAPI) {
141				if (!IsTestUnitReady()) {
142					dprintf("ahci: sata_request::finish ATAPI packet %02x %02x "
143						"%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x "
144						"%02x %02x %02x %02x (len %d)\n",
145						fCcb->cdb[0], fCcb->cdb[1], fCcb->cdb[2], fCcb->cdb[3],
146						fCcb->cdb[4], fCcb->cdb[5], fCcb->cdb[6], fCcb->cdb[7],
147						fCcb->cdb[8], fCcb->cdb[9], fCcb->cdb[10],
148						fCcb->cdb[11], fCcb->cdb[12], fCcb->cdb[13],
149						fCcb->cdb[14], fCcb->cdb[15], fCcb->cdb_length);
150				}
151
152				fCcb->device_status = SCSI_STATUS_CHECK_CONDITION;
153			} else {
154				// TODO ATA error handling goes here
155/*
156				// TODO check ABORT bit if this is useful
157				if ((tfd >> 8) & 0x04) { // ABRT
158					fCcb->subsys_status = SCSI_REQ_ABORTED;
159				} else {
160					fCcb->device_status = SCSI_STATUS_CHECK_CONDITION;
161					fCcb->subsys_status |= SCSI_AUTOSNS_VALID;
162					fCcb->sense_resid = 0; //FIXME
163					scsi_sense *sense = (scsi_sense *)fCcb->sense;
164					sense->error_code = SCSIS_CURR_ERROR;
165					sense->sense_key = error >> 4;
166					sense->asc = 0;
167					sense->ascq = 0;
168				}
169*/
170			}
171		}
172		gSCSI->finished(fCcb, 1);
173		delete this;
174	} else {
175		fCompletionStatus = tfd;
176		release_sem(fCompletionSem);
177	}
178}
179
180
181void
182sata_request::Abort()
183{
184	dprintf("ahci: sata_request::abort called for command 0x%02x\n", fFis[2]);
185	if (fCcb != NULL) {
186		fCcb->subsys_status = SCSI_REQ_ABORTED;
187		gSCSI->finished(fCcb, 1);
188		delete this;
189	} else {
190		fCompletionStatus = ATA_STATUS_ERROR;
191		release_sem(fCompletionSem);
192	}
193}
194
195
196void
197sata_request::WaitForCompletion()
198{
199	ASSERT(fCcb == NULL);
200	acquire_sem(fCompletionSem);
201}
202
203
204int
205sata_request::CompletionStatus()
206{
207	ASSERT(fCcb == NULL);
208	return fCompletionStatus;
209}
210