Author: korli Date: 2010-11-01 17:31:09 +0100 (Mon, 01 Nov 2010) New Revision: 39252 Changeset: http://dev.haiku-os.org/changeset/39252 Modified: haiku/trunk/headers/private/drivers/scsi_cmds.h haiku/trunk/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp haiku/trunk/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp haiku/trunk/src/add-ons/kernel/generic/scsi_periph/block.cpp Log: * ata: added ATADevice::ReadCapacity16() * ata: don't fail if lba_sector_count is null and lba48_sector_count is not * scsi_periph: if ReadCapacity() returns 0xffffffff, use ReadCapacity16() instead * scsi_disk: use a different computation in the struct geometry computation for bigger disks Tested successfully with a virtual 10TB hard drive. Modified: haiku/trunk/headers/private/drivers/scsi_cmds.h =================================================================== --- haiku/trunk/headers/private/drivers/scsi_cmds.h 2010-11-01 15:37:46 UTC (rev 39251) +++ haiku/trunk/headers/private/drivers/scsi_cmds.h 2010-11-01 16:31:09 UTC (rev 39252) @@ -313,7 +313,7 @@ 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,7 +336,23 @@ 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) typedef struct scsi_cmd_rw_6 { Modified: haiku/trunk/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp 2010-11-01 15:37:46 UTC (rev 39251) +++ haiku/trunk/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp 2010-11-01 16:31:09 UTC (rev 39252) @@ -182,7 +182,27 @@ 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); return B_OK; @@ -251,6 +271,11 @@ 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 @@ } } - 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; } Modified: haiku/trunk/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h =================================================================== --- haiku/trunk/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h 2010-11-01 15:37:46 UTC (rev 39251) +++ haiku/trunk/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h 2010-11-01 16:31:09 UTC (rev 39252) @@ -176,6 +176,7 @@ 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, Modified: haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp 2010-11-01 15:37:46 UTC (rev 39251) +++ haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp 2010-11-01 16:31:09 UTC (rev 39252) @@ -92,9 +92,16 @@ 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_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 @@ 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); Modified: haiku/trunk/src/add-ons/kernel/generic/scsi_periph/block.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/scsi_periph/block.cpp 2010-11-01 15:37:46 UTC (rev 39251) +++ haiku/trunk/src/add-ons/kernel/generic/scsi_periph/block.cpp 2010-11-01 16:31:09 UTC (rev 39252) @@ -56,6 +56,33 @@ 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 @@ 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;