diff --git a/headers/private/drivers/scsi_cmds.h b/headers/private/drivers/scsi_cmds.h index 28f5fb5295..078f960ef6 100644 --- a/headers/private/drivers/scsi_cmds.h +++ b/headers/private/drivers/scsi_cmds.h @@ -313,7 +313,7 @@ typedef struct scsi_page_usn { char psn[1]; // size according to page_length } _PACKED scsi_page_usn; -// READ CAPACITY +// READ CAPACITY (10) typedef struct scsi_cmd_read_capacity { uint8 opcode; @@ -336,6 +336,22 @@ typedef struct scsi_res_read_capacity { uint32 block_size; // in bytes } _PACKED scsi_res_read_capacity; +// READ CAPACITY (16) + +typedef struct scsi_cmd_read_capacity_long { + uint8 opcode; + uint8 service_action; + uint64 lba; + uint32 alloc_length; + uint8 relative_address; + uint8 control; +} _PACKED scsi_cmd_read_capacity_long; + +typedef struct scsi_res_read_capacity_long { + uint64 lba; // big endian + uint32 block_size; // in bytes +} _PACKED scsi_res_read_capacity_long; + // READ (6), WRITE (6) diff --git a/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp b/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp index 459677287f..5457dc5376 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp @@ -182,6 +182,26 @@ ATADevice::ReadCapacity(ATARequest *request) uint32 lastBlock = fTotalSectors - 1; data.lba = B_HOST_TO_BENDIAN_INT32(lastBlock); TRACE("returning last block: %lu\n", B_BENDIAN_TO_HOST_INT32(data.lba)); + + copy_sg_data(ccb, 0, ccb->data_length, &data, sizeof(data), false); + ccb->data_resid = MAX(ccb->data_length - sizeof(data), 0); + return B_OK; +} + + +status_t +ATADevice::ReadCapacity16(ATARequest *request) +{ + TRACE_FUNCTION("%p\n", request); + + scsi_ccb *ccb = request->CCB(); + scsi_res_read_capacity_long data; + data.block_size = B_HOST_TO_BENDIAN_INT32(fBlockSize); + + uint64 lastBlock = fTotalSectors - 1; + data.lba = (((uint64)B_HOST_TO_BENDIAN_INT32(lastBlock >> 32)) << 32) + | B_HOST_TO_BENDIAN_INT32(lastBlock); + TRACE("returning last block: %llu\n", data.lba); copy_sg_data(ccb, 0, ccb->data_length, &data, sizeof(data), false); ccb->data_resid = MAX(ccb->data_length - sizeof(data), 0); @@ -251,6 +271,11 @@ ATADevice::ExecuteIO(ATARequest *request) case SCSI_OP_READ_CAPACITY: return ReadCapacity(request); + case SCSI_OP_SERVICE_ACTION_IN: + if ((ccb->cdb[1] & 0x1f) == SCSI_SAI_READ_CAPACITY_16) + return ReadCapacity16(request); + break; + case SCSI_OP_SYNCHRONIZE_CACHE: // we ignore range and immediate bit, we always immediately // flush everything @@ -443,7 +468,8 @@ ATADevice::Configure() } } - if (!fInfoBlock.lba_supported || fInfoBlock.lba_sector_count == 0) { + if (!fInfoBlock.lba_supported || (fInfoBlock.lba_sector_count == 0 + && fInfoBlock.lba48_sector_count == 0)) { TRACE_ERROR("non-lba devices not supported\n"); return B_ERROR; } diff --git a/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h b/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h index c84d90d95f..8f01a4fba7 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h +++ b/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h @@ -176,6 +176,7 @@ public: status_t Eject(ATARequest *request); status_t Inquiry(ATARequest *request); status_t ReadCapacity(ATARequest *request); + status_t ReadCapacity16(ATARequest *request); virtual status_t ExecuteIO(ATARequest *request); void GetRestrictions(bool *noAutoSense, diff --git a/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp b/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp index 39564027db..0f2fe7f220 100644 --- a/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp +++ b/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp @@ -92,9 +92,16 @@ get_geometry(das_handle* handle, device_geometry* geometry) return status; geometry->bytes_per_sector = info->block_size; - geometry->sectors_per_track = 1; - geometry->cylinder_count = info->capacity; - geometry->head_count = 1; + if (info->capacity > UINT_MAX) { + // TODO this doesn't work for capacity greater than 35TB + geometry->sectors_per_track = 256; + geometry->cylinder_count = info->capacity / (256 * 32); + geometry->head_count = 32; + } else { + geometry->sectors_per_track = 1; + geometry->cylinder_count = info->capacity; + geometry->head_count = 1; + } geometry->device_type = B_DISK; geometry->removable = info->removable; @@ -323,7 +330,7 @@ das_ioctl(void* cookie, uint32 op, void* buffer, size_t length) das_handle* handle = (das_handle*)cookie; das_driver_info* info = handle->info; - TRACE("ioctl(op = %d)\n", op); + TRACE("ioctl(op = %ld)\n", op); switch (op) { case B_GET_DEVICE_SIZE: @@ -398,7 +405,7 @@ static void das_set_capacity(das_driver_info* info, uint64 capacity, uint32 blockSize) { TRACE("das_set_capacity(device = %p, capacity = %Ld, blockSize = %ld)\n", - device, capacity, blockSize); + info, capacity, blockSize); // get log2, if possible uint32 blockShift = log2(blockSize); diff --git a/src/add-ons/kernel/generic/scsi_periph/block.cpp b/src/add-ons/kernel/generic/scsi_periph/block.cpp index 8940fc960d..a896557b90 100644 --- a/src/add-ons/kernel/generic/scsi_periph/block.cpp +++ b/src/add-ons/kernel/generic/scsi_periph/block.cpp @@ -56,6 +56,33 @@ periph_check_capacity(scsi_periph_device_info *device, scsi_ccb *request) if (res == B_OK && request->data_resid == 0) { capacity = B_BENDIAN_TO_HOST_INT32(capacityResult.lba); + if (capacity == UINT_MAX) { + RELEASE_BEN(&device->mutex); + + scsi_cmd_read_capacity_long *cmd = + (scsi_cmd_read_capacity_long *)request->cdb; + + scsi_res_read_capacity_long capacityLongResult; + request->data = (uint8*)&capacityLongResult; + request->data_length = sizeof(capacityLongResult); + request->cdb_length = sizeof(scsi_cmd_read_capacity_long); + + memset(cmd, 0, sizeof(*cmd)); + cmd->opcode = SCSI_OP_SERVICE_ACTION_IN; + cmd->service_action = SCSI_SAI_READ_CAPACITY_16; + + res = periph_safe_exec(device, request); + + ACQUIRE_BEN(&device->mutex); + + if (res == B_OK && request->data_resid == 0) { + capacity = (((uint64)B_BENDIAN_TO_HOST_INT32( + capacityLongResult.lba >> 32)) << 32) + | B_BENDIAN_TO_HOST_INT32(capacityLongResult.lba); + } else + capacity = 0; + } + // the command returns the index of the _last_ block, // i.e. the size is one larger ++capacity; @@ -66,7 +93,7 @@ periph_check_capacity(scsi_periph_device_info *device, scsi_ccb *request) blockSize = 0; } - SHOW_FLOW(3, "capacity = %Ld, block_size = %ld", capacity, blockSize); + SHOW_FLOW(3, "capacity = %lld, block_size = %ld", capacity, blockSize); device->block_size = blockSize;