diff options
Diffstat (limited to 'sys/cam/scsi/scsi_all.c')
-rw-r--r-- | sys/cam/scsi/scsi_all.c | 892 |
1 files changed, 892 insertions, 0 deletions
diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c index 6267693..25c5451 100644 --- a/sys/cam/scsi/scsi_all.c +++ b/sys/cam/scsi/scsi_all.c @@ -6454,6 +6454,830 @@ bailout: return (retval); } +struct scsi_attrib_table_entry scsi_mam_attr_table[] = { + { SMA_ATTR_REM_CAP_PARTITION, SCSI_ATTR_FLAG_NONE, + "Remaining Capacity in Partition", + /*suffix*/ "MB", /*to_str*/ scsi_attrib_int_sbuf,/*parse_str*/ NULL }, + { SMA_ATTR_MAX_CAP_PARTITION, SCSI_ATTR_FLAG_NONE, + "Maximum Capacity in Partition", + /*suffix*/"MB", /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, + { SMA_ATTR_TAPEALERT_FLAGS, SCSI_ATTR_FLAG_HEX, + "TapeAlert Flags", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, + { SMA_ATTR_LOAD_COUNT, SCSI_ATTR_FLAG_NONE, + "Load Count", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, + { SMA_ATTR_MAM_SPACE_REMAINING, SCSI_ATTR_FLAG_NONE, + "MAM Space Remaining", + /*suffix*/"bytes", /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_DEV_ASSIGNING_ORG, SCSI_ATTR_FLAG_NONE, + "Assigning Organization", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_FORMAT_DENSITY_CODE, SCSI_ATTR_FLAG_HEX, + "Format Density Code", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, + { SMA_ATTR_INITIALIZATION_COUNT, SCSI_ATTR_FLAG_NONE, + "Initialization Count", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, /*parse_str*/ NULL }, + { SMA_ATTR_VOLUME_ID, SCSI_ATTR_FLAG_NONE, + "Volume Identifier", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_VOLUME_CHANGE_REF, SCSI_ATTR_FLAG_HEX, + "Volume Change Reference", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_DEV_SERIAL_LAST_LOAD, SCSI_ATTR_FLAG_NONE, + "Device Vendor/Serial at Last Load", + /*suffix*/NULL, /*to_str*/ scsi_attrib_vendser_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_DEV_SERIAL_LAST_LOAD_1, SCSI_ATTR_FLAG_NONE, + "Device Vendor/Serial at Last Load - 1", + /*suffix*/NULL, /*to_str*/ scsi_attrib_vendser_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_DEV_SERIAL_LAST_LOAD_2, SCSI_ATTR_FLAG_NONE, + "Device Vendor/Serial at Last Load - 2", + /*suffix*/NULL, /*to_str*/ scsi_attrib_vendser_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_DEV_SERIAL_LAST_LOAD_3, SCSI_ATTR_FLAG_NONE, + "Device Vendor/Serial at Last Load - 3", + /*suffix*/NULL, /*to_str*/ scsi_attrib_vendser_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_TOTAL_MB_WRITTEN_LT, SCSI_ATTR_FLAG_NONE, + "Total MB Written in Medium Life", + /*suffix*/ "MB", /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_TOTAL_MB_READ_LT, SCSI_ATTR_FLAG_NONE, + "Total MB Read in Medium Life", + /*suffix*/ "MB", /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_TOTAL_MB_WRITTEN_CUR, SCSI_ATTR_FLAG_NONE, + "Total MB Written in Current/Last Load", + /*suffix*/ "MB", /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_TOTAL_MB_READ_CUR, SCSI_ATTR_FLAG_NONE, + "Total MB Read in Current/Last Load", + /*suffix*/ "MB", /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_FIRST_ENC_BLOCK, SCSI_ATTR_FLAG_NONE, + "Logical Position of First Encrypted Block", + /*suffix*/ NULL, /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_NEXT_UNENC_BLOCK, SCSI_ATTR_FLAG_NONE, + "Logical Position of First Unencrypted Block after First " + "Encrypted Block", + /*suffix*/ NULL, /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MEDIUM_USAGE_HIST, SCSI_ATTR_FLAG_NONE, + "Medium Usage History", + /*suffix*/ NULL, /*to_str*/ NULL, + /*parse_str*/ NULL }, + { SMA_ATTR_PART_USAGE_HIST, SCSI_ATTR_FLAG_NONE, + "Partition Usage History", + /*suffix*/ NULL, /*to_str*/ NULL, + /*parse_str*/ NULL }, + { SMA_ATTR_MED_MANUF, SCSI_ATTR_FLAG_NONE, + "Medium Manufacturer", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MED_SERIAL, SCSI_ATTR_FLAG_NONE, + "Medium Serial Number", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MED_LENGTH, SCSI_ATTR_FLAG_NONE, + "Medium Length", + /*suffix*/"m", /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MED_WIDTH, SCSI_ATTR_FLAG_FP | SCSI_ATTR_FLAG_DIV_10 | + SCSI_ATTR_FLAG_FP_1DIGIT, + "Medium Width", + /*suffix*/"mm", /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MED_ASSIGNING_ORG, SCSI_ATTR_FLAG_NONE, + "Assigning Organization", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MED_DENSITY_CODE, SCSI_ATTR_FLAG_HEX, + "Medium Density Code", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MED_MANUF_DATE, SCSI_ATTR_FLAG_NONE, + "Medium Manufacture Date", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MAM_CAPACITY, SCSI_ATTR_FLAG_NONE, + "MAM Capacity", + /*suffix*/"bytes", /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MED_TYPE, SCSI_ATTR_FLAG_HEX, + "Medium Type", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MED_TYPE_INFO, SCSI_ATTR_FLAG_HEX, + "Medium Type Information", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MED_SERIAL_NUM, SCSI_ATTR_FLAG_NONE, + "Medium Serial Number", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_APP_VENDOR, SCSI_ATTR_FLAG_NONE, + "Application Vendor", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_APP_NAME, SCSI_ATTR_FLAG_NONE, + "Application Name", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_APP_VERSION, SCSI_ATTR_FLAG_NONE, + "Application Version", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_USER_MED_TEXT_LABEL, SCSI_ATTR_FLAG_NONE, + "User Medium Text Label", + /*suffix*/NULL, /*to_str*/ scsi_attrib_text_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_LAST_WRITTEN_TIME, SCSI_ATTR_FLAG_NONE, + "Date and Time Last Written", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_TEXT_LOCAL_ID, SCSI_ATTR_FLAG_HEX, + "Text Localization Identifier", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_BARCODE, SCSI_ATTR_FLAG_NONE, + "Barcode", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_HOST_OWNER_NAME, SCSI_ATTR_FLAG_NONE, + "Owning Host Textual Name", + /*suffix*/NULL, /*to_str*/ scsi_attrib_text_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_MEDIA_POOL, SCSI_ATTR_FLAG_NONE, + "Media Pool", + /*suffix*/NULL, /*to_str*/ scsi_attrib_text_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_PART_USER_LABEL, SCSI_ATTR_FLAG_NONE, + "Partition User Text Label", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_LOAD_UNLOAD_AT_PART, SCSI_ATTR_FLAG_NONE, + "Load/Unload at Partition", + /*suffix*/NULL, /*to_str*/ scsi_attrib_int_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_APP_FORMAT_VERSION, SCSI_ATTR_FLAG_NONE, + "Application Format Version", + /*suffix*/NULL, /*to_str*/ scsi_attrib_ascii_sbuf, + /*parse_str*/ NULL }, + { SMA_ATTR_VOL_COHERENCY_INFO, SCSI_ATTR_FLAG_NONE, + "Volume Coherency Information", + /*suffix*/NULL, /*to_str*/ scsi_attrib_volcoh_sbuf, + /*parse_str*/ NULL }, + { 0x0ff1, SCSI_ATTR_FLAG_NONE, + "Spectra MLM Creation", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x0ff2, SCSI_ATTR_FLAG_NONE, + "Spectra MLM C3", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x0ff3, SCSI_ATTR_FLAG_NONE, + "Spectra MLM RW", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x0ff4, SCSI_ATTR_FLAG_NONE, + "Spectra MLM SDC List", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x0ff7, SCSI_ATTR_FLAG_NONE, + "Spectra MLM Post Scan", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x0ffe, SCSI_ATTR_FLAG_NONE, + "Spectra MLM Checksum", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x17f1, SCSI_ATTR_FLAG_NONE, + "Spectra MLM Creation", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x17f2, SCSI_ATTR_FLAG_NONE, + "Spectra MLM C3", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x17f3, SCSI_ATTR_FLAG_NONE, + "Spectra MLM RW", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x17f4, SCSI_ATTR_FLAG_NONE, + "Spectra MLM SDC List", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x17f7, SCSI_ATTR_FLAG_NONE, + "Spectra MLM Post Scan", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, + { 0x17ff, SCSI_ATTR_FLAG_NONE, + "Spectra MLM Checksum", + /*suffix*/NULL, /*to_str*/ scsi_attrib_hexdump_sbuf, + /*parse_str*/ NULL }, +}; + +/* + * Print out Volume Coherency Information (Attribute 0x080c). + * This field has two variable length members, including one at the + * beginning, so it isn't practical to have a fixed structure definition. + * This is current as of SSC4r03 (see section 4.2.21.3), dated March 25, + * 2013. + */ +int +scsi_attrib_volcoh_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, + uint32_t valid_len, uint32_t flags, + uint32_t output_flags, char *error_str, + int error_str_len) +{ + size_t avail_len; + uint32_t field_size; + uint64_t tmp_val; + uint8_t *cur_ptr; + int retval; + int vcr_len, as_len; + + retval = 0; + tmp_val = 0; + + field_size = scsi_2btoul(hdr->length); + avail_len = valid_len - sizeof(*hdr); + if (field_size > avail_len) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "Available " + "length of attribute ID 0x%.4x %zu < field " + "length %u", scsi_2btoul(hdr->id), avail_len, + field_size); + } + retval = 1; + goto bailout; + } else if (field_size == 0) { + /* + * It isn't clear from the spec whether a field length of + * 0 is invalid here. It probably is, but be lenient here + * to avoid inconveniencing the user. + */ + goto bailout; + } + cur_ptr = hdr->attribute; + vcr_len = *cur_ptr; + cur_ptr++; + + sbuf_printf(sb, "\n\tVolume Change Reference Value:"); + + switch (vcr_len) { + case 0: + if (error_str != NULL) { + snprintf(error_str, error_str_len, "Volume Change " + "Reference value has length of 0"); + } + retval = 1; + goto bailout; + break; /*NOTREACHED*/ + case 1: + tmp_val = *cur_ptr; + break; + case 2: + tmp_val = scsi_2btoul(cur_ptr); + break; + case 3: + tmp_val = scsi_3btoul(cur_ptr); + break; + case 4: + tmp_val = scsi_4btoul(cur_ptr); + break; + case 8: + tmp_val = scsi_8btou64(cur_ptr); + break; + default: + sbuf_printf(sb, "\n"); + sbuf_hexdump(sb, cur_ptr, vcr_len, NULL, 0); + break; + } + if (vcr_len <= 8) + sbuf_printf(sb, " 0x%jx\n", (uintmax_t)tmp_val); + + cur_ptr += vcr_len; + tmp_val = scsi_8btou64(cur_ptr); + sbuf_printf(sb, "\tVolume Coherency Count: %ju\n", (uintmax_t)tmp_val); + + cur_ptr += sizeof(tmp_val); + tmp_val = scsi_8btou64(cur_ptr); + sbuf_printf(sb, "\tVolume Coherency Set Identifier: 0x%jx\n", + (uintmax_t)tmp_val); + + /* + * Figure out how long the Application Client Specific Information + * is and produce a hexdump. + */ + cur_ptr += sizeof(tmp_val); + as_len = scsi_2btoul(cur_ptr); + cur_ptr += sizeof(uint16_t); + sbuf_printf(sb, "\tApplication Client Specific Information: "); + if (((as_len == SCSI_LTFS_VER0_LEN) + || (as_len == SCSI_LTFS_VER1_LEN)) + && (strncmp(cur_ptr, SCSI_LTFS_STR_NAME, SCSI_LTFS_STR_LEN) == 0)) { + sbuf_printf(sb, "LTFS\n"); + cur_ptr += SCSI_LTFS_STR_LEN + 1; + if (cur_ptr[SCSI_LTFS_UUID_LEN] != '\0') + cur_ptr[SCSI_LTFS_UUID_LEN] = '\0'; + sbuf_printf(sb, "\tLTFS UUID: %s\n", cur_ptr); + cur_ptr += SCSI_LTFS_UUID_LEN + 1; + /* XXX KDM check the length */ + sbuf_printf(sb, "\tLTFS Version: %d\n", *cur_ptr); + } else { + sbuf_printf(sb, "Unknown\n"); + sbuf_hexdump(sb, cur_ptr, as_len, NULL, 0); + } + +bailout: + return (retval); +} + +int +scsi_attrib_vendser_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, + uint32_t valid_len, uint32_t flags, + uint32_t output_flags, char *error_str, + int error_str_len) +{ + size_t avail_len; + uint32_t field_size; + struct scsi_attrib_vendser *vendser; + cam_strvis_flags strvis_flags; + int retval = 0; + + field_size = scsi_2btoul(hdr->length); + avail_len = valid_len - sizeof(*hdr); + if (field_size > avail_len) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "Available " + "length of attribute ID 0x%.4x %zu < field " + "length %u", scsi_2btoul(hdr->id), avail_len, + field_size); + } + retval = 1; + goto bailout; + } else if (field_size == 0) { + /* + * A field size of 0 doesn't make sense here. The device + * can at least give you the vendor ID, even if it can't + * give you the serial number. + */ + if (error_str != NULL) { + snprintf(error_str, error_str_len, "The length of " + "attribute ID 0x%.4x is 0", + scsi_2btoul(hdr->id)); + } + retval = 1; + goto bailout; + } + vendser = (struct scsi_attrib_vendser *)hdr->attribute; + + switch (output_flags & SCSI_ATTR_OUTPUT_NONASCII_MASK) { + case SCSI_ATTR_OUTPUT_NONASCII_TRIM: + strvis_flags = CAM_STRVIS_FLAG_NONASCII_TRIM; + break; + case SCSI_ATTR_OUTPUT_NONASCII_RAW: + strvis_flags = CAM_STRVIS_FLAG_NONASCII_RAW; + break; + case SCSI_ATTR_OUTPUT_NONASCII_ESC: + default: + strvis_flags = CAM_STRVIS_FLAG_NONASCII_ESC; + break;; + } + cam_strvis_sbuf(sb, vendser->vendor, sizeof(vendser->vendor), + strvis_flags); + sbuf_putc(sb, ' '); + cam_strvis_sbuf(sb, vendser->serial_num, sizeof(vendser->serial_num), + strvis_flags); +bailout: + return (retval); +} + +int +scsi_attrib_hexdump_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, + uint32_t valid_len, uint32_t flags, + uint32_t output_flags, char *error_str, + int error_str_len) +{ + uint32_t field_size; + ssize_t avail_len; + uint32_t print_len; + uint8_t *num_ptr; + int retval = 0; + + field_size = scsi_2btoul(hdr->length); + avail_len = valid_len - sizeof(*hdr); + print_len = MIN(avail_len, field_size); + num_ptr = hdr->attribute; + + if (print_len > 0) { + sbuf_printf(sb, "\n"); + sbuf_hexdump(sb, num_ptr, print_len, NULL, 0); + } + + return (retval); +} + +int +scsi_attrib_int_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, + uint32_t valid_len, uint32_t flags, + uint32_t output_flags, char *error_str, + int error_str_len) +{ + uint64_t print_number; + size_t avail_len; + uint32_t number_size; + int retval = 0; + + number_size = scsi_2btoul(hdr->length); + + avail_len = valid_len - sizeof(*hdr); + if (avail_len < number_size) { + if (error_str != NULL) { + snprintf(error_str, error_str_len, "Available " + "length of attribute ID 0x%.4x %zu < field " + "length %u", scsi_2btoul(hdr->id), avail_len, + number_size); + } + retval = 1; + goto bailout; + } + + switch (number_size) { + case 0: + /* + * We don't treat this as an error, since there may be + * scenarios where a device reports a field but then gives + * a length of 0. See the note in scsi_attrib_ascii_sbuf(). + */ + goto bailout; + break; /*NOTREACHED*/ + case 1: + print_number = hdr->attribute[0]; + break; + case 2: + print_number = scsi_2btoul(hdr->attribute); + break; + case 3: + print_number = scsi_3btoul(hdr->attribute); + break; + case 4: + print_number = scsi_4btoul(hdr->attribute); + break; + case 8: + print_number = scsi_8btou64(hdr->attribute); + break; + default: + /* + * If we wind up here, the number is too big to print + * normally, so just do a hexdump. + */ + retval = scsi_attrib_hexdump_sbuf(sb, hdr, valid_len, + flags, output_flags, + error_str, error_str_len); + goto bailout; + break; + } + + if (flags & SCSI_ATTR_FLAG_FP) { +#ifndef _KERNEL + long double num_float; + + num_float = (long double)print_number; + + if (flags & SCSI_ATTR_FLAG_DIV_10) + num_float /= 10; + + sbuf_printf(sb, "%.*Lf", (flags & SCSI_ATTR_FLAG_FP_1DIGIT) ? + 1 : 0, num_float); +#else /* _KERNEL */ + sbuf_printf(sb, "%ju", (flags & SCSI_ATTR_FLAG_DIV_10) ? + (print_number / 10) : print_number); +#endif /* _KERNEL */ + } else if (flags & SCSI_ATTR_FLAG_HEX) { + sbuf_printf(sb, "0x%jx", (uintmax_t)print_number); + } else + sbuf_printf(sb, "%ju", (uintmax_t)print_number); + +bailout: + return (retval); +} + +int +scsi_attrib_ascii_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, + uint32_t valid_len, uint32_t flags, + uint32_t output_flags, char *error_str, + int error_str_len) +{ + size_t avail_len; + uint32_t field_size, print_size; + int retval = 0; + + avail_len = valid_len - sizeof(*hdr); + field_size = scsi_2btoul(hdr->length); + print_size = MIN(avail_len, field_size); + + if (print_size > 0) { + cam_strvis_flags strvis_flags; + + switch (output_flags & SCSI_ATTR_OUTPUT_NONASCII_MASK) { + case SCSI_ATTR_OUTPUT_NONASCII_TRIM: + strvis_flags = CAM_STRVIS_FLAG_NONASCII_TRIM; + break; + case SCSI_ATTR_OUTPUT_NONASCII_RAW: + strvis_flags = CAM_STRVIS_FLAG_NONASCII_RAW; + break; + case SCSI_ATTR_OUTPUT_NONASCII_ESC: + default: + strvis_flags = CAM_STRVIS_FLAG_NONASCII_ESC; + break; + } + cam_strvis_sbuf(sb, hdr->attribute, print_size, strvis_flags); + } else if (avail_len < field_size) { + /* + * We only report an error if the user didn't allocate + * enough space to hold the full value of this field. If + * the field length is 0, that is allowed by the spec. + * e.g. in SPC-4r37, section 7.4.2.2.5, VOLUME IDENTIFIER + * "This attribute indicates the current volume identifier + * (see SMC-3) of the medium. If the device server supports + * this attribute but does not have access to the volume + * identifier, the device server shall report this attribute + * with an attribute length value of zero." + */ + if (error_str != NULL) { + snprintf(error_str, error_str_len, "Available " + "length of attribute ID 0x%.4x %zu < field " + "length %u", scsi_2btoul(hdr->id), avail_len, + field_size); + } + retval = 1; + } + + return (retval); +} + +int +scsi_attrib_text_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, + uint32_t valid_len, uint32_t flags, + uint32_t output_flags, char *error_str, + int error_str_len) +{ + size_t avail_len; + uint32_t field_size, print_size; + int retval = 0; + int esc_text = 1; + + avail_len = valid_len - sizeof(*hdr); + field_size = scsi_2btoul(hdr->length); + print_size = MIN(avail_len, field_size); + + if ((output_flags & SCSI_ATTR_OUTPUT_TEXT_MASK) == + SCSI_ATTR_OUTPUT_TEXT_RAW) + esc_text = 0; + + if (print_size > 0) { + uint32_t i; + + for (i = 0; i < print_size; i++) { + if (hdr->attribute[i] == '\0') + continue; + else if (((unsigned char)hdr->attribute[i] < 0x80) + || (esc_text == 0)) + sbuf_putc(sb, hdr->attribute[i]); + else + sbuf_printf(sb, "%%%02x", + (unsigned char)hdr->attribute[i]); + } + } else if (avail_len < field_size) { + /* + * We only report an error if the user didn't allocate + * enough space to hold the full value of this field. + */ + if (error_str != NULL) { + snprintf(error_str, error_str_len, "Available " + "length of attribute ID 0x%.4x %zu < field " + "length %u", scsi_2btoul(hdr->id), avail_len, + field_size); + } + retval = 1; + } + + return (retval); +} + +struct scsi_attrib_table_entry * +scsi_find_attrib_entry(struct scsi_attrib_table_entry *table, + size_t num_table_entries, uint32_t id) +{ + uint32_t i; + + for (i = 0; i < num_table_entries; i++) { + if (table[i].id == id) + return (&table[i]); + } + + return (NULL); +} + +struct scsi_attrib_table_entry * +scsi_get_attrib_entry(uint32_t id) +{ + return (scsi_find_attrib_entry(scsi_mam_attr_table, + sizeof(scsi_mam_attr_table) / sizeof(scsi_mam_attr_table[0]), + id)); +} + +int +scsi_attrib_value_sbuf(struct sbuf *sb, uint32_t valid_len, + struct scsi_mam_attribute_header *hdr, uint32_t output_flags, + char *error_str, size_t error_str_len) +{ + int retval; + + switch (hdr->byte2 & SMA_FORMAT_MASK) { + case SMA_FORMAT_ASCII: + retval = scsi_attrib_ascii_sbuf(sb, hdr, valid_len, + SCSI_ATTR_FLAG_NONE, output_flags, error_str,error_str_len); + break; + case SMA_FORMAT_BINARY: + if (scsi_2btoul(hdr->length) <= 8) + retval = scsi_attrib_int_sbuf(sb, hdr, valid_len, + SCSI_ATTR_FLAG_NONE, output_flags, error_str, + error_str_len); + else + retval = scsi_attrib_hexdump_sbuf(sb, hdr, valid_len, + SCSI_ATTR_FLAG_NONE, output_flags, error_str, + error_str_len); + break; + case SMA_FORMAT_TEXT: + retval = scsi_attrib_text_sbuf(sb, hdr, valid_len, + SCSI_ATTR_FLAG_NONE, output_flags, error_str, + error_str_len); + break; + default: + if (error_str != NULL) { + snprintf(error_str, error_str_len, "Unknown attribute " + "format 0x%x", hdr->byte2 & SMA_FORMAT_MASK); + } + retval = 1; + goto bailout; + break; /*NOTREACHED*/ + } + + sbuf_trim(sb); + +bailout: + + return (retval); +} + +void +scsi_attrib_prefix_sbuf(struct sbuf *sb, uint32_t output_flags, + struct scsi_mam_attribute_header *hdr, + uint32_t valid_len, const char *desc) +{ + int need_space = 0; + uint32_t len; + uint32_t id; + + /* + * We can't do anything if we don't have enough valid data for the + * header. + */ + if (valid_len < sizeof(*hdr)) + return; + + id = scsi_2btoul(hdr->id); + /* + * Note that we print out the value of the attribute listed in the + * header, regardless of whether we actually got that many bytes + * back from the device through the controller. A truncated result + * could be the result of a failure to ask for enough data; the + * header indicates how many bytes are allocated for this attribute + * in the MAM. + */ + len = scsi_2btoul(hdr->length); + + if ((output_flags & SCSI_ATTR_OUTPUT_FIELD_MASK) == + SCSI_ATTR_OUTPUT_FIELD_NONE) + return; + + if ((output_flags & SCSI_ATTR_OUTPUT_FIELD_DESC) + && (desc != NULL)) { + sbuf_printf(sb, "%s", desc); + need_space = 1; + } + + if (output_flags & SCSI_ATTR_OUTPUT_FIELD_NUM) { + sbuf_printf(sb, "%s(0x%.4x)", (need_space) ? " " : "", id); + need_space = 0; + } + + if (output_flags & SCSI_ATTR_OUTPUT_FIELD_SIZE) { + sbuf_printf(sb, "%s[%d]", (need_space) ? " " : "", len); + need_space = 0; + } + if (output_flags & SCSI_ATTR_OUTPUT_FIELD_RW) { + sbuf_printf(sb, "%s(%s)", (need_space) ? " " : "", + (hdr->byte2 & SMA_READ_ONLY) ? "RO" : "RW"); + } + sbuf_printf(sb, ": "); +} + +int +scsi_attrib_sbuf(struct sbuf *sb, struct scsi_mam_attribute_header *hdr, + uint32_t valid_len, struct scsi_attrib_table_entry *user_table, + size_t num_user_entries, int prefer_user_table, + uint32_t output_flags, char *error_str, int error_str_len) +{ + int retval; + struct scsi_attrib_table_entry *table1 = NULL, *table2 = NULL; + struct scsi_attrib_table_entry *entry = NULL; + size_t table1_size = 0, table2_size = 0; + uint32_t id; + + retval = 0; + + if (valid_len < sizeof(*hdr)) { + retval = 1; + goto bailout; + } + + id = scsi_2btoul(hdr->id); + + if (user_table != NULL) { + if (prefer_user_table != 0) { + table1 = user_table; + table1_size = num_user_entries; + table2 = scsi_mam_attr_table; + table2_size = sizeof(scsi_mam_attr_table) / + sizeof(scsi_mam_attr_table[0]); + } else { + table1 = scsi_mam_attr_table; + table1_size = sizeof(scsi_mam_attr_table) / + sizeof(scsi_mam_attr_table[0]); + table2 = user_table; + table2_size = num_user_entries; + } + } else { + table1 = scsi_mam_attr_table; + table1_size = sizeof(scsi_mam_attr_table) / + sizeof(scsi_mam_attr_table[0]); + } + + entry = scsi_find_attrib_entry(table1, table1_size, id); + if (entry != NULL) { + scsi_attrib_prefix_sbuf(sb, output_flags, hdr, valid_len, + entry->desc); + if (entry->to_str == NULL) + goto print_default; + retval = entry->to_str(sb, hdr, valid_len, entry->flags, + output_flags, error_str, error_str_len); + goto bailout; + } + if (table2 != NULL) { + entry = scsi_find_attrib_entry(table2, table2_size, id); + if (entry != NULL) { + if (entry->to_str == NULL) + goto print_default; + + scsi_attrib_prefix_sbuf(sb, output_flags, hdr, + valid_len, entry->desc); + retval = entry->to_str(sb, hdr, valid_len, entry->flags, + output_flags, error_str, + error_str_len); + goto bailout; + } + } + + scsi_attrib_prefix_sbuf(sb, output_flags, hdr, valid_len, NULL); + +print_default: + retval = scsi_attrib_value_sbuf(sb, valid_len, hdr, output_flags, + error_str, error_str_len); +bailout: + if (retval == 0) { + if ((entry != NULL) + && (entry->suffix != NULL)) + sbuf_printf(sb, " %s", entry->suffix); + + sbuf_trim(sb); + sbuf_printf(sb, "\n"); + } + + return (retval); +} + void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), @@ -7404,6 +8228,74 @@ scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, timeout); } +void +scsi_read_attribute(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, u_int8_t service_action, + uint32_t element, u_int8_t elem_type, int logical_volume, + int partition, u_int32_t first_attribute, int cache, + u_int8_t *data_ptr, u_int32_t length, int sense_len, + u_int32_t timeout) +{ + struct scsi_read_attribute *scsi_cmd; + + scsi_cmd = (struct scsi_read_attribute *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = READ_ATTRIBUTE; + scsi_cmd->service_action = service_action, + scsi_ulto2b(element, scsi_cmd->element); + scsi_cmd->elem_type = elem_type; + scsi_cmd->logical_volume = logical_volume; + scsi_cmd->partition = partition; + scsi_ulto2b(first_attribute, scsi_cmd->first_attribute); + scsi_ulto4b(length, scsi_cmd->length); + if (cache != 0) + scsi_cmd->cache |= SRA_CACHE; + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + /*data_ptr*/data_ptr, + /*dxfer_len*/length, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_write_attribute(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, uint32_t element, int logical_volume, + int partition, int wtc, u_int8_t *data_ptr, + u_int32_t length, int sense_len, u_int32_t timeout) +{ + struct scsi_write_attribute *scsi_cmd; + + scsi_cmd = (struct scsi_write_attribute *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = WRITE_ATTRIBUTE; + if (wtc != 0) + scsi_cmd->byte2 = SWA_WTC; + scsi_ulto3b(element, scsi_cmd->element); + scsi_cmd->logical_volume = logical_volume; + scsi_cmd->partition = partition; + scsi_ulto4b(length, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_OUT, + tag_action, + /*data_ptr*/data_ptr, + /*dxfer_len*/length, + sense_len, + sizeof(*scsi_cmd), + timeout); +} void scsi_persistent_reserve_in(struct ccb_scsiio *csio, uint32_t retries, |