/** * Get a device information (capacity & sector sizes). */ static int zbc_scsi_get_capacity(zbc_device_t *dev) { zbc_sg_cmd_t cmd; zbc_zone_t *zones = NULL; int logical_per_physical; unsigned int nr_zones = 0; uint64_t max_lba; int ret; /* READ CAPACITY 16 */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_READ_CAPACITY, NULL, ZBC_SG_READ_CAPACITY_REPLY_LEN); if ( ret != 0 ) { zbc_error("zbc_sg_cmd_init failed\n"); return( ret ); } /* Fill command CDB */ cmd.cdb[0] = ZBC_SG_READ_CAPACITY_CDB_OPCODE; cmd.cdb[1] = ZBC_SG_READ_CAPACITY_CDB_SA; zbc_sg_cmd_set_int32(&cmd.cdb[10], ZBC_SG_READ_CAPACITY_REPLY_LEN); /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret != 0 ) { goto out; } /* Logical block size */ dev->zbd_info.zbd_logical_block_size = zbc_sg_cmd_get_int32(&cmd.out_buf[8]); if ( dev->zbd_info.zbd_logical_block_size <= 0 ) { zbc_error("%s: invalid logical sector size\n", dev->zbd_filename); ret = -EINVAL; goto out; } logical_per_physical = 1 << cmd.out_buf[13] & 0x0f; /* Check RC_BASIS field */ switch( (cmd.out_buf[12] & 0x30) >> 4 ) { case 0x00: /* The logical block address indicates the last LBA of the */ /* conventional zones at the beginning of the disk. To get */ /* the entire disk capacity, we need to get last LBA of */ /* the last zone of the disk. */ ret = zbc_scsi_report_zones(dev, 0, ZBC_RO_ALL, &max_lba, NULL, &nr_zones); if ( ret != 0 ) { zbc_error("zbc_report_zones failed\n"); goto out; } /* Set the drive capacity to the reported max LBA */ dev->zbd_info.zbd_logical_blocks = max_lba + 1; break; case 0x01: /* The disk last LBA was reproted */ dev->zbd_info.zbd_logical_blocks = zbc_sg_cmd_get_int64(&cmd.out_buf[0]) + 1; break; default: zbc_error("%s: invalid RC_BASIS field encountered in READ CAPACITY result\n", dev->zbd_filename); ret = -EIO; goto out; } if ( ! dev->zbd_info.zbd_logical_blocks ) { zbc_error("%s: invalid capacity (logical blocks)\n", dev->zbd_filename); ret = -EINVAL; goto out; } dev->zbd_info.zbd_physical_block_size = dev->zbd_info.zbd_logical_block_size * logical_per_physical; dev->zbd_info.zbd_physical_blocks = dev->zbd_info.zbd_logical_blocks / logical_per_physical; out: zbc_sg_cmd_destroy(&cmd); if ( zones ) { free(zones); } return( ret ); }
/** * Get a device information (capacity & sector sizes). */ static int zbc_scsi_get_capacity(zbc_device_t *dev) { zbc_sg_cmd_t cmd; zbc_zone_t *zones = NULL; int logical_per_physical; unsigned int nr_zones = 0; uint64_t slba = 0; int ret; /* READ CAPACITY 16 */ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_READ_CAPACITY, NULL, ZBC_SG_READ_CAPACITY_REPLY_LEN); if ( ret != 0 ) { zbc_error("zbc_sg_cmd_init failed\n"); return( ret ); } /* Fill command CDB */ cmd.cdb[0] = ZBC_SG_READ_CAPACITY_CDB_OPCODE; cmd.cdb[1] = ZBC_SG_READ_CAPACITY_CDB_SA; zbc_sg_cmd_set_int32(&cmd.cdb[10], ZBC_SG_READ_CAPACITY_REPLY_LEN); /* Send the SG_IO command */ ret = zbc_sg_cmd_exec(dev, &cmd); if ( ret != 0 ) { goto out; } /* Logical block size */ dev->zbd_info.zbd_logical_block_size = zbc_sg_cmd_get_int32(&cmd.out_buf[8]); if ( dev->zbd_info.zbd_logical_block_size <= 0 ) { zbc_error("%s: invalid logical sector size\n", dev->zbd_filename); ret = -EINVAL; goto out; } logical_per_physical = 1 << cmd.out_buf[13] & 0x0f; /* Check RC_BASIS field */ switch( (cmd.out_buf[12] & 0x30) >> 4 ) { case 0x00: /* The logical block address indicates the last LBA of the */ /* conventional zones at the beginning of the disk. To get */ /* the entire disk capacity, we need to get last LBA of */ /* the last zone of the disk. */ ret = zbc_scsi_report_zones(dev, 0, 0, NULL, &nr_zones); if ( ret != 0 ) { zbc_error("zbc_report_zones failed\n"); goto out; } if ( ! nr_zones ) { ret = -EIO; goto out; } /* Allocate zone array */ zones = (zbc_zone_t *) malloc(sizeof(zbc_zone_t) * nr_zones); if ( ! zones ) { zbc_error("No memory\n"); ret = -ENOMEM; goto out; } memset(zones, 0, sizeof(zbc_zone_t) * nr_zones); /* Get all zone information */ unsigned int n, z = 0, nz = 0; while ( nz < nr_zones ) { n= nr_zones - nz; ret = zbc_scsi_report_zones(dev, slba, 0, &zones[z], &n); if ( ret != 0 ) { zbc_error("zbc_report_zones failed\n"); goto out; } if ( n == 0 ) { ret = -EIO; break; } nz += n; z += n; slba = zones[z - 1].zbz_start + zones[z - 1].zbz_length; } /* Get the drive capacity from the last zone last LBA */ dev->zbd_info.zbd_logical_blocks = zbc_zone_next_lba(&zones[nr_zones - 1]); break; case 0x01: /* The disk last LBA was reproted */ dev->zbd_info.zbd_logical_blocks = zbc_sg_cmd_get_int64(&cmd.out_buf[0]) + 1; break; default: zbc_error("%s: invalid RC_BASIS field encountered in READ CAPACITY result\n", dev->zbd_filename); ret = -EIO; goto out; } if ( ! dev->zbd_info.zbd_logical_blocks ) { zbc_error("%s: invalid capacity (logical blocks)\n", dev->zbd_filename); ret = -EINVAL; goto out; } dev->zbd_info.zbd_physical_block_size = dev->zbd_info.zbd_logical_block_size * logical_per_physical; dev->zbd_info.zbd_physical_blocks = dev->zbd_info.zbd_logical_blocks / logical_per_physical; out: zbc_sg_cmd_destroy(&cmd); if ( zones ) { free(zones); } return( ret ); }