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