Beispiel #1
0
void
AHCIPort::ScsiReadWrite(scsi_ccb *request, uint64 lba, size_t sectorCount, bool isWrite)
{
	RWTRACE("[%lld] %ld ScsiReadWrite: position %llu, size %lu, isWrite %d\n",
		system_time(), find_thread(NULL), 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);
}
Beispiel #2
0
void
AHCIPort::ScsiUnmap(scsi_ccb* request, scsi_unmap_parameter_list* unmapBlocks)
{
	// Determine how many ranges we'll need
	// We assume that the SCSI unmap ranges cannot be merged together
	uint32 scsiRangeCount = B_BENDIAN_TO_HOST_INT16(
		unmapBlocks->block_data_length) / sizeof(scsi_unmap_block_descriptor);
	uint32 lbaRangeCount = 0;
	for (uint32 i = 0; i < scsiRangeCount; i++) {
		lbaRangeCount += ((uint32)B_BENDIAN_TO_HOST_INT32(
			unmapBlocks->blocks[i].block_count) + 65534) / 65535;
	}

	uint32 lbaRangesSize = lbaRangeCount * sizeof(uint64);
	uint64* lbaRanges = (uint64*)malloc(lbaRangesSize);
	if (lbaRanges == NULL) {
		TRACE("out of memory when allocating %" B_PRIu32 " unmap ranges\n",
			lbaRangeCount);
		request->subsys_status = SCSI_REQ_ABORTED;
		gSCSI->finished(request, 1);
		return;
	}

	MemoryDeleter deleter(lbaRanges);

	for (uint32 i = 0, scsiIndex = 0; scsiIndex < scsiRangeCount; scsiIndex++) {
		uint64 lba = (uint64)B_BENDIAN_TO_HOST_INT64(
			unmapBlocks->blocks[scsiIndex].lba);
		uint64 blocksLeft = (uint32)B_BENDIAN_TO_HOST_INT32(
			unmapBlocks->blocks[scsiIndex].block_count);
		while (blocksLeft > 0) {
			uint16 blocks = blocksLeft > 65535 ? 65535 : (uint16)blocksLeft;
			lbaRanges[i++] = B_HOST_TO_LENDIAN_INT64(
				((uint64)blocks << 48) | lba);
			lba += blocks;
			blocksLeft -= blocks;
		}
	}

	sata_request sreq;
	sreq.SetATA48Command(ATA_COMMAND_DATA_SET_MANAGEMENT, 0,
		(lbaRangesSize + 511) / 512);
	sreq.SetFeature(1);
	sreq.SetData(lbaRanges, lbaRangesSize);

	ExecuteSataRequest(&sreq);
	sreq.WaitForCompletion();

	if ((sreq.CompletionStatus() & ATA_ERR) != 0) {
		TRACE("trim failed (%" B_PRIu32 " ranges)!\n", lbaRangeCount);
		request->subsys_status = SCSI_REQ_CMP_ERR;
	} else
		request->subsys_status = SCSI_REQ_CMP;

	request->data_resid = 0;
	request->device_status = SCSI_STATUS_GOOD;
	gSCSI->finished(request, 1);
}
Beispiel #3
0
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);
}
Beispiel #4
0
void
AHCIPort::ScsiReadWrite(scsi_ccb* request, uint64 lba, size_t sectorCount,
	bool isWrite)
{
	RWTRACE("[%lld] %ld ScsiReadWrite: position %llu, size %lu, isWrite %d\n",
		system_time(), find_thread(NULL), 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 (sreq == NULL) {
		TRACE("out of memory when allocating read/write request\n");
		request->subsys_status = SCSI_REQ_ABORTED;
		gSCSI->finished(request, 1);
		return;
	}

	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->SetATA48Command(
			isWrite ? ATA_COMMAND_WRITE_DMA_EXT : ATA_COMMAND_READ_DMA_EXT,
			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->SetATA28Command(isWrite
			? ATA_COMMAND_WRITE_DMA : ATA_COMMAND_READ_DMA, lba, sectorCount);
	}

	ExecuteSataRequest(sreq, isWrite);
}
Beispiel #5
0
void
AHCIPort::ScsiSynchronizeCache(scsi_ccb *request)
{
    //TRACE("AHCIPort::ScsiSynchronizeCache port %d\n", fIndex);

    sata_request *sreq = new(std::nothrow) sata_request(request);
    if (sreq == NULL) {
        TRACE("out of memory when allocating sync request\n");
        request->subsys_status = SCSI_REQ_ABORTED;
        gSCSI->finished(request, 1);
        return;
    }

    sreq->set_ata_cmd(fUse48BitCommands ? 0xea : 0xe7); // Flush Cache
    ExecuteSataRequest(sreq);
}
Beispiel #6
0
void
AHCIPort::ScsiSynchronizeCache(scsi_ccb* request)
{
	//TRACE("AHCIPort::ScsiSynchronizeCache port %d\n", fIndex);

	sata_request* sreq = new(std::nothrow) sata_request(request);
	if (sreq == NULL) {
		TRACE("out of memory when allocating sync request\n");
		request->subsys_status = SCSI_REQ_ABORTED;
		gSCSI->finished(request, 1);
		return;
	}

	sreq->SetATACommand(fUse48BitCommands
		? ATA_COMMAND_FLUSH_CACHE_EXT : ATA_COMMAND_FLUSH_CACHE);
	ExecuteSataRequest(sreq);
}
Beispiel #7
0
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 (fIsATAPI) {
        bool isWrite = false;
        switch (request->flags & SCSI_DIR_MASK) {
        case SCSI_DIR_NONE:
            ASSERT(request->data_length == 0);
            break;
        case SCSI_DIR_IN:
            ASSERT(request->data_length > 0);
            break;
        case SCSI_DIR_OUT:
            isWrite = true;
            ASSERT(request->data_length > 0);
            break;
        default:
            panic("CDB has invalid direction mask");
        }

//		TRACE("AHCIPort::ScsiExecuteRequest ATAPI: port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);

        sata_request *sreq = new(std::nothrow) sata_request(request);
        if (sreq == NULL) {
            TRACE("out of memory when allocating atapi request\n");
            request->subsys_status = SCSI_REQ_ABORTED;
            gSCSI->finished(request, 1);
            return;
        }

        sreq->set_atapi_cmd(request->data_length);
//		uint8 *data = (uint8*) sreq->ccb()->cdb;
//		for (int i = 0; i < 16; 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]);
//		}
        ExecuteSataRequest(sreq, isWrite);
        return;
    }

    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_SERVICE_ACTION_IN:
        if ((request->cdb[1] & 0x1f) == SCSI_SAI_READ_CAPACITY_16)
            ScsiReadCapacity16(request);
        else {
            request->subsys_status = SCSI_REQ_INVALID;
            gSCSI->finished(request, 1);
        }
        break;
    case SCSI_OP_SYNCHRONIZE_CACHE:
        ScsiSynchronizeCache(request);
        break;
    case SCSI_OP_READ_6:
    case SCSI_OP_WRITE_6:
    {
        const scsi_cmd_rw_6 *cmd = (const 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:
    {
        const scsi_cmd_rw_10 *cmd = (const 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:
    {
        const scsi_cmd_rw_12 *cmd = (const 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;
    }
    case SCSI_OP_READ_16:
    case SCSI_OP_WRITE_16:
    {
        const scsi_cmd_rw_16 *cmd = (const scsi_cmd_rw_16 *)request->cdb;
        uint64 position = B_BENDIAN_TO_HOST_INT64(cmd->lba);
        size_t length = B_BENDIAN_TO_HOST_INT32(cmd->length);
        bool isWrite = request->cdb[0] == SCSI_OP_WRITE_16;
        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_WRITE_SAME_16:
    {
        const scsi_cmd_wsame_16 *cmd = (const scsi_cmd_wsame_16 *)request->cdb;

        // SCSI unmap is used for trim, otherwise we don't support it
        if (!cmd->unmap) {
            TRACE("%s port %d: unsupported request opcode 0x%02x\n",
                  __func__, fIndex, request->cdb[0]);
            request->subsys_status = SCSI_REQ_ABORTED;
            gSCSI->finished(request, 1);
            break;
        }

        if (!fTrim) {
            // Drive doesn't support trim (or atapi)
            // Just say it was successful and quit
            request->subsys_status = SCSI_REQ_CMP;
        } else {
            TRACE("%s unimplemented: TRIM call\n", __func__);
            // TODO: Make Serial ATA (sata_request?) trim call here.
            request->subsys_status = SCSI_REQ_ABORTED;
        }
        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);
    }
}
Beispiel #8
0
void
AHCIPort::ScsiInquiry(scsi_ccb *request)
{
    TRACE("AHCIPort::ScsiInquiry port %d\n", fIndex);

    const scsi_cmd_inquiry *cmd = (const scsi_cmd_inquiry *)request->cdb;
    scsi_res_inquiry scsiData;
    ata_device_infoblock 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_completion();

    if (sreq.completion_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
                           ? ataData.word_0.atapi.command_packet_set : scsi_dev_direct_access;
    scsiData.device_qualifier = scsi_periph_qual_connected;
    scsiData.device_type_modifier = 0;
    scsiData.removable_medium = ataData.word_0.ata.removable_media_device;
    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(scsi_res_inquiry) - 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;

    if (!fIsATAPI) {
        fSectorCount = ataData.SectorCount(fUse48BitCommands, true);
        fSectorSize = ataData.SectorSize();
        fTrim = ataData.data_set_management_support;
        TRACE("lba %d, lba48 %d, fUse48BitCommands %d, sectors %" B_PRIu32
              ", sectors48 %" B_PRIu64 ", size %" B_PRIu64 "\n",
              ataData.dma_supported != 0, ataData.lba48_supported != 0,
              fUse48BitCommands, ataData.lba_sector_count,
              ataData.lba48_sector_count, 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);
    TRACE("trim support: %s\n", fTrim ? "yes" : "no");

    // There's not enough space to fit all of the data in. ATA has 40 bytes for
    // the model number, 20 for the serial number and another 8 for the
    // firmware revision. SCSI has room for 8 for vendor ident, 16 for product
    // ident and another 4 for product revision. We just try and fit in as much
    // as possible of the model number into the vendor and product ident fields
    // and put a little of the serial number into the product revision field.
    memcpy(scsiData.vendor_ident, modelNumber, sizeof(scsiData.vendor_ident));
    memcpy(scsiData.product_ident, modelNumber + 8,
           sizeof(scsiData.product_ident));
    memcpy(scsiData.product_rev, serialNumber, sizeof(scsiData.product_rev));

    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);
}
Beispiel #9
0
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 (fIsATAPI) {
		bool isWrite = false;
		switch (request->flags & SCSI_DIR_MASK) {
			case SCSI_DIR_NONE:
				ASSERT(request->data_length == 0);
				break;
			case SCSI_DIR_IN:
				ASSERT(request->data_length > 0);
				break;
			case SCSI_DIR_OUT:
				isWrite = true;
				ASSERT(request->data_length > 0);
				break;
			default:
				panic("CDB has invalid direction mask");
		}
	
//		TRACE("AHCIPort::ScsiExecuteRequest ATAPI: port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);

		sata_request *sreq = new(std::nothrow) sata_request(request);
		sreq->set_atapi_cmd(request->data_length);
//		uint8 *data = (uint8*) sreq->ccb()->cdb;
//		for (int i = 0; i < 16; 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]);
//		}
		ExecuteSataRequest(sreq, isWrite);
		return;
	}

	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);
	}
}
Beispiel #10
0
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);
}
Beispiel #11
0
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 (fIsATAPI) {
		bool isWrite = false;
		switch (request->flags & SCSI_DIR_MASK) {
			case SCSI_DIR_NONE:
				ASSERT(request->data_length == 0);
				break;
			case SCSI_DIR_IN:
				ASSERT(request->data_length > 0);
				break;
			case SCSI_DIR_OUT:
				isWrite = true;
				ASSERT(request->data_length > 0);
				break;
			default:
				panic("CDB has invalid direction mask");
		}

//		TRACE("AHCIPort::ScsiExecuteRequest ATAPI: port %d, opcode 0x%02x, length %u\n", fIndex, request->cdb[0], request->cdb_length);

		sata_request* sreq = new(std::nothrow) sata_request(request);
		if (sreq == NULL) {
			TRACE("out of memory when allocating atapi request\n");
			request->subsys_status = SCSI_REQ_ABORTED;
			gSCSI->finished(request, 1);
			return;
		}

		sreq->SetATAPICommand(request->data_length);
//		uint8* data = (uint8*) sreq->ccb()->cdb;
//		for (int i = 0; i < 16; 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]);
//		}
		ExecuteSataRequest(sreq, isWrite);
		return;
	}

	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_SERVICE_ACTION_IN:
			if ((request->cdb[1] & 0x1f) == SCSI_SAI_READ_CAPACITY_16)
				ScsiReadCapacity16(request);
			else {
				request->subsys_status = SCSI_REQ_INVALID;
				gSCSI->finished(request, 1);
			}
			break;
		case SCSI_OP_SYNCHRONIZE_CACHE:
			ScsiSynchronizeCache(request);
			break;
		case SCSI_OP_READ_6:
		case SCSI_OP_WRITE_6:
		{
			const scsi_cmd_rw_6* cmd = (const 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:
		{
			const scsi_cmd_rw_10* cmd = (const 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:
		{
			const scsi_cmd_rw_12* cmd = (const 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;
		}
		case SCSI_OP_READ_16:
		case SCSI_OP_WRITE_16:
		{
			const scsi_cmd_rw_16* cmd = (const scsi_cmd_rw_16*)request->cdb;
			uint64 position = B_BENDIAN_TO_HOST_INT64(cmd->lba);
			size_t length = B_BENDIAN_TO_HOST_INT32(cmd->length);
			bool isWrite = request->cdb[0] == SCSI_OP_WRITE_16;
			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_UNMAP:
		{
			const scsi_cmd_unmap* cmd = (const scsi_cmd_unmap*)request->cdb;

			if (!fTrimSupported) {
				TRACE("%s port %d: unsupported request opcode 0x%02x\n",
					__func__, fIndex, request->cdb[0]);
				request->subsys_status = SCSI_REQ_ABORTED;
				gSCSI->finished(request, 1);
				break;
			}

			scsi_unmap_parameter_list* unmapBlocks
				= (scsi_unmap_parameter_list*)request->data;
			if (unmapBlocks == NULL
				|| B_BENDIAN_TO_HOST_INT16(cmd->length) != request->data_length
				|| B_BENDIAN_TO_HOST_INT16(unmapBlocks->data_length)
					!= request->data_length - 1) {
				TRACE("%s port %d: invalid unmap parameter data length\n",
					__func__, fIndex);
				request->subsys_status = SCSI_REQ_ABORTED;
				gSCSI->finished(request, 1);
			} else {
				ScsiUnmap(request, unmapBlocks);
			}
			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);
	}
}