moved tracing into it's own header
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25579 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
307fe93f85
commit
6e251c6604
@ -12,10 +12,11 @@
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <ByteOrder.h>
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <tracing.h>
|
||||
#include <new>
|
||||
|
||||
#include "ahci_tracing.h"
|
||||
|
||||
#define TRACE(a...) dprintf("\33[34mahci:\33[0m " a)
|
||||
//#define FLOW(a...) dprintf("ahci: " a)
|
||||
@ -23,63 +24,6 @@
|
||||
#define FLOW(a...)
|
||||
#define RWTRACE(a...)
|
||||
|
||||
#if AHCI_PORT_TRACING
|
||||
|
||||
namespace AHCIPortTracing {
|
||||
|
||||
class AHCIPortTraceEntry : public AbstractTraceEntry {
|
||||
protected:
|
||||
AHCIPortTraceEntry(AHCIController* controller, int index)
|
||||
: fController(controller)
|
||||
, fIndex(index)
|
||||
{
|
||||
}
|
||||
|
||||
void AddDump(TraceOutput& out, const char* name)
|
||||
{
|
||||
out.Print("ahci port");
|
||||
out.Print(" - %s - ", name);
|
||||
out.Print("controller: %p", fController);
|
||||
out.Print(", index: %d", fIndex);
|
||||
}
|
||||
|
||||
AHCIController* fController;
|
||||
int fIndex;
|
||||
};
|
||||
|
||||
|
||||
class AHCIPortPrdTable : public AHCIPortTraceEntry {
|
||||
public:
|
||||
AHCIPortPrdTable(AHCIController* controller, int index, void* address,
|
||||
size_t size)
|
||||
: AHCIPortTraceEntry(controller, index)
|
||||
, fAddress(address)
|
||||
, fSize(size)
|
||||
{
|
||||
Initialized();
|
||||
}
|
||||
|
||||
void AddDump(TraceOutput& out)
|
||||
{
|
||||
AHCIPortTraceEntry::AddDump(out, "prd table");
|
||||
|
||||
out.Print(", address: %p", fAddress);
|
||||
out.Print(", size: %lu", fSize);
|
||||
}
|
||||
|
||||
void* fAddress;
|
||||
int fSize;
|
||||
};
|
||||
|
||||
|
||||
} // namespace AHCIPortTracing
|
||||
|
||||
# define T(x) new(std::nothrow) AHCIPortTracing::x
|
||||
|
||||
#else
|
||||
# define T(x)
|
||||
#endif // AHCI_PORT_TRACING
|
||||
|
||||
|
||||
AHCIPort::AHCIPort(AHCIController *controller, int index)
|
||||
: fController(controller)
|
||||
@ -385,7 +329,7 @@ AHCIPort::FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax, const
|
||||
while (sgCount > 0 && dataSize > 0) {
|
||||
size_t size = min_c(sgTable->size, dataSize);
|
||||
void *address = sgTable->address;
|
||||
T(AHCIPortPrdTable(fController, fIndex, address, size));
|
||||
T_PORT(AHCIPortPrdTable(fController, fIndex, address, size));
|
||||
FLOW("FillPrdTable: sg-entry addr %p, size %lu\n", address, size);
|
||||
if ((uint32)address & 1) {
|
||||
TRACE("AHCIPort::FillPrdTable: data alignment error\n");
|
||||
|
@ -1,30 +1,14 @@
|
||||
/*
|
||||
* Copyright 2007-2008, Marcus Overhagen. All rights reserved.
|
||||
* Copyright 2008, Bruno G. Albuquerque. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "ahci_port.h"
|
||||
#include "ahci_controller.h"
|
||||
#include "util.h"
|
||||
#include "ata_cmds.h"
|
||||
#include "scsi_cmds.h"
|
||||
#include "sata_request.h"
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <ByteOrder.h>
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <tracing.h>
|
||||
|
||||
#define TRACE(a...) dprintf("\33[34mahci:\33[0m " a)
|
||||
//#define FLOW(a...) dprintf("ahci: " a)
|
||||
//#define RWTRACE(a...) dprintf("\33[34mahci:\33[0m " a)
|
||||
#define FLOW(a...)
|
||||
#define RWTRACE(a...)
|
||||
|
||||
#if AHCI_PORT_TRACING
|
||||
|
||||
class AHCIController;
|
||||
|
||||
namespace AHCIPortTracing {
|
||||
|
||||
class AHCIPortTraceEntry : public AbstractTraceEntry {
|
||||
@ -74,759 +58,9 @@ class AHCIPortPrdTable : public AHCIPortTraceEntry {
|
||||
|
||||
} // namespace AHCIPortTracing
|
||||
|
||||
# define T(x) new(std::nothrow) AHCIPortTracing::x
|
||||
# define T_PORT(x) new(std::nothrow) AHCIPortTracing::x
|
||||
|
||||
#else
|
||||
# define T(x)
|
||||
# define T_PORT(x)
|
||||
#endif // AHCI_PORT_TRACING
|
||||
|
||||
|
||||
AHCIPort::AHCIPort(AHCIController *controller, int index)
|
||||
: fController(controller)
|
||||
, fIndex(index)
|
||||
, fRegs(&controller->fRegs->port[index])
|
||||
, fArea(-1)
|
||||
, fSpinlock(0)
|
||||
, fCommandsActive(0)
|
||||
, fRequestSem(-1)
|
||||
, fResponseSem(-1)
|
||||
, fDevicePresent(false)
|
||||
, fUse48BitCommands(false)
|
||||
, fSectorSize(0)
|
||||
, fSectorCount(0)
|
||||
, fIsATAPI(false)
|
||||
{
|
||||
fRequestSem = create_sem(1, "ahci request");
|
||||
fResponseSem = create_sem(0, "ahci response");
|
||||
}
|
||||
|
||||
|
||||
AHCIPort::~AHCIPort()
|
||||
{
|
||||
delete_sem(fRequestSem);
|
||||
delete_sem(fResponseSem);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AHCIPort::Init1()
|
||||
{
|
||||
TRACE("AHCIPort::Init1 port %d\n", fIndex);
|
||||
|
||||
size_t size = sizeof(command_list_entry) * COMMAND_LIST_ENTRY_COUNT + sizeof(fis) + sizeof(command_table) + sizeof(prd) * PRD_TABLE_ENTRY_COUNT;
|
||||
|
||||
char *virtAddr;
|
||||
char *physAddr;
|
||||
|
||||
fArea = alloc_mem((void **)&virtAddr, (void **)&physAddr, size, 0, "some AHCI port");
|
||||
if (fArea < B_OK) {
|
||||
TRACE("failed allocating memory for port %d\n", fIndex);
|
||||
return fArea;
|
||||
}
|
||||
memset(virtAddr, 0, size);
|
||||
|
||||
fCommandList = (command_list_entry *)virtAddr;
|
||||
virtAddr += sizeof(command_list_entry) * COMMAND_LIST_ENTRY_COUNT;
|
||||
fFIS = (fis *)virtAddr;
|
||||
virtAddr += sizeof(fis);
|
||||
fCommandTable = (command_table *)virtAddr;
|
||||
virtAddr += sizeof(command_table);
|
||||
fPRDTable = (prd *)virtAddr;
|
||||
TRACE("PRD table is at %p\n", fPRDTable);
|
||||
|
||||
fRegs->clb = LO32(physAddr);
|
||||
fRegs->clbu = HI32(physAddr);
|
||||
physAddr += sizeof(command_list_entry) * COMMAND_LIST_ENTRY_COUNT;
|
||||
fRegs->fb = LO32(physAddr);
|
||||
fRegs->fbu = HI32(physAddr);
|
||||
physAddr += sizeof(fis);
|
||||
fCommandList[0].ctba = LO32(physAddr);
|
||||
fCommandList[0].ctbau = HI32(physAddr);
|
||||
// prdt follows after command table
|
||||
|
||||
// disable transitions to partial or slumber state
|
||||
fRegs->sctl |= 0x300;
|
||||
|
||||
// clear IRQ status bits
|
||||
fRegs->is = fRegs->is;
|
||||
|
||||
// clear error bits
|
||||
fRegs->serr = fRegs->serr;
|
||||
|
||||
// power up device
|
||||
fRegs->cmd |= PORT_CMD_POD;
|
||||
|
||||
// spin up device
|
||||
fRegs->cmd |= PORT_CMD_SUD;
|
||||
|
||||
// activate link
|
||||
fRegs->cmd = (fRegs->cmd & ~PORT_CMD_ICC_MASK) | PORT_CMD_ICC_ACTIVE;
|
||||
|
||||
// enable FIS receive
|
||||
fRegs->cmd |= PORT_CMD_FER;
|
||||
|
||||
FlushPostedWrites();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// called with global interrupts enabled
|
||||
status_t
|
||||
AHCIPort::Init2()
|
||||
{
|
||||
TRACE("AHCIPort::Init2 port %d\n", fIndex);
|
||||
|
||||
// start DMA engine
|
||||
fRegs->cmd |= PORT_CMD_ST;
|
||||
|
||||
// enable interrupts
|
||||
fRegs->ie = PORT_INT_MASK;
|
||||
|
||||
FlushPostedWrites();
|
||||
|
||||
ResetDevice();
|
||||
PostResetDevice();
|
||||
|
||||
TRACE("ie 0x%08lx\n", fRegs->ie);
|
||||
TRACE("is 0x%08lx\n", fRegs->is);
|
||||
TRACE("cmd 0x%08lx\n", fRegs->cmd);
|
||||
TRACE("ssts 0x%08lx\n", fRegs->ssts);
|
||||
TRACE("sctl 0x%08lx\n", fRegs->sctl);
|
||||
TRACE("serr 0x%08lx\n", fRegs->serr);
|
||||
TRACE("sact 0x%08lx\n", fRegs->sact);
|
||||
TRACE("tfd 0x%08lx\n", fRegs->tfd);
|
||||
|
||||
fDevicePresent = (fRegs->ssts & 0xf) == 0x3;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::Uninit()
|
||||
{
|
||||
TRACE("AHCIPort::Uninit port %d\n", fIndex);
|
||||
|
||||
// disable FIS receive
|
||||
fRegs->cmd &= ~PORT_CMD_FER;
|
||||
|
||||
// wait for receive completition, up to 500ms
|
||||
if (wait_until_clear(&fRegs->cmd, PORT_CMD_FR, 500000) < B_OK) {
|
||||
TRACE("AHCIPort::Uninit port %d error FIS rx still running\n", fIndex);
|
||||
}
|
||||
|
||||
// stop DMA engine
|
||||
fRegs->cmd &= ~PORT_CMD_ST;
|
||||
|
||||
// wait for DMA completition
|
||||
if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) {
|
||||
TRACE("AHCIPort::Uninit port %d error DMA engine still running\n", fIndex);
|
||||
}
|
||||
|
||||
// disable interrupts
|
||||
fRegs->ie = 0;
|
||||
|
||||
// clear pending interrupts
|
||||
fRegs->is = fRegs->is;
|
||||
|
||||
// invalidate DMA addresses
|
||||
fRegs->clb = 0;
|
||||
fRegs->clbu = 0;
|
||||
fRegs->fb = 0;
|
||||
fRegs->fbu = 0;
|
||||
|
||||
delete_area(fArea);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AHCIPort::ResetDevice()
|
||||
{
|
||||
TRACE("AHCIPort::ResetDevice port %d\n", fIndex);
|
||||
|
||||
// stop DMA engine
|
||||
fRegs->cmd &= ~PORT_CMD_ST;
|
||||
FlushPostedWrites();
|
||||
|
||||
if (wait_until_clear(&fRegs->cmd, PORT_CMD_CR, 500000) < B_OK) {
|
||||
TRACE("AHCIPort::ResetDevice port %d error DMA engine doesn't stop\n", fIndex);
|
||||
}
|
||||
|
||||
// perform a hard reset
|
||||
fRegs->sctl = (fRegs->sctl & ~0xf) | 1;
|
||||
FlushPostedWrites();
|
||||
spin(1100);
|
||||
fRegs->sctl &= ~0xf;
|
||||
FlushPostedWrites();
|
||||
|
||||
if (wait_until_set(&fRegs->ssts, 0x1, 100000) < B_OK) {
|
||||
TRACE("AHCIPort::ResetDevice port %d no device detected\n", fIndex);
|
||||
}
|
||||
|
||||
// clear error bits
|
||||
fRegs->serr = fRegs->serr;
|
||||
FlushPostedWrites();
|
||||
|
||||
if (fRegs->ssts & 1) {
|
||||
if (wait_until_set(&fRegs->ssts, 0x3, 500000) < B_OK) {
|
||||
TRACE("AHCIPort::ResetDevice port %d device present but no phy communication\n", fIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// clear error bits
|
||||
fRegs->serr = fRegs->serr;
|
||||
FlushPostedWrites();
|
||||
|
||||
// start DMA engine
|
||||
fRegs->cmd |= PORT_CMD_ST;
|
||||
FlushPostedWrites();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AHCIPort::PostResetDevice()
|
||||
{
|
||||
TRACE("AHCIPort::PostResetDevice port %d\n", fIndex);
|
||||
|
||||
if ((fRegs->ssts & 0xf) != 0x3 || (fRegs->tfd & 0xff) == 0x7f) {
|
||||
TRACE("AHCIPort::PostResetDevice port %d: no device\n", fIndex);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if ((fRegs->tfd & 0xff) == 0xff)
|
||||
snooze(200000);
|
||||
|
||||
if ((fRegs->tfd & 0xff) == 0xff) {
|
||||
TRACE("AHCIPort::PostResetDevice port %d: invalid task file status 0xff\n", fIndex);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
wait_until_clear(&fRegs->tfd, ATA_BSY, 31000000);
|
||||
|
||||
fIsATAPI = fRegs->sig == 0xeb140101;
|
||||
|
||||
if (fIsATAPI)
|
||||
fRegs->cmd |= PORT_CMD_ATAPI;
|
||||
else
|
||||
fRegs->cmd &= ~PORT_CMD_ATAPI;
|
||||
FlushPostedWrites();
|
||||
|
||||
TRACE("device signature 0x%08lx (%s)\n", fRegs->sig,
|
||||
(fRegs->sig == 0xeb140101) ? "ATAPI" : (fRegs->sig == 0x00000101) ? "ATA" : "unknown");
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::DumpD2HFis()
|
||||
{
|
||||
TRACE("D2H FIS:\n");
|
||||
TRACE(" DW0 %02x %02x %02x %02x\n", fFIS->rfis[3], fFIS->rfis[2], fFIS->rfis[1], fFIS->rfis[0]);
|
||||
TRACE(" DW1 %02x %02x %02x %02x\n", fFIS->rfis[7], fFIS->rfis[6], fFIS->rfis[5], fFIS->rfis[4]);
|
||||
TRACE(" DW2 %02x %02x %02x %02x\n", fFIS->rfis[11], fFIS->rfis[10], fFIS->rfis[9], fFIS->rfis[8]);
|
||||
TRACE(" DW3 %02x %02x %02x %02x\n", fFIS->rfis[15], fFIS->rfis[14], fFIS->rfis[13], fFIS->rfis[12]);
|
||||
TRACE(" DW4 %02x %02x %02x %02x\n", fFIS->rfis[19], fFIS->rfis[18], fFIS->rfis[17], fFIS->rfis[16]);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::Interrupt()
|
||||
{
|
||||
uint32 is = fRegs->is;
|
||||
uint32 ci = fRegs->ci;
|
||||
fRegs->is = is; // clear interrupts
|
||||
|
||||
RWTRACE("AHCIPort::Interrupt port %d, fCommandsActive 0x%08lx, is 0x%08lx, ci 0x%08lx\n", fIndex, fCommandsActive, is, ci);
|
||||
|
||||
if (is & PORT_INT_ERROR)
|
||||
TRACE("AHCIPort::Interrupt port %d, fCommandsActive 0x%08lx, is 0x%08lx, ci 0x%08lx\n", fIndex, fCommandsActive, is, ci);
|
||||
|
||||
if (is & PORT_INT_FATAL)
|
||||
panic("ahci fatal error, is 0x%08lx", is);
|
||||
|
||||
int release = 0;
|
||||
|
||||
acquire_spinlock(&fSpinlock);
|
||||
if ((fCommandsActive & 1) && !(ci & 1)) {
|
||||
release = 1;
|
||||
fCommandsActive &= ~1;
|
||||
}
|
||||
release_spinlock(&fSpinlock);
|
||||
|
||||
if (release)
|
||||
release_sem_etc(fResponseSem, 1, B_RELEASE_IF_WAITING_ONLY | B_DO_NOT_RESCHEDULE);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AHCIPort::FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax, const void *data, size_t dataSize)
|
||||
{
|
||||
int peMax = prdMax + 1;
|
||||
physical_entry pe[peMax];
|
||||
if (get_memory_map(data, dataSize, pe, peMax ) < B_OK) {
|
||||
TRACE("AHCIPort::FillPrdTable get_memory_map failed\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
int peUsed;
|
||||
for (peUsed = 0; pe[peUsed].size; peUsed++)
|
||||
;
|
||||
return FillPrdTable(prdTable, prdCount, prdMax, pe, peUsed, dataSize);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AHCIPort::FillPrdTable(volatile prd *prdTable, int *prdCount, int prdMax, const physical_entry *sgTable, int sgCount, size_t dataSize)
|
||||
{
|
||||
*prdCount = 0;
|
||||
while (sgCount > 0 && dataSize > 0) {
|
||||
size_t size = min_c(sgTable->size, dataSize);
|
||||
void *address = sgTable->address;
|
||||
T(AHCIPortPrdTable(fController, fIndex, address, size));
|
||||
FLOW("FillPrdTable: sg-entry addr %p, size %lu\n", address, size);
|
||||
if ((uint32)address & 1) {
|
||||
TRACE("AHCIPort::FillPrdTable: data alignment error\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
dataSize -= size;
|
||||
while (size > 0) {
|
||||
size_t bytes = min_c(size, PRD_MAX_DATA_LENGTH);
|
||||
if (*prdCount == prdMax) {
|
||||
TRACE("AHCIPort::FillPrdTable: prd table exhausted\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
FLOW("FillPrdTable: prd-entry %u, addr %p, size %lu\n", *prdCount, address, bytes);
|
||||
prdTable->dba = LO32(address);
|
||||
prdTable->dbau = HI32(address);
|
||||
prdTable->res = 0;
|
||||
prdTable->dbc = bytes - 1;
|
||||
*prdCount += 1;
|
||||
prdTable++;
|
||||
address = (char *)address + bytes;
|
||||
size -= bytes;
|
||||
}
|
||||
sgTable++;
|
||||
sgCount--;
|
||||
}
|
||||
if (*prdCount == 0) {
|
||||
TRACE("AHCIPort::FillPrdTable: count is 0\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
if (dataSize > 0) {
|
||||
TRACE("AHCIPort::FillPrdTable: sg table %ld bytes too small\n", dataSize);
|
||||
return B_ERROR;
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::StartTransfer()
|
||||
{
|
||||
acquire_sem(fRequestSem);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
AHCIPort::WaitForTransfer(int *tfd, bigtime_t timeout)
|
||||
{
|
||||
status_t result = B_OK;
|
||||
if (acquire_sem_etc(fResponseSem, 1, B_RELATIVE_TIMEOUT, timeout) < B_OK) {
|
||||
fCommandsActive &= ~1;
|
||||
result = B_TIMED_OUT;
|
||||
} else {
|
||||
*tfd = fRegs->tfd;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::FinishTransfer()
|
||||
{
|
||||
release_sem(fRequestSem);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::ScsiTestUnitReady(scsi_ccb *request)
|
||||
{
|
||||
TRACE("AHCIPort::ScsiTestUnitReady port %d\n", fIndex);
|
||||
request->subsys_status = SCSI_REQ_CMP;
|
||||
gSCSI->finished(request, 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::ScsiInquiry(scsi_ccb *request)
|
||||
{
|
||||
TRACE("AHCIPort::ScsiInquiry port %d\n", fIndex);
|
||||
|
||||
scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)request->cdb;
|
||||
scsi_res_inquiry scsiData;
|
||||
ata_res_identify_device ataData;
|
||||
|
||||
ASSERT(sizeof(ataData) == 512);
|
||||
|
||||
if (cmd->evpd || cmd->page_code || request->data_length < sizeof(scsiData)) {
|
||||
TRACE("invalid request\n");
|
||||
request->subsys_status = SCSI_REQ_ABORTED;
|
||||
gSCSI->finished(request, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
sata_request sreq;
|
||||
sreq.set_data(&ataData, sizeof(ataData));
|
||||
sreq.set_ata_cmd(fIsATAPI ? 0xa1 : 0xec); // Identify (Packet) Device
|
||||
ExecuteSataRequest(&sreq);
|
||||
sreq.wait_for_completition();
|
||||
|
||||
if (sreq.completition_status() & ATA_ERR) {
|
||||
TRACE("identify device failed\n");
|
||||
request->subsys_status = SCSI_REQ_CMP_ERR;
|
||||
gSCSI->finished(request, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
uint8 *data = (uint8*) &ataData;
|
||||
for (int i = 0; i < 512; i += 8) {
|
||||
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]);
|
||||
}
|
||||
|
||||
|
||||
scsiData.device_type = fIsATAPI ? scsi_dev_CDROM : scsi_dev_direct_access;
|
||||
scsiData.device_qualifier = scsi_periph_qual_connected;
|
||||
scsiData.device_type_modifier = 0;
|
||||
scsiData.removable_medium = fIsATAPI;
|
||||
scsiData.ansi_version = 2;
|
||||
scsiData.ecma_version = 0;
|
||||
scsiData.iso_version = 0;
|
||||
scsiData.response_data_format = 2;
|
||||
scsiData.term_iop = false;
|
||||
scsiData.additional_length = sizeof(scsiData) - 4;
|
||||
scsiData.soft_reset = false;
|
||||
scsiData.cmd_queue = false;
|
||||
scsiData.linked = false;
|
||||
scsiData.sync = false;
|
||||
scsiData.write_bus16 = true;
|
||||
scsiData.write_bus32 = false;
|
||||
scsiData.relative_address = false;
|
||||
memcpy(scsiData.vendor_ident, ataData.model_number, sizeof(scsiData.vendor_ident));
|
||||
memcpy(scsiData.product_ident, ataData.model_number + 8, sizeof(scsiData.product_ident));
|
||||
memcpy(scsiData.product_rev, ataData.serial_number, sizeof(scsiData.product_rev));
|
||||
|
||||
if (!fIsATAPI) {
|
||||
bool lba = (ataData.words[49] & (1 << 9)) != 0;
|
||||
bool lba48 = (ataData.words[83] & (1 << 10)) != 0;
|
||||
uint32 sectors = *(uint32*)&ataData.words[60];
|
||||
uint64 sectors48 = *(uint64*)&ataData.words[100];
|
||||
fUse48BitCommands = lba && lba48;
|
||||
fSectorSize = 512;
|
||||
fSectorCount = !(lba || sectors) ? 0 : lba48 ? sectors48 : sectors;
|
||||
TRACE("lba %d, lba48 %d, fUse48BitCommands %d, sectors %lu, sectors48 %llu, size %llu\n",
|
||||
lba, lba48, fUse48BitCommands, sectors, sectors48, fSectorCount * fSectorSize);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (fSectorCount < 0x0fffffff) {
|
||||
TRACE("disabling 48 bit commands\n");
|
||||
fUse48BitCommands = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
char modelNumber[sizeof(ataData.model_number) + 1];
|
||||
char serialNumber[sizeof(ataData.serial_number) + 1];
|
||||
char firmwareRev[sizeof(ataData.firmware_revision) + 1];
|
||||
|
||||
strlcpy(modelNumber, ataData.model_number, sizeof(modelNumber));
|
||||
strlcpy(serialNumber, ataData.serial_number, sizeof(serialNumber));
|
||||
strlcpy(firmwareRev, ataData.firmware_revision, sizeof(firmwareRev));
|
||||
|
||||
swap_words(modelNumber, sizeof(modelNumber) - 1);
|
||||
swap_words(serialNumber, sizeof(serialNumber) - 1);
|
||||
swap_words(firmwareRev, sizeof(firmwareRev) - 1);
|
||||
|
||||
TRACE("model number: %s\n", modelNumber);
|
||||
TRACE("serial number: %s\n", serialNumber);
|
||||
TRACE("firmware rev.: %s\n", firmwareRev);
|
||||
|
||||
if (sg_memcpy(request->sg_list, request->sg_count, &scsiData, sizeof(scsiData)) < B_OK) {
|
||||
request->subsys_status = SCSI_DATA_RUN_ERR;
|
||||
} else {
|
||||
request->subsys_status = SCSI_REQ_CMP;
|
||||
request->data_resid = request->data_length - sizeof(scsiData);
|
||||
}
|
||||
gSCSI->finished(request, 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::ScsiSynchronizeCache(scsi_ccb *request)
|
||||
{
|
||||
TRACE("AHCIPort::ScsiSynchronizeCache port %d\n", fIndex);
|
||||
|
||||
sata_request *sreq = new(std::nothrow) sata_request(request);
|
||||
sreq->set_ata_cmd(fUse48BitCommands ? 0xea : 0xe7); // Flush Cache
|
||||
ExecuteSataRequest(sreq);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::ScsiReadCapacity(scsi_ccb *request)
|
||||
{
|
||||
TRACE("AHCIPort::ScsiReadCapacity port %d\n", fIndex);
|
||||
|
||||
scsi_cmd_read_capacity *cmd = (scsi_cmd_read_capacity *)request->cdb;
|
||||
scsi_res_read_capacity scsiData;
|
||||
|
||||
if (cmd->pmi || cmd->lba || request->data_length < sizeof(scsiData)) {
|
||||
TRACE("invalid request\n");
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE("SectorSize %lu, SectorCount 0x%llx\n", fSectorSize, fSectorCount);
|
||||
|
||||
if (fSectorCount > 0xffffffff)
|
||||
panic("ahci: SCSI emulation doesn't support harddisks larger than 2TB");
|
||||
|
||||
scsiData.block_size = B_HOST_TO_BENDIAN_INT32(fSectorSize);
|
||||
scsiData.lba = B_HOST_TO_BENDIAN_INT32(fSectorCount - 1);
|
||||
|
||||
if (sg_memcpy(request->sg_list, request->sg_count, &scsiData, sizeof(scsiData)) < B_OK) {
|
||||
request->subsys_status = SCSI_DATA_RUN_ERR;
|
||||
} else {
|
||||
request->subsys_status = SCSI_REQ_CMP;
|
||||
request->data_resid = request->data_length - sizeof(scsiData);
|
||||
}
|
||||
gSCSI->finished(request, 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::ScsiReadWrite(scsi_ccb *request, uint64 lba, size_t sectorCount, bool isWrite)
|
||||
{
|
||||
RWTRACE("ScsiReadWrite: position %llu, size %lu, isWrite %d\n", lba * 512, sectorCount * 512, isWrite);
|
||||
|
||||
#if 0
|
||||
if (isWrite) {
|
||||
TRACE("write request ignored\n");
|
||||
request->subsys_status = SCSI_REQ_CMP;
|
||||
request->data_resid = 0;
|
||||
gSCSI->finished(request, 1);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ASSERT(request->data_length == sectorCount * 512);
|
||||
sata_request *sreq = new(std::nothrow) sata_request(request);
|
||||
|
||||
if (fUse48BitCommands) {
|
||||
if (sectorCount > 65536)
|
||||
panic("ahci: ScsiReadWrite length too large, %lu sectors", sectorCount);
|
||||
if (lba > MAX_SECTOR_LBA_48)
|
||||
panic("achi: ScsiReadWrite position too large for 48-bit LBA\n");
|
||||
sreq->set_ata48_cmd(isWrite ? 0x35 : 0x25, lba, sectorCount);
|
||||
} else {
|
||||
if (sectorCount > 256)
|
||||
panic("ahci: ScsiReadWrite length too large, %lu sectors", sectorCount);
|
||||
if (lba > MAX_SECTOR_LBA_28)
|
||||
panic("achi: ScsiReadWrite position too large for normal LBA\n");
|
||||
sreq->set_ata28_cmd(isWrite ? 0xca : 0xc8, lba, sectorCount);
|
||||
}
|
||||
|
||||
ExecuteSataRequest(sreq, isWrite);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::ExecuteSataRequest(sata_request *request, bool isWrite)
|
||||
{
|
||||
FLOW("ExecuteAtaRequest port %d\n", fIndex);
|
||||
|
||||
StartTransfer();
|
||||
|
||||
int prdEntrys;
|
||||
|
||||
if (request->ccb())
|
||||
FillPrdTable(fPRDTable, &prdEntrys, PRD_TABLE_ENTRY_COUNT, request->ccb()->sg_list, request->ccb()->sg_count, request->ccb()->data_length);
|
||||
else if (request->data() && request->size())
|
||||
FillPrdTable(fPRDTable, &prdEntrys, PRD_TABLE_ENTRY_COUNT, request->data(), request->size());
|
||||
else
|
||||
prdEntrys = 0;
|
||||
|
||||
FLOW("prdEntrys %d\n", prdEntrys);
|
||||
|
||||
memcpy((char *)fCommandTable->cfis, request->fis(), 20);
|
||||
fCommandList->prdtl_flags_cfl = 0;
|
||||
fCommandList->cfl = 5; // length is 20 bytes, in DWORDS
|
||||
if (isWrite)
|
||||
fCommandList->w = 1;
|
||||
fCommandList->prdtl = prdEntrys;
|
||||
fCommandList->prdbc = 0;
|
||||
|
||||
if (wait_until_clear(&fRegs->tfd, ATA_BSY | ATA_DRQ, 1000000) < B_OK) {
|
||||
TRACE("ExecuteAtaRequest port %d: device is busy\n", fIndex);
|
||||
FinishTransfer();
|
||||
request->abort();
|
||||
return;
|
||||
}
|
||||
|
||||
cpu_status cpu = disable_interrupts();
|
||||
acquire_spinlock(&fSpinlock);
|
||||
fRegs->ci = 1;
|
||||
FlushPostedWrites();
|
||||
fCommandsActive |= 1;
|
||||
release_spinlock(&fSpinlock);
|
||||
restore_interrupts(cpu);
|
||||
|
||||
int tfd;
|
||||
status_t status = WaitForTransfer(&tfd, 5000000);
|
||||
|
||||
FLOW("tfd %#x\n", tfd);
|
||||
FLOW("prdbc %ld\n", fCommandList->prdbc);
|
||||
FLOW("ci 0x%08lx\n", fRegs->ci);
|
||||
FLOW("is 0x%08lx\n", fRegs->is);
|
||||
FLOW("serr 0x%08lx\n", fRegs->serr);
|
||||
|
||||
/*
|
||||
TRACE("ci 0x%08lx\n", fRegs->ci);
|
||||
TRACE("ie 0x%08lx\n", fRegs->ie);
|
||||
TRACE("is 0x%08lx\n", fRegs->is);
|
||||
TRACE("cmd 0x%08lx\n", fRegs->cmd);
|
||||
TRACE("ssts 0x%08lx\n", fRegs->ssts);
|
||||
TRACE("sctl 0x%08lx\n", fRegs->sctl);
|
||||
TRACE("serr 0x%08lx\n", fRegs->serr);
|
||||
TRACE("sact 0x%08lx\n", fRegs->sact);
|
||||
TRACE("tfd 0x%08lx\n", fRegs->tfd);
|
||||
*/
|
||||
|
||||
size_t bytesTransfered = fCommandList->prdbc;
|
||||
|
||||
FinishTransfer();
|
||||
|
||||
if (status < B_OK) {
|
||||
TRACE("ExecuteAtaRequest port %d: device transfer timeout\n", fIndex);
|
||||
request->abort();
|
||||
} else
|
||||
request->finish(tfd, bytesTransfered);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::ScsiExecuteRequest(scsi_ccb *request)
|
||||
{
|
||||
|
||||
// TRACE("AHCIPort::ScsiExecuteRequest port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);
|
||||
|
||||
if (request->cdb[0] == SCSI_OP_REQUEST_SENSE) {
|
||||
panic("ahci: SCSI_OP_REQUEST_SENSE not yet supported\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fDevicePresent) {
|
||||
TRACE("no device present on port %d\n", fIndex);
|
||||
request->subsys_status = SCSI_DEV_NOT_THERE;
|
||||
gSCSI->finished(request, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
request->subsys_status = SCSI_REQ_CMP;
|
||||
|
||||
switch (request->cdb[0]) {
|
||||
case SCSI_OP_TEST_UNIT_READY:
|
||||
ScsiTestUnitReady(request);
|
||||
break;
|
||||
case SCSI_OP_INQUIRY:
|
||||
ScsiInquiry(request);
|
||||
break;
|
||||
case SCSI_OP_READ_CAPACITY:
|
||||
ScsiReadCapacity(request);
|
||||
break;
|
||||
case SCSI_OP_SYNCHRONIZE_CACHE:
|
||||
ScsiSynchronizeCache(request);
|
||||
break;
|
||||
case SCSI_OP_READ_6:
|
||||
case SCSI_OP_WRITE_6:
|
||||
{
|
||||
scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
|
||||
uint32 position = ((uint32)cmd->high_lba << 16) | ((uint32)cmd->mid_lba << 8) | (uint32)cmd->low_lba;
|
||||
size_t length = cmd->length != 0 ? cmd->length : 256;
|
||||
bool isWrite = request->cdb[0] == SCSI_OP_WRITE_6;
|
||||
ScsiReadWrite(request, position, length, isWrite);
|
||||
break;
|
||||
}
|
||||
case SCSI_OP_READ_10:
|
||||
case SCSI_OP_WRITE_10:
|
||||
{
|
||||
scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
|
||||
uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba);
|
||||
size_t length = B_BENDIAN_TO_HOST_INT16(cmd->length);
|
||||
bool isWrite = request->cdb[0] == SCSI_OP_WRITE_10;
|
||||
if (length) {
|
||||
ScsiReadWrite(request, position, length, isWrite);
|
||||
} else {
|
||||
TRACE("AHCIPort::ScsiExecuteRequest error: transfer without data!\n");
|
||||
request->subsys_status = SCSI_REQ_INVALID;
|
||||
gSCSI->finished(request, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SCSI_OP_READ_12:
|
||||
case SCSI_OP_WRITE_12:
|
||||
{
|
||||
scsi_cmd_rw_12 *cmd = (scsi_cmd_rw_12 *)request->cdb;
|
||||
uint32 position = B_BENDIAN_TO_HOST_INT32(cmd->lba);
|
||||
size_t length = B_BENDIAN_TO_HOST_INT32(cmd->length);
|
||||
bool isWrite = request->cdb[0] == SCSI_OP_WRITE_12;
|
||||
if (length) {
|
||||
ScsiReadWrite(request, position, length, isWrite);
|
||||
} else {
|
||||
TRACE("AHCIPort::ScsiExecuteRequest error: transfer without data!\n");
|
||||
request->subsys_status = SCSI_REQ_INVALID;
|
||||
gSCSI->finished(request, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
TRACE("AHCIPort::ScsiExecuteRequest port %d unsupported request opcode 0x%02x\n", fIndex, request->cdb[0]);
|
||||
request->subsys_status = SCSI_REQ_ABORTED;
|
||||
gSCSI->finished(request, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uchar
|
||||
AHCIPort::ScsiAbortRequest(scsi_ccb *request)
|
||||
{
|
||||
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
uchar
|
||||
AHCIPort::ScsiTerminateRequest(scsi_ccb *request)
|
||||
{
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
uchar
|
||||
AHCIPort::ScsiResetDevice()
|
||||
{
|
||||
return SCSI_REQ_CMP;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AHCIPort::ScsiGetRestrictions(bool *isATAPI, bool *noAutoSense, uint32 *maxBlocks)
|
||||
{
|
||||
*isATAPI = fIsATAPI;
|
||||
*noAutoSense = false;
|
||||
*maxBlocks = fUse48BitCommands ? 65536 : 256;
|
||||
TRACE("AHCIPort::ScsiGetRestrictions port %d: isATAPI %d, noAutoSense %d, maxBlocks %lu\n",
|
||||
fIndex, *isATAPI, *noAutoSense, *maxBlocks);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user