* 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.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39252 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Jérôme Duval
2010-11-01 16:31:09 +00:00
parent ce658a595d
commit f10a55a632
5 changed files with 85 additions and 8 deletions
+17 -1
View File
@@ -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)
@@ -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;
}
@@ -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,
@@ -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);
@@ -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;