diff options
Diffstat (limited to 'sys/cam')
-rw-r--r-- | sys/cam/cam.c | 50 | ||||
-rw-r--r-- | sys/cam/cam.h | 12 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_all.c | 892 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_all.h | 283 |
4 files changed, 1235 insertions, 2 deletions
diff --git a/sys/cam/cam.c b/sys/cam/cam.c index f608d6f..c7dc2dd 100644 --- a/sys/cam/cam.c +++ b/sys/cam/cam.c @@ -158,6 +158,56 @@ cam_strvis(u_int8_t *dst, const u_int8_t *src, int srclen, int dstlen) *dst = '\0'; } +void +cam_strvis_sbuf(struct sbuf *sb, const u_int8_t *src, int srclen, + uint32_t flags) +{ + + /* Trim leading/trailing spaces, nulls. */ + while (srclen > 0 && src[0] == ' ') + src++, srclen--; + while (srclen > 0 + && (src[srclen-1] == ' ' || src[srclen-1] == '\0')) + srclen--; + + while (srclen > 0) { + if (*src < 0x20 || *src >= 0x80) { + /* SCSI-II Specifies that these should never occur. */ + /* non-printable character */ + switch (flags & CAM_STRVIS_FLAG_NONASCII_MASK) { + case CAM_STRVIS_FLAG_NONASCII_ESC: + sbuf_printf(sb, "\\%c%c%c", + ((*src & 0300) >> 6) + '0', + ((*src & 0070) >> 3) + '0', + ((*src & 0007) >> 0) + '0'); + break; + case CAM_STRVIS_FLAG_NONASCII_RAW: + /* + * If we run into a NUL, just transform it + * into a space. + */ + if (*src != 0x00) + sbuf_putc(sb, *src); + else + sbuf_putc(sb, ' '); + break; + case CAM_STRVIS_FLAG_NONASCII_SPC: + sbuf_putc(sb, ' '); + break; + case CAM_STRVIS_FLAG_NONASCII_TRIM: + default: + break; + } + } else { + /* normal character */ + sbuf_putc(sb, *src); + } + src++; + srclen--; + } +} + + /* * Compare string with pattern, returning 0 on match. * Short pattern matches trailing blanks in name, diff --git a/sys/cam/cam.h b/sys/cam/cam.h index c226e3e..5b8a5e0 100644 --- a/sys/cam/cam.h +++ b/sys/cam/cam.h @@ -346,6 +346,15 @@ typedef enum { CAM_EAF_PRINT_RESULT = 0x20 } cam_error_ata_flags; +typedef enum { + CAM_STRVIS_FLAG_NONE = 0x00, + CAM_STRVIS_FLAG_NONASCII_MASK = 0x03, + CAM_STRVIS_FLAG_NONASCII_TRIM = 0x00, + CAM_STRVIS_FLAG_NONASCII_RAW = 0x01, + CAM_STRVIS_FLAG_NONASCII_SPC = 0x02, + CAM_STRVIS_FLAG_NONASCII_ESC = 0x03 +} cam_strvis_flags; + struct cam_status_entry { cam_status status_code; @@ -358,6 +367,7 @@ extern const int num_cam_status_entries; extern int cam_sort_io_queues; #endif union ccb; +struct sbuf; #ifdef SYSCTL_DECL /* from sysctl.h */ SYSCTL_DECL(_kern_cam); @@ -370,6 +380,8 @@ caddr_t cam_quirkmatch(caddr_t target, caddr_t quirk_table, int num_entries, int entry_size, cam_quirkmatch_t *comp_func); void cam_strvis(u_int8_t *dst, const u_int8_t *src, int srclen, int dstlen); +void cam_strvis_sbuf(struct sbuf *sb, const u_int8_t *src, int srclen, + uint32_t flags); int cam_strmatch(const u_int8_t *str, const u_int8_t *pattern, int str_len); const struct cam_status_entry* 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, diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index f70b094..e446f6c 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -996,6 +996,216 @@ struct scsi_write_buffer u_int8_t control; }; +struct scsi_read_attribute +{ + u_int8_t opcode; + u_int8_t service_action; +#define SRA_SA_ATTR_VALUES 0x00 +#define SRA_SA_ATTR_LIST 0x01 +#define SRA_SA_LOG_VOL_LIST 0x02 +#define SRA_SA_PART_LIST 0x03 +#define SRA_SA_RESTRICTED 0x04 +#define SRA_SA_SUPPORTED_ATTRS 0x05 +#define SRA_SA_MASK 0x1f + u_int8_t element[2]; + u_int8_t elem_type; + u_int8_t logical_volume; + u_int8_t reserved1; + u_int8_t partition; + u_int8_t first_attribute[2]; + u_int8_t length[4]; + u_int8_t cache; +#define SRA_CACHE 0x01 + u_int8_t control; +}; + +struct scsi_write_attribute +{ + u_int8_t opcode; + u_int8_t byte2; +#define SWA_WTC 0x01 + u_int8_t element[3]; + u_int8_t logical_volume; + u_int8_t reserved1; + u_int8_t partition; + u_int8_t reserved2[2]; + u_int8_t length[4]; + u_int8_t reserved3; + u_int8_t control; +}; + + +struct scsi_read_attribute_values +{ + u_int8_t length[4]; + u_int8_t attribute_0[0]; +}; + +struct scsi_mam_attribute_header +{ + u_int8_t id[2]; + /* + * Attributes obtained from SPC-4r36g (section 7.4.2.2) and + * SSC-4r03 (section 4.2.21). + */ +#define SMA_ATTR_ID_DEVICE_MIN 0x0000 + +#define SMA_ATTR_REM_CAP_PARTITION 0x0000 +#define SMA_ATTR_MAX_CAP_PARTITION 0x0001 +#define SMA_ATTR_TAPEALERT_FLAGS 0x0002 +#define SMA_ATTR_LOAD_COUNT 0x0003 +#define SMA_ATTR_MAM_SPACE_REMAINING 0x0004 + +#define SMA_ATTR_DEV_ASSIGNING_ORG 0x0005 +#define SMA_ATTR_FORMAT_DENSITY_CODE 0x0006 +#define SMA_ATTR_INITIALIZATION_COUNT 0x0007 +#define SMA_ATTR_VOLUME_ID 0x0008 +#define SMA_ATTR_VOLUME_CHANGE_REF 0x0009 + +#define SMA_ATTR_DEV_SERIAL_LAST_LOAD 0x020a +#define SMA_ATTR_DEV_SERIAL_LAST_LOAD_1 0x020b +#define SMA_ATTR_DEV_SERIAL_LAST_LOAD_2 0x020c +#define SMA_ATTR_DEV_SERIAL_LAST_LOAD_3 0x020d + +#define SMA_ATTR_TOTAL_MB_WRITTEN_LT 0x0220 +#define SMA_ATTR_TOTAL_MB_READ_LT 0x0221 +#define SMA_ATTR_TOTAL_MB_WRITTEN_CUR 0x0222 +#define SMA_ATTR_TOTAL_MB_READ_CUR 0x0223 +#define SMA_ATTR_FIRST_ENC_BLOCK 0x0224 +#define SMA_ATTR_NEXT_UNENC_BLOCK 0x0225 + +#define SMA_ATTR_MEDIUM_USAGE_HIST 0x0340 +#define SMA_ATTR_PART_USAGE_HIST 0x0341 + +#define SMA_ATTR_ID_DEVICE_MAX 0x03ff + +#define SMA_ATTR_ID_MEDIUM_MIN 0x0400 + +#define SMA_ATTR_MED_MANUF 0x0400 +#define SMA_ATTR_MED_SERIAL 0x0401 + +#define SMA_ATTR_MED_LENGTH 0x0402 +#define SMA_ATTR_MED_WIDTH 0x0403 +#define SMA_ATTR_MED_ASSIGNING_ORG 0x0404 +#define SMA_ATTR_MED_DENSITY_CODE 0x0405 + +#define SMA_ATTR_MED_MANUF_DATE 0x0406 +#define SMA_ATTR_MAM_CAPACITY 0x0407 +#define SMA_ATTR_MED_TYPE 0x0408 +#define SMA_ATTR_MED_TYPE_INFO 0x0409 +#define SMA_ATTR_MED_SERIAL_NUM 0x040a + +#define SMA_ATTR_ID_MEDIUM_MAX 0x07ff + +#define SMA_ATTR_ID_HOST_MIN 0x0800 + +#define SMA_ATTR_APP_VENDOR 0x0800 +#define SMA_ATTR_APP_NAME 0x0801 +#define SMA_ATTR_APP_VERSION 0x0802 +#define SMA_ATTR_USER_MED_TEXT_LABEL 0x0803 +#define SMA_ATTR_LAST_WRITTEN_TIME 0x0804 +#define SMA_ATTR_TEXT_LOCAL_ID 0x0805 +#define SMA_ATTR_BARCODE 0x0806 +#define SMA_ATTR_HOST_OWNER_NAME 0x0807 +#define SMA_ATTR_MEDIA_POOL 0x0808 +#define SMA_ATTR_PART_USER_LABEL 0x0809 +#define SMA_ATTR_LOAD_UNLOAD_AT_PART 0x080a +#define SMA_ATTR_APP_FORMAT_VERSION 0x080b +#define SMA_ATTR_VOL_COHERENCY_INFO 0x080c + +#define SMA_ATTR_ID_HOST_MAX 0x0bff + +#define SMA_ATTR_VENDOR_DEVICE_MIN 0x0c00 +#define SMA_ATTR_VENDOR_DEVICE_MAX 0x0fff +#define SMA_ATTR_VENDOR_MEDIUM_MIN 0x1000 +#define SMA_ATTR_VENDOR_MEDIUM_MAX 0x13ff +#define SMA_ATTR_VENDOR_HOST_MIN 0x1400 +#define SMA_ATTR_VENDOR_HOST_MAX 0x17ff + u_int8_t byte2; +#define SMA_FORMAT_BINARY 0x00 +#define SMA_FORMAT_ASCII 0x01 +#define SMA_FORMAT_TEXT 0x02 +#define SMA_FORMAT_MASK 0x03 +#define SMA_READ_ONLY 0x80 + u_int8_t length[2]; + u_int8_t attribute[0]; +}; + +struct scsi_attrib_list_header { + u_int8_t length[4]; + u_int8_t first_attr_0[0]; +}; + +struct scsi_attrib_lv_list { + u_int8_t length[2]; + u_int8_t first_lv_number; + u_int8_t num_logical_volumes; +}; + +struct scsi_attrib_vendser { + uint8_t vendor[8]; + uint8_t serial_num[32]; +}; + +/* + * These values are used to decode the Volume Coherency Information + * Attribute (0x080c) for LTFS-format coherency information. + * Although the Application Client Specific lengths are different for + * Version 0 and Version 1, the data is in fact the same. The length + * difference was due to a code bug. + */ +#define SCSI_LTFS_VER0_LEN 42 +#define SCSI_LTFS_VER1_LEN 43 +#define SCSI_LTFS_UUID_LEN 36 +#define SCSI_LTFS_STR_NAME "LTFS" +#define SCSI_LTFS_STR_LEN 4 + +typedef enum { + SCSI_ATTR_FLAG_NONE = 0x00, + SCSI_ATTR_FLAG_HEX = 0x01, + SCSI_ATTR_FLAG_FP = 0x02, + SCSI_ATTR_FLAG_DIV_10 = 0x04, + SCSI_ATTR_FLAG_FP_1DIGIT = 0x08 +} scsi_attrib_flags; + +typedef enum { + SCSI_ATTR_OUTPUT_NONE = 0x00, + SCSI_ATTR_OUTPUT_TEXT_MASK = 0x03, + SCSI_ATTR_OUTPUT_TEXT_RAW = 0x00, + SCSI_ATTR_OUTPUT_TEXT_ESC = 0x01, + SCSI_ATTR_OUTPUT_TEXT_RSV1 = 0x02, + SCSI_ATTR_OUTPUT_TEXT_RSV2 = 0x03, + SCSI_ATTR_OUTPUT_NONASCII_MASK = 0x0c, + SCSI_ATTR_OUTPUT_NONASCII_TRIM = 0x00, + SCSI_ATTR_OUTPUT_NONASCII_ESC = 0x04, + SCSI_ATTR_OUTPUT_NONASCII_RAW = 0x08, + SCSI_ATTR_OUTPUT_NONASCII_RSV1 = 0x0c, + SCSI_ATTR_OUTPUT_FIELD_MASK = 0xf0, + SCSI_ATTR_OUTPUT_FIELD_ALL = 0xf0, + SCSI_ATTR_OUTPUT_FIELD_NONE = 0x00, + SCSI_ATTR_OUTPUT_FIELD_DESC = 0x10, + SCSI_ATTR_OUTPUT_FIELD_NUM = 0x20, + SCSI_ATTR_OUTPUT_FIELD_SIZE = 0x40, + SCSI_ATTR_OUTPUT_FIELD_RW = 0x80 +} scsi_attrib_output_flags; + +struct sbuf; + +struct scsi_attrib_table_entry +{ + u_int32_t id; + u_int32_t flags; + const char *desc; + const char *suffix; + int (*to_str)(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); + int (*parse_str)(char *str, struct scsi_mam_attribute_header *hdr, + uint32_t alloc_len, uint32_t flags, char *error_str, + int error_str_len); +}; + struct scsi_rw_6 { u_int8_t opcode; @@ -1750,6 +1960,8 @@ struct ata_pass_16 { #define READ_16 0x88 #define COMPARE_AND_WRITE 0x89 #define WRITE_16 0x8A +#define READ_ATTRIBUTE 0x8C +#define WRITE_ATTRIBUTE 0x8D #define WRITE_VERIFY_16 0x8E #define VERIFY_16 0x8F #define SYNCHRONIZE_CACHE_16 0x91 @@ -3272,8 +3484,6 @@ struct cam_device; extern const char *scsi_sense_key_text[]; -struct sbuf; - __BEGIN_DECLS void scsi_sense_desc(int sense_key, int asc, int ascq, struct scsi_inquiry_data *inq_data, @@ -3465,6 +3675,63 @@ int scsi_parse_transportid(char *transportid_str, #endif char *error_str, int error_str_len); + +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); + +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); + +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); + +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); + +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); + +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); + +struct scsi_attrib_table_entry *scsi_find_attrib_entry( + struct scsi_attrib_table_entry *table, + size_t num_table_entries, uint32_t id); + +struct scsi_attrib_table_entry *scsi_get_attrib_entry(uint32_t 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); + +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 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); + void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), @@ -3659,6 +3926,18 @@ void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int start, int load_eject, int immediate, u_int8_t sense_len, u_int32_t 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); +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); void scsi_security_protocol_in(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), |