xref: /haiku/src/add-ons/kernel/busses/scsi/ahci/ahci_port.cpp (revision c9ad965c81b08802fed0827fd1dd16f45297928a)
1 /*
2  * Copyright 2007-2009, Marcus Overhagen. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "ahci_port.h"
7 #include "ahci_controller.h"
8 #include "util.h"
9 #include "ata_cmds.h"
10 #include "scsi_cmds.h"
11 #include "sata_request.h"
12 
13 #include <KernelExport.h>
14 #include <ByteOrder.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <new>
18 
19 #include "ahci_tracing.h"
20 
21 #define TRACE_AHCI
22 #ifdef TRACE_AHCI
23 #	define TRACE(a...) dprintf("ahci: " a)
24 #else
25 #	define TRACE(a...)
26 #endif
27 //#define FLOW(a...)	dprintf("ahci: " a)
28 //#define RWTRACE(a...) dprintf("ahci: " a)
29 #define FLOW(a...)
30 #define RWTRACE(a...)
31 
32 
33 AHCIPort::AHCIPort(AHCIController *controller, int index)
34 	: fController(controller),
35 	fIndex(index),
36 	fRegs(&controller->fRegs->port[index]),
37 	fArea(-1),
38 	fCommandsActive(0),
39 	fRequestSem(-1),
40 	fResponseSem(-1),
41 	fDevicePresent(false),
42 	fUse48BitCommands(false),
43 	fSectorSize(0),
44 	fSectorCount(0),
45 	fIsATAPI(false),
46 	fTestUnitReadyActive(false),
47 	fResetPort(false),
48 	fError(false)
49 {
50 	B_INITIALIZE_SPINLOCK(&fSpinlock);
51 	fRequestSem = create_sem(1, "ahci request");
52 	fResponseSem = create_sem(0, "ahci response");
53 }
54 
55 
56 AHCIPort::~AHCIPort()
57 {
58 	delete_sem(fRequestSem);
59 	delete_sem(fResponseSem);
60 }
61 
62 
63 status_t
64 AHCIPort::Init1()
65 {
66 	TRACE("AHCIPort::Init1 port %d\n", fIndex);
67 
68 	size_t size = sizeof(command_list_entry) * COMMAND_LIST_ENTRY_COUNT
69 		+ sizeof(fis) + sizeof(command_table)
70 		+ sizeof(prd) * PRD_TABLE_ENTRY_COUNT;
71 
72 	char *virtAddr;
73 	char *physAddr;
74 
75 	fArea = alloc_mem((void **)&virtAddr, (void **)&physAddr, size, 0,
76 		"some AHCI port");
77 	if (fArea < B_OK) {
78 		TRACE("failed allocating memory for port %d\n", fIndex);
79 		return fArea;
80 	}
81 	memset(virtAddr, 0, size);
82 
83 	fCommandList = (command_list_entry *)virtAddr;
84 	virtAddr += sizeof(command_list_entry) * COMMAND_LIST_ENTRY_COUNT;
85 	fFIS = (fis *)virtAddr;
86 	virtAddr += sizeof(fis);
87 	fCommandTable = (command_table *)virtAddr;
88 	virtAddr += sizeof(command_table);
89 	fPRDTable = (prd *)virtAddr;
90 	TRACE("PRD table is at %p\n", fPRDTable);
91 
92 	fRegs->clb  = LO32(physAddr);
93 	fRegs->clbu = HI32(physAddr);
94 	physAddr += sizeof(command_list_entry) * COMMAND_LIST_ENTRY_COUNT;
95 	fRegs->fb   = LO32(physAddr);
96 	fRegs->fbu  = HI32(physAddr);
97 	physAddr += sizeof(fis);
98 	fCommandList[0].ctba  = LO32(physAddr);
99 	fCommandList[0].ctbau = HI32(physAddr);
100 	// prdt follows after command table
101 
102 	// disable transitions to partial or slumber state
103 	fRegs->sctl |= 0x300;
104 
105 	// clear IRQ status bits
106 	fRegs->is = fRegs->is;
107 
108 	// clear error bits
109 	fRegs->serr = fRegs->serr;
110 
111 	// power up device
112 	fRegs->cmd |= PORT_CMD_POD;
113 
114 	// spin up device
115 	fRegs->cmd |= PORT_CMD_SUD;
116 
117 	// activate link
118 	fRegs->cmd = (fRegs->cmd & ~PORT_CMD_ICC_MASK) | PORT_CMD_ICC_ACTIVE;
119 
120 	// enable FIS receive
121 	fRegs->cmd |= PORT_CMD_FER;
122 
123 	FlushPostedWrites();
124 
125 	return B_OK;
126 }
127 
128 
129 // called with global interrupts enabled
130 status_t
131 AHCIPort::Init2()
132 {
133 	TRACE("AHCIPort::Init2 port %d\n", fIndex);
134 
135 	// start DMA engine
136 	fRegs->cmd |= PORT_CMD_ST;
137 
138 	// enable interrupts
139 	fRegs->ie = PORT_INT_MASK;
140 
141 	FlushPostedWrites();
142 
143 	ResetPort(true);
144 
145 	TRACE("ie   0x%08lx\n", fRegs->ie);
146 	TRACE("is   0x%08lx\n", fRegs->is);
147 	TRACE("cmd  0x%08lx\n", fRegs->cmd);
148 	TRACE("ssts 0x%08lx\n", fRegs->ssts);
149 	TRACE("sctl 0x%08lx\n", fRegs->sctl);
150 	TRACE("serr 0x%08lx\n", fRegs->serr);
151 	TRACE("sact 0x%08lx\n", fRegs->sact);
152 	TRACE("tfd  0x%08lx\n", fRegs->tfd);
153 
154 	fDevicePresent = (fRegs->ssts & 0xf) == 0x3;
155 
156 	return B_OK;
157 }
158 
159 
160 void
161 AHCIPort::Uninit()
162 {
163 	TRACE("AHCIPort::Uninit port %d\n", fIndex);
164 
165 	// disable FIS receive
166 	fRegs->cmd &= ~PORT_CMD_FER;
167 
168 	// wait for receive completition, up to 500ms
169 	if (wait_until_clear(&fRegs->cmd, PORT_CMD_FR, 500000) < B_OK) {
170 		TRACE("AHCIPort::Uninit port %d error FIS rx still running\n", fIndex);
171 	}
172 
173 	// stop DMA engine
174 	fRegs->cmd &= ~PORT_CMD_ST;
175 
176 	// wait for DMA completition
177 	if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) {
178 		TRACE("AHCIPort::Uninit port %d error DMA engine still running\n",
179 			fIndex);
180 	}
181 
182 	// disable interrupts
183 	fRegs->ie = 0;
184 
185 	// clear pending interrupts
186 	fRegs->is = fRegs->is;
187 
188 	// invalidate DMA addresses
189 	fRegs->clb  = 0;
190 	fRegs->clbu = 0;
191 	fRegs->fb   = 0;
192 	fRegs->fbu  = 0;
193 
194 	delete_area(fArea);
195 }
196 
197 
198 void
199 AHCIPort::ResetDevice()
200 {
201 	if (fRegs->cmd & PORT_CMD_ST)
202 		TRACE("AHCIPort::ResetDevice PORT_CMD_ST set, behaviour undefined\n");
203 
204 	// perform a hard reset
205 	fRegs->sctl = (fRegs->sctl & ~0xf) | 1;
206 	FlushPostedWrites();
207 	spin(1100);
208 	fRegs->sctl &= ~0xf;
209 	FlushPostedWrites();
210 
211 	if (wait_until_set(&fRegs->ssts, 0x1, 100000) < B_OK) {
212 		TRACE("AHCIPort::ResetDevice port %d no device detected\n", fIndex);
213 	}
214 
215 	// clear error bits
216 	fRegs->serr = fRegs->serr;
217 	FlushPostedWrites();
218 
219 	if (fRegs->ssts & 1) {
220 		if (wait_until_set(&fRegs->ssts, 0x3, 500000) < B_OK) {
221 			TRACE("AHCIPort::ResetDevice port %d device present but no phy "
222 				"communication\n", fIndex);
223 		}
224 	}
225 
226 	// clear error bits
227 	fRegs->serr = fRegs->serr;
228 	FlushPostedWrites();
229 }
230 
231 
232 
233 status_t
234 AHCIPort::ResetPort(bool forceDeviceReset)
235 {
236 	if (!fTestUnitReadyActive)
237 		TRACE("AHCIPort::ResetPort port %d\n", fIndex);
238 
239 	// stop DMA engine
240 	fRegs->cmd &= ~PORT_CMD_ST;
241 	FlushPostedWrites();
242 
243 	if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) {
244 		TRACE("AHCIPort::ResetPort port %d error DMA engine doesn't stop\n",
245 			fIndex);
246 	}
247 
248 	bool deviceBusy = fRegs->tfd & (ATA_BSY | ATA_DRQ);
249 
250 	if (!fTestUnitReadyActive) {
251 		TRACE("AHCIPort::ResetPort port %d, deviceBusy %d, "
252 			"forceDeviceReset %d\n", fIndex, deviceBusy, forceDeviceReset);
253 	}
254 
255 	if (deviceBusy || forceDeviceReset)
256 		ResetDevice();
257 
258 	// start DMA engine
259 	fRegs->cmd |= PORT_CMD_ST;
260 	FlushPostedWrites();
261 
262 	return PostReset();
263 }
264 
265 
266 status_t
267 AHCIPort::PostReset()
268 {
269 	if (!fTestUnitReadyActive)
270 		TRACE("AHCIPort::PostReset port %d\n", fIndex);
271 
272 	if ((fRegs->ssts & 0xf) != 0x3 || (fRegs->tfd & 0xff) == 0x7f) {
273 		TRACE("AHCIPort::PostReset port %d: no device\n", fIndex);
274 		return B_OK;
275 	}
276 
277 	if ((fRegs->tfd & 0xff) == 0xff)
278 		snooze(200000);
279 
280 	if ((fRegs->tfd & 0xff) == 0xff) {
281 		TRACE("AHCIPort::PostReset port %d: invalid task file status 0xff\n",
282 			fIndex);
283 		return B_ERROR;
284 	}
285 
286 	wait_until_clear(&fRegs->tfd, ATA_BSY, 31000000);
287 
288 	fIsATAPI = fRegs->sig == 0xeb140101;
289 
290 	if (fIsATAPI)
291 		fRegs->cmd |= PORT_CMD_ATAPI;
292 	else
293 		fRegs->cmd &= ~PORT_CMD_ATAPI;
294 	FlushPostedWrites();
295 
296 	if (!fTestUnitReadyActive) {
297 		TRACE("device signature 0x%08lx (%s)\n", fRegs->sig,
298 			(fRegs->sig == 0xeb140101) ? "ATAPI" : (fRegs->sig == 0x00000101) ?
299 				"ATA" : "unknown");
300 	}
301 
302 	return B_OK;
303 }
304 
305 
306 void
307 AHCIPort::DumpD2HFis()
308 {
309 	TRACE("D2H FIS:\n");
310 	TRACE("  DW0  %02x %02x %02x %02x\n", fFIS->rfis[3], fFIS->rfis[2],
311 		fFIS->rfis[1], fFIS->rfis[0]);
312 	TRACE("  DW1  %02x %02x %02x %02x\n", fFIS->rfis[7], fFIS->rfis[6],
313 		fFIS->rfis[5], fFIS->rfis[4]);
314 	TRACE("  DW2  %02x %02x %02x %02x\n", fFIS->rfis[11], fFIS->rfis[10],
315 		fFIS->rfis[9], fFIS->rfis[8]);
316 	TRACE("  DW3  %02x %02x %02x %02x\n", fFIS->rfis[15], fFIS->rfis[14],
317 		fFIS->rfis[13], fFIS->rfis[12]);
318 	TRACE("  DW4  %02x %02x %02x %02x\n", fFIS->rfis[19], fFIS->rfis[18],
319 		fFIS->rfis[17], fFIS->rfis[16]);
320 }
321 
322 
323 void
324 AHCIPort::Interrupt()
325 {
326 	uint32 is = fRegs->is;
327 	fRegs->is = is; // clear interrupts
328 
329 	if (is & PORT_INT_ERROR) {
330 		InterruptErrorHandler(is);
331 		return;
332 	}
333 
334 	uint32 ci = fRegs->ci;
335 
336 	RWTRACE("[%lld] %ld AHCIPort::Interrupt port %d, fCommandsActive 0x%08lx, "
337 		"is 0x%08lx, ci 0x%08lx\n", system_time(), find_thread(NULL),
338 		fIndex, fCommandsActive, is, ci);
339 
340 	acquire_spinlock(&fSpinlock);
341 	if ((fCommandsActive & 1) && !(ci & 1)) {
342 		fCommandsActive &= ~1;
343 		release_sem_etc(fResponseSem, 1, B_DO_NOT_RESCHEDULE);
344 	}
345 	release_spinlock(&fSpinlock);
346 }
347 
348 
349 void
350 AHCIPort::InterruptErrorHandler(uint32 is)
351 {
352 	uint32 ci = fRegs->ci;
353 
354 	if (!fTestUnitReadyActive) {
355 		TRACE("AHCIPort::InterruptErrorHandler port %d, "
356 			"fCommandsActive 0x%08lx, is 0x%08lx, ci 0x%08lx\n", fIndex,
357 			fCommandsActive, is, ci);
358 
359 		TRACE("ssts 0x%08lx\n", fRegs->ssts);
360 		TRACE("sctl 0x%08lx\n", fRegs->sctl);
361 		TRACE("serr 0x%08lx\n", fRegs->serr);
362 		TRACE("sact 0x%08lx\n", fRegs->sact);
363 	}
364 
365 	// read and clear SError
366 	uint32 serr = fRegs->serr;
367 	fRegs->serr = serr;
368 
369 	if (is & PORT_INT_TFE) {
370 		if (!fTestUnitReadyActive)
371 			TRACE("Task File Error\n");
372 
373 		fResetPort = true;
374 		fError = true;
375 	}
376 	if (is & PORT_INT_HBF) {
377 		TRACE("Host Bus Fatal Error\n");
378 		fResetPort = true;
379 		fError = true;
380 	}
381 	if (is & PORT_INT_HBD) {
382 		TRACE("Host Bus Data Error\n");
383 		fResetPort = true;
384 		fError = true;
385 	}
386 	if (is & PORT_INT_IF) {
387 		TRACE("Interface Fatal Error\n");
388 		fResetPort = true;
389 		fError = true;
390 	}
391 	if (is & PORT_INT_INF) {
392 		TRACE("Interface Non Fatal Error\n");
393 	}
394 	if (is & PORT_INT_OF) {
395 		TRACE("Overflow");
396 		fResetPort = true;
397 		fError = true;
398 	}
399 	if (is & PORT_INT_IPM) {
400 		TRACE("Incorrect Port Multiplier Status");
401 	}
402 	if (is & PORT_INT_PRC) {
403 		TRACE("PhyReady Change\n");
404 //		fResetPort = true;
405 	}
406 	if (is & PORT_INT_PC) {
407 		TRACE("Port Connect Change\n");
408 //		fResetPort = true;
409 	}
410 	if (is & PORT_INT_UF) {
411 		TRACE("Unknown FIS\n");
412 		fResetPort = true;
413 	}
414 
415 	if (fError) {
416 		acquire_spinlock(&fSpinlock);
417 		if ((fCommandsActive & 1)) {
418 			fCommandsActive &= ~1;
419 			release_sem_etc(fResponseSem, 1, B_DO_NOT_RESCHEDULE);
420 		}
421 		release_spinlock(&fSpinlock);
422 	}
423 }
424 
425 
426 status_t
427 AHCIPort::FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax,
428 	const void *data, size_t dataSize)
429 {
430 	int peMax = prdMax + 1;
431 	physical_entry pe[peMax];
432 	if (get_memory_map(data, dataSize, pe, peMax ) < B_OK) {
433 		TRACE("AHCIPort::FillPrdTable get_memory_map failed\n");
434 		return B_ERROR;
435 	}
436 	int peUsed;
437 	for (peUsed = 0; pe[peUsed].size; peUsed++)
438 		;
439 	return FillPrdTable(prdTable, prdCount, prdMax, pe, peUsed, dataSize);
440 }
441 
442 
443 status_t
444 AHCIPort::FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax,
445 	const physical_entry *sgTable, int sgCount, size_t dataSize)
446 {
447 	*prdCount = 0;
448 	while (sgCount > 0 && dataSize > 0) {
449 		size_t size = min_c(sgTable->size, dataSize);
450 		void *address = sgTable->address;
451 		T_PORT(AHCIPortPrdTable(fController, fIndex, address, size));
452 		FLOW("FillPrdTable: sg-entry addr %p, size %lu\n", address, size);
453 		if ((uint32)address & 1) {
454 			TRACE("AHCIPort::FillPrdTable: data alignment error\n");
455 			return B_ERROR;
456 		}
457 		dataSize -= size;
458 		while (size > 0) {
459 			size_t bytes = min_c(size, PRD_MAX_DATA_LENGTH);
460 			if (*prdCount == prdMax) {
461 				TRACE("AHCIPort::FillPrdTable: prd table exhausted\n");
462 				return B_ERROR;
463 			}
464 			FLOW("FillPrdTable: prd-entry %u, addr %p, size %lu\n",
465 				*prdCount, address, bytes);
466 
467 			prdTable->dba  = LO32(address);
468 			prdTable->dbau = HI32(address);
469 			prdTable->res  = 0;
470 			prdTable->dbc  = bytes - 1;
471 			*prdCount += 1;
472 			prdTable++;
473 			address = (char *)address + bytes;
474 			size -= bytes;
475 		}
476 		sgTable++;
477 		sgCount--;
478 	}
479 	if (*prdCount == 0) {
480 		TRACE("AHCIPort::FillPrdTable: count is 0\n");
481 		return B_ERROR;
482 	}
483 	if (dataSize > 0) {
484 		TRACE("AHCIPort::FillPrdTable: sg table %ld bytes too small\n",
485 			dataSize);
486 		return B_ERROR;
487 	}
488 	return B_OK;
489 }
490 
491 
492 void
493 AHCIPort::StartTransfer()
494 {
495 	acquire_sem(fRequestSem);
496 }
497 
498 
499 status_t
500 AHCIPort::WaitForTransfer(int *tfd, bigtime_t timeout)
501 {
502 	status_t result = acquire_sem_etc(fResponseSem, 1, B_RELATIVE_TIMEOUT,
503 		timeout);
504 	if (result < B_OK) {
505 		cpu_status cpu = disable_interrupts();
506 		acquire_spinlock(&fSpinlock);
507 		fCommandsActive &= ~1;
508 		release_spinlock(&fSpinlock);
509 		restore_interrupts(cpu);
510 
511 		result = B_TIMED_OUT;
512 	} else if (fError) {
513 		*tfd = fRegs->tfd;
514 		result = B_ERROR;
515 		fError = false;
516 	} else {
517 		*tfd = fRegs->tfd;
518 	}
519 	return result;
520 }
521 
522 
523 void
524 AHCIPort::FinishTransfer()
525 {
526 	release_sem(fRequestSem);
527 }
528 
529 
530 void
531 AHCIPort::ScsiTestUnitReady(scsi_ccb *request)
532 {
533 	TRACE("AHCIPort::ScsiTestUnitReady port %d\n", fIndex);
534 	request->subsys_status = SCSI_REQ_CMP;
535 	gSCSI->finished(request, 1);
536 }
537 
538 
539 void
540 AHCIPort::ScsiInquiry(scsi_ccb *request)
541 {
542 	TRACE("AHCIPort::ScsiInquiry port %d\n", fIndex);
543 
544 	scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)request->cdb;
545 	scsi_res_inquiry scsiData;
546 	ata_res_identify_device	ataData;
547 
548 	ASSERT(sizeof(ataData) == 512);
549 
550 	if (cmd->evpd || cmd->page_code || request->data_length < sizeof(scsiData)) {
551 		TRACE("invalid request\n");
552 		request->subsys_status = SCSI_REQ_ABORTED;
553 		gSCSI->finished(request, 1);
554 		return;
555 	}
556 
557 	sata_request sreq;
558 	sreq.set_data(&ataData, sizeof(ataData));
559 	sreq.set_ata_cmd(fIsATAPI ? 0xa1 : 0xec); // Identify (Packet) Device
560 	ExecuteSataRequest(&sreq);
561 	sreq.wait_for_completition();
562 
563 	if (sreq.completition_status() & ATA_ERR) {
564 		TRACE("identify device failed\n");
565 		request->subsys_status = SCSI_REQ_CMP_ERR;
566 		gSCSI->finished(request, 1);
567 		return;
568 	}
569 
570 /*
571 	uint8 *data = (uint8*) &ataData;
572 	for (int i = 0; i < 512; i += 8) {
573 		TRACE("  %02x %02x %02x %02x %02x %02x %02x %02x\n", data[i], data[i+1],
574 			data[i+2], data[i+3], data[i+4], data[i+5], data[i+6], data[i+7]);
575 	}
576 */
577 
578 	scsiData.device_type = fIsATAPI ? scsi_dev_CDROM : scsi_dev_direct_access;
579 	scsiData.device_qualifier = scsi_periph_qual_connected;
580 	scsiData.device_type_modifier = 0;
581 	scsiData.removable_medium = fIsATAPI;
582 	scsiData.ansi_version = 2;
583 	scsiData.ecma_version = 0;
584 	scsiData.iso_version = 0;
585 	scsiData.response_data_format = 2;
586 	scsiData.term_iop = false;
587 	scsiData.additional_length = sizeof(scsiData) - 4;
588 	scsiData.soft_reset = false;
589 	scsiData.cmd_queue = false;
590 	scsiData.linked = false;
591 	scsiData.sync = false;
592 	scsiData.write_bus16 = true;
593 	scsiData.write_bus32 = false;
594 	scsiData.relative_address = false;
595 	memcpy(scsiData.vendor_ident, ataData.model_number,
596 		sizeof(scsiData.vendor_ident));
597 	memcpy(scsiData.product_ident, ataData.model_number + 8,
598 		sizeof(scsiData.product_ident));
599 	memcpy(scsiData.product_rev, ataData.serial_number,
600 		sizeof(scsiData.product_rev));
601 
602 	if (!fIsATAPI) {
603 		bool lba			= (ataData.words[49] & (1 << 9)) != 0;
604 		bool lba48			= (ataData.words[83] & (1 << 10)) != 0;
605 		uint32 sectors		= *(uint32*)&ataData.words[60];
606 		uint64 sectors48	= *(uint64*)&ataData.words[100];
607 		fUse48BitCommands   = lba && lba48;
608 		fSectorSize			= 512;
609 		fSectorCount		= !(lba || sectors) ? 0 : lba48 ? sectors48 : sectors;
610 		TRACE("lba %d, lba48 %d, fUse48BitCommands %d, sectors %lu, "
611 			"sectors48 %llu, size %llu\n",
612 			lba, lba48, fUse48BitCommands, sectors, sectors48,
613 			fSectorCount * fSectorSize);
614 	}
615 
616 #if 0
617 	if (fSectorCount < 0x0fffffff) {
618 		TRACE("disabling 48 bit commands\n");
619 		fUse48BitCommands = 0;
620 	}
621 #endif
622 
623 	char modelNumber[sizeof(ataData.model_number) + 1];
624 	char serialNumber[sizeof(ataData.serial_number) + 1];
625 	char firmwareRev[sizeof(ataData.firmware_revision) + 1];
626 
627 	strlcpy(modelNumber, ataData.model_number, sizeof(modelNumber));
628 	strlcpy(serialNumber, ataData.serial_number, sizeof(serialNumber));
629 	strlcpy(firmwareRev, ataData.firmware_revision, sizeof(firmwareRev));
630 
631 	swap_words(modelNumber, sizeof(modelNumber) - 1);
632 	swap_words(serialNumber, sizeof(serialNumber) - 1);
633 	swap_words(firmwareRev, sizeof(firmwareRev) - 1);
634 
635 	TRACE("model number: %s\n", modelNumber);
636 	TRACE("serial number: %s\n", serialNumber);
637   	TRACE("firmware rev.: %s\n", firmwareRev);
638 
639 	if (sg_memcpy(request->sg_list, request->sg_count, &scsiData,
640 		sizeof(scsiData)) < B_OK) {
641 		request->subsys_status = SCSI_DATA_RUN_ERR;
642 	} else {
643 		request->subsys_status = SCSI_REQ_CMP;
644 		request->data_resid = request->data_length - sizeof(scsiData);
645 	}
646 	gSCSI->finished(request, 1);
647 }
648 
649 
650 void
651 AHCIPort::ScsiSynchronizeCache(scsi_ccb *request)
652 {
653 	//TRACE("AHCIPort::ScsiSynchronizeCache port %d\n", fIndex);
654 
655 	sata_request *sreq = new(std::nothrow) sata_request(request);
656 	sreq->set_ata_cmd(fUse48BitCommands ? 0xea : 0xe7); // Flush Cache
657 	ExecuteSataRequest(sreq);
658 }
659 
660 
661 void
662 AHCIPort::ScsiReadCapacity(scsi_ccb *request)
663 {
664 	TRACE("AHCIPort::ScsiReadCapacity port %d\n", fIndex);
665 
666 	scsi_cmd_read_capacity *cmd = (scsi_cmd_read_capacity *)request->cdb;
667 	scsi_res_read_capacity scsiData;
668 
669 	if (cmd->pmi || cmd->lba || request->data_length < sizeof(scsiData)) {
670 		TRACE("invalid request\n");
671 		return;
672 	}
673 
674 	TRACE("SectorSize %lu, SectorCount 0x%llx\n", fSectorSize, fSectorCount);
675 
676 	if (fSectorCount > 0xffffffff)
677 		panic("ahci: SCSI emulation doesn't support harddisks larger than 2TB");
678 
679 	scsiData.block_size = B_HOST_TO_BENDIAN_INT32(fSectorSize);
680 	scsiData.lba = B_HOST_TO_BENDIAN_INT32(fSectorCount - 1);
681 
682 	if (sg_memcpy(request->sg_list, request->sg_count, &scsiData, sizeof(scsiData)) < B_OK) {
683 		request->subsys_status = SCSI_DATA_RUN_ERR;
684 	} else {
685 		request->subsys_status = SCSI_REQ_CMP;
686 		request->data_resid = request->data_length - sizeof(scsiData);
687 	}
688 	gSCSI->finished(request, 1);
689 }
690 
691 
692 void
693 AHCIPort::ScsiReadWrite(scsi_ccb *request, uint64 lba, size_t sectorCount, bool isWrite)
694 {
695 	RWTRACE("[%lld] %ld ScsiReadWrite: position %llu, size %lu, isWrite %d\n",
696 		system_time(), find_thread(NULL), lba * 512, sectorCount * 512, isWrite);
697 
698 #if 0
699 	if (isWrite) {
700 		TRACE("write request ignored\n");
701 		request->subsys_status = SCSI_REQ_CMP;
702 		request->data_resid = 0;
703 		gSCSI->finished(request, 1);
704 		return;
705 	}
706 #endif
707 
708 	ASSERT(request->data_length == sectorCount * 512);
709 	sata_request *sreq = new(std::nothrow) sata_request(request);
710 
711 	if (fUse48BitCommands) {
712 		if (sectorCount > 65536)
713 			panic("ahci: ScsiReadWrite length too large, %lu sectors", sectorCount);
714 		if (lba > MAX_SECTOR_LBA_48)
715 			panic("achi: ScsiReadWrite position too large for 48-bit LBA\n");
716 		sreq->set_ata48_cmd(isWrite ? 0x35 : 0x25, lba, sectorCount);
717 	} else {
718 		if (sectorCount > 256)
719 			panic("ahci: ScsiReadWrite length too large, %lu sectors", sectorCount);
720 		if (lba > MAX_SECTOR_LBA_28)
721 			panic("achi: ScsiReadWrite position too large for normal LBA\n");
722 		sreq->set_ata28_cmd(isWrite ? 0xca : 0xc8, lba, sectorCount);
723 	}
724 
725 	ExecuteSataRequest(sreq, isWrite);
726 }
727 
728 
729 void
730 AHCIPort::ExecuteSataRequest(sata_request *request, bool isWrite)
731 {
732 	FLOW("ExecuteAtaRequest port %d\n", fIndex);
733 
734 	StartTransfer();
735 
736 	int prdEntrys;
737 
738 	if (request->ccb() && request->ccb()->data_length)
739 		FillPrdTable(fPRDTable, &prdEntrys, PRD_TABLE_ENTRY_COUNT,
740 			request->ccb()->sg_list, request->ccb()->sg_count,
741 			request->ccb()->data_length);
742 	else if (request->data() && request->size())
743 		FillPrdTable(fPRDTable, &prdEntrys, PRD_TABLE_ENTRY_COUNT,
744 			request->data(), request->size());
745 	else
746 		prdEntrys = 0;
747 
748 	FLOW("prdEntrys %d\n", prdEntrys);
749 
750 	fCommandList->prdtl_flags_cfl = 0;
751 	fCommandList->cfl = 5; // 20 bytes, length in DWORDS
752 	memcpy((char *)fCommandTable->cfis, request->fis(), 20);
753 
754 	fTestUnitReadyActive = request->is_test_unit_ready();
755 	if (request->is_atapi()) {
756 		// ATAPI PACKET is a 12 or 16 byte SCSI command
757 		memset((char *)fCommandTable->acmd, 0, 32);
758 		memcpy((char *)fCommandTable->acmd, request->ccb()->cdb,
759 			request->ccb()->cdb_length);
760 		fCommandList->a = 1;
761 	}
762 
763 	if (isWrite)
764 		fCommandList->w = 1;
765 	fCommandList->prdtl = prdEntrys;
766 	fCommandList->prdbc = 0;
767 
768 	if (wait_until_clear(&fRegs->tfd, ATA_BSY | ATA_DRQ, 1000000) < B_OK) {
769 		TRACE("ExecuteAtaRequest port %d: device is busy\n", fIndex);
770 		ResetPort();
771 		FinishTransfer();
772 		request->abort();
773 		return;
774 	}
775 
776 	cpu_status cpu = disable_interrupts();
777 	acquire_spinlock(&fSpinlock);
778 	fCommandsActive |= 1;
779 	fRegs->ci = 1;
780 	FlushPostedWrites();
781 	release_spinlock(&fSpinlock);
782 	restore_interrupts(cpu);
783 
784 	int tfd;
785 	status_t status = WaitForTransfer(&tfd, 20000000);
786 
787 	FLOW("tfd %#x\n", tfd);
788 	FLOW("prdbc %ld\n", fCommandList->prdbc);
789 	FLOW("ci   0x%08lx\n", fRegs->ci);
790 	FLOW("is   0x%08lx\n", fRegs->is);
791 	FLOW("serr 0x%08lx\n", fRegs->serr);
792 
793 /*
794 	TRACE("ci   0x%08lx\n", fRegs->ci);
795 	TRACE("ie   0x%08lx\n", fRegs->ie);
796 	TRACE("is   0x%08lx\n", fRegs->is);
797 	TRACE("cmd  0x%08lx\n", fRegs->cmd);
798 	TRACE("ssts 0x%08lx\n", fRegs->ssts);
799 	TRACE("sctl 0x%08lx\n", fRegs->sctl);
800 	TRACE("serr 0x%08lx\n", fRegs->serr);
801 	TRACE("sact 0x%08lx\n", fRegs->sact);
802 	TRACE("tfd  0x%08lx\n", fRegs->tfd);
803 */
804 
805 	if (fResetPort || status == B_TIMED_OUT) {
806 		fResetPort = false;
807 		ResetPort();
808 	}
809 
810 	size_t bytesTransfered = fCommandList->prdbc;
811 
812 	FinishTransfer();
813 
814 	if (status == B_TIMED_OUT) {
815 		TRACE("ExecuteAtaRequest port %d: device timeout\n", fIndex);
816 		request->abort();
817 	} else {
818 		request->finish(tfd, bytesTransfered);
819 	}
820 }
821 
822 
823 void
824 AHCIPort::ScsiExecuteRequest(scsi_ccb *request)
825 {
826 //	TRACE("AHCIPort::ScsiExecuteRequest port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);
827 
828 	if (fIsATAPI) {
829 		bool isWrite = false;
830 		switch (request->flags & SCSI_DIR_MASK) {
831 			case SCSI_DIR_NONE:
832 				ASSERT(request->data_length == 0);
833 				break;
834 			case SCSI_DIR_IN:
835 				ASSERT(request->data_length > 0);
836 				break;
837 			case SCSI_DIR_OUT:
838 				isWrite = true;
839 				ASSERT(request->data_length > 0);
840 				break;
841 			default:
842 				panic("CDB has invalid direction mask");
843 		}
844 
845 //		TRACE("AHCIPort::ScsiExecuteRequest ATAPI: port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);
846 
847 		sata_request *sreq = new(std::nothrow) sata_request(request);
848 		sreq->set_atapi_cmd(request->data_length);
849 //		uint8 *data = (uint8*) sreq->ccb()->cdb;
850 //		for (int i = 0; i < 16; i += 8) {
851 //			TRACE("  %02x %02x %02x %02x %02x %02x %02x %02x\n", data[i], data[i+1], data[i+2], data[i+3], data[i+4], data[i+5], data[i+6], data[i+7]);
852 //		}
853 		ExecuteSataRequest(sreq, isWrite);
854 		return;
855 	}
856 
857 	if (request->cdb[0] == SCSI_OP_REQUEST_SENSE) {
858 		panic("ahci: SCSI_OP_REQUEST_SENSE not yet supported\n");
859 		return;
860 	}
861 
862 	if (!fDevicePresent) {
863 		TRACE("no device present on port %d\n", fIndex);
864 		request->subsys_status = SCSI_DEV_NOT_THERE;
865 		gSCSI->finished(request, 1);
866 		return;
867 	}
868 
869 	request->subsys_status = SCSI_REQ_CMP;
870 
871 	switch (request->cdb[0]) {
872 		case SCSI_OP_TEST_UNIT_READY:
873 			ScsiTestUnitReady(request);
874 			break;
875 		case SCSI_OP_INQUIRY:
876 			ScsiInquiry(request);
877 			break;
878 		case SCSI_OP_READ_CAPACITY:
879 			ScsiReadCapacity(request);
880 			break;
881 		case SCSI_OP_SYNCHRONIZE_CACHE:
882 			ScsiSynchronizeCache(request);
883 			break;
884 		case SCSI_OP_READ_6:
885 		case SCSI_OP_WRITE_6:
886 		{
887 			scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
888 			uint32 position = ((uint32)cmd->high_lba << 16) | ((uint32)cmd->mid_lba << 8) | (uint32)cmd->low_lba;
889 			size_t length = cmd->length != 0 ? cmd->length : 256;
890 			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_6;
891 			ScsiReadWrite(request, position, length, isWrite);
892 			break;
893 		}
894 		case SCSI_OP_READ_10:
895 		case SCSI_OP_WRITE_10:
896 		{
897 			scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
898 			uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba);
899 			size_t length = B_BENDIAN_TO_HOST_INT16(cmd->length);
900 			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_10;
901 			if (length) {
902 				ScsiReadWrite(request, position, length, isWrite);
903 			} else {
904 				TRACE("AHCIPort::ScsiExecuteRequest error: transfer without data!\n");
905 				request->subsys_status = SCSI_REQ_INVALID;
906 				gSCSI->finished(request, 1);
907 			}
908 			break;
909 		}
910 		case SCSI_OP_READ_12:
911 		case SCSI_OP_WRITE_12:
912 		{
913 			scsi_cmd_rw_12 *cmd = (scsi_cmd_rw_12 *)request->cdb;
914 			uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba);
915 			size_t length = B_BENDIAN_TO_HOST_INT32(cmd->length);
916 			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_12;
917 			if (length) {
918 				ScsiReadWrite(request, position, length, isWrite);
919 			} else {
920 				TRACE("AHCIPort::ScsiExecuteRequest error: transfer without data!\n");
921 				request->subsys_status = SCSI_REQ_INVALID;
922 				gSCSI->finished(request, 1);
923 			}
924 			break;
925 		}
926 		default:
927 			TRACE("AHCIPort::ScsiExecuteRequest port %d unsupported request opcode 0x%02x\n", fIndex, request->cdb[0]);
928 			request->subsys_status = SCSI_REQ_ABORTED;
929 			gSCSI->finished(request, 1);
930 	}
931 }
932 
933 
934 uchar
935 AHCIPort::ScsiAbortRequest(scsi_ccb *request)
936 {
937 
938 	return SCSI_REQ_CMP;
939 }
940 
941 
942 uchar
943 AHCIPort::ScsiTerminateRequest(scsi_ccb *request)
944 {
945 	return SCSI_REQ_CMP;
946 }
947 
948 
949 uchar
950 AHCIPort::ScsiResetDevice()
951 {
952 	return SCSI_REQ_CMP;
953 }
954 
955 
956 void
957 AHCIPort::ScsiGetRestrictions(bool *isATAPI, bool *noAutoSense, uint32 *maxBlocks)
958 {
959 	*isATAPI = fIsATAPI;
960 	*noAutoSense = fIsATAPI; // emulated auto sense for ATA, but not ATAPI
961 	*maxBlocks = fUse48BitCommands ? 65536 : 256;
962 	TRACE("AHCIPort::ScsiGetRestrictions port %d: isATAPI %d, noAutoSense %d, maxBlocks %lu\n",
963 		fIndex, *isATAPI, *noAutoSense, *maxBlocks);
964 }
965