summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/cam/scsi/scsi_all.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/cam/scsi/scsi_all.c')
-rw-r--r--freebsd/sys/cam/scsi/scsi_all.c2726
1 files changed, 2604 insertions, 122 deletions
diff --git a/freebsd/sys/cam/scsi/scsi_all.c b/freebsd/sys/cam/scsi/scsi_all.c
index 7bb0425d..0cb7118a 100644
--- a/freebsd/sys/cam/scsi/scsi_all.c
+++ b/freebsd/sys/cam/scsi/scsi_all.c
@@ -50,11 +50,13 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
+#include <sys/ctype.h>
#else
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
#endif
#include <cam/cam.h>
@@ -116,6 +118,7 @@ static void fetchtableentries(int sense_key, int asc, int ascq,
struct scsi_inquiry_data *,
const struct sense_key_table_entry **,
const struct asc_table_entry **);
+
#ifdef _KERNEL
static void init_scsi_delay(void);
static int sysctl_scsi_delay(SYSCTL_HANDLER_ARGS);
@@ -159,7 +162,7 @@ static struct scsi_op_quirk_entry scsi_op_quirk_table[] = {
* feel free to change this quirk entry.
*/
{T_CDROM, SIP_MEDIA_REMOVABLE, "PLEXTOR", "CD-ROM PX*", "*"},
- sizeof(plextor_cd_ops)/sizeof(struct op_table_entry),
+ nitems(plextor_cd_ops),
plextor_cd_ops
}
};
@@ -180,7 +183,7 @@ static struct op_table_entry scsi_op_codes[] = {
*
* SCSI Operation Codes
* Numeric Sorted Listing
- * as of 3/11/08
+ * as of 5/26/15
*
* D - DIRECT ACCESS DEVICE (SBC-2) device column key
* .T - SEQUENTIAL ACCESS DEVICE (SSC-2) -----------------
@@ -478,7 +481,8 @@ static struct op_table_entry scsi_op_codes[] = {
*/
/* 88 MM O O O READ(16) */
{ 0x88, D | T | W | O | B, "READ(16)" },
- /* 89 */
+ /* 89 O COMPARE AND WRITE*/
+ { 0x89, D, "COMPARE AND WRITE" },
/* 8A OM O O O WRITE(16) */
{ 0x8A, D | T | W | O | B, "WRITE(16)" },
/* 8B O ORWRITE */
@@ -505,20 +509,25 @@ static struct op_table_entry scsi_op_codes[] = {
{ 0x93, D, "WRITE SAME(16)" },
/* 93 M ERASE(16) */
{ 0x93, T, "ERASE(16)" },
- /* 94 [usage proposed by SCSI Socket Services project] */
- /* 95 [usage proposed by SCSI Socket Services project] */
- /* 96 [usage proposed by SCSI Socket Services project] */
- /* 97 [usage proposed by SCSI Socket Services project] */
+ /* 94 O ZBC OUT */
+ { 0x94, ALL, "ZBC OUT" },
+ /* 95 O ZBC IN */
+ { 0x95, ALL, "ZBC IN" },
+ /* 96 */
+ /* 97 */
/* 98 */
/* 99 */
- /* 9A */
- /* 9B */
- /* 9C */
- /* 9D */
+ /* 9A O WRITE STREAM(16) */
+ { 0x9A, D, "WRITE STREAM(16)" },
+ /* 9B OOOOOOOOOO OOO READ BUFFER(16) */
+ { 0x9B, ALL & ~(B) , "READ BUFFER(16)" },
+ /* 9C O WRITE ATOMIC(16) */
+ { 0x9C, D, "WRITE ATOMIC(16)" },
+ /* 9D SERVICE ACTION BIDIRECTIONAL */
+ { 0x9D, ALL, "SERVICE ACTION BIDIRECTIONAL" },
/* XXX KDM ALL for this? op-num.txt defines it for none.. */
/* 9E SERVICE ACTION IN(16) */
{ 0x9E, ALL, "SERVICE ACTION IN(16)" },
- /* XXX KDM ALL for this? op-num.txt defines it for ADC.. */
/* 9F M SERVICE ACTION OUT(16) */
{ 0x9F, ALL, "SERVICE ACTION OUT(16)" },
/* A0 MMOOO OMMM OMO REPORT LUNS */
@@ -643,8 +652,7 @@ scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data)
match = cam_quirkmatch((caddr_t)inq_data,
(caddr_t)scsi_op_quirk_table,
- sizeof(scsi_op_quirk_table)/
- sizeof(*scsi_op_quirk_table),
+ nitems(scsi_op_quirk_table),
sizeof(*scsi_op_quirk_table),
scsi_inquiry_match);
}
@@ -653,7 +661,7 @@ scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data)
table[0] = ((struct scsi_op_quirk_entry *)match)->op_table;
num_ops[0] = ((struct scsi_op_quirk_entry *)match)->num_ops;
table[1] = scsi_op_codes;
- num_ops[1] = sizeof(scsi_op_codes)/sizeof(scsi_op_codes[0]);
+ num_ops[1] = nitems(scsi_op_codes);
num_tables = 2;
} else {
/*
@@ -664,7 +672,7 @@ scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data)
return("Vendor Specific Command");
table[0] = scsi_op_codes;
- num_ops[0] = sizeof(scsi_op_codes)/sizeof(scsi_op_codes[0]);
+ num_ops[0] = nitems(scsi_op_codes);
num_tables = 1;
}
@@ -672,6 +680,12 @@ scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data)
if (pd_type == T_RBC)
pd_type = T_DIRECT;
+ /*
+ * Host managed drives are direct access for the most part.
+ */
+ if (pd_type == T_ZBC_HM)
+ pd_type = T_DIRECT;
+
/* Map NODEVICE to Direct Access Device to handle REPORT LUNS, etc. */
if (pd_type == T_NODEVICE)
pd_type = T_DIRECT;
@@ -735,9 +749,6 @@ const struct sense_key_table_entry sense_key_table[] =
{ SSD_KEY_COMPLETED, SS_NOP, "COMPLETED" }
};
-const int sense_key_table_size =
- sizeof(sense_key_table)/sizeof(sense_key_table[0]);
-
static struct asc_table_entry quantum_fireball_entries[] = {
{ SST(0x04, 0x0b, SS_START | SSQ_DECREMENT_COUNT | ENXIO,
"Logical unit not ready, initializing cmd. required") }
@@ -922,7 +933,7 @@ static struct scsi_sense_quirk_entry sense_quirk_table[] = {
*/
{T_DIRECT, SIP_MEDIA_FIXED, "QUANTUM", "FIREBALL S*", "*"},
/*num_sense_keys*/0,
- sizeof(quantum_fireball_entries)/sizeof(struct asc_table_entry),
+ nitems(quantum_fireball_entries),
/*sense key entries*/NULL,
quantum_fireball_entries
},
@@ -933,7 +944,7 @@ static struct scsi_sense_quirk_entry sense_quirk_table[] = {
*/
{T_DIRECT, SIP_MEDIA_REMOVABLE, "SONY", "SMO-*", "*"},
/*num_sense_keys*/0,
- sizeof(sony_mo_entries)/sizeof(struct asc_table_entry),
+ nitems(sony_mo_entries),
/*sense key entries*/NULL,
sony_mo_entries
},
@@ -943,7 +954,7 @@ static struct scsi_sense_quirk_entry sense_quirk_table[] = {
*/
{T_DIRECT, SIP_MEDIA_FIXED, "HGST", "*", "*"},
/*num_sense_keys*/0,
- sizeof(hgst_entries)/sizeof(struct asc_table_entry),
+ nitems(hgst_entries),
/*sense key entries*/NULL,
hgst_entries
},
@@ -953,14 +964,13 @@ static struct scsi_sense_quirk_entry sense_quirk_table[] = {
*/
{T_DIRECT, SIP_MEDIA_FIXED, "SEAGATE", "*", "*"},
/*num_sense_keys*/0,
- sizeof(seagate_entries)/sizeof(struct asc_table_entry),
+ nitems(seagate_entries),
/*sense key entries*/NULL,
seagate_entries
}
};
-const int sense_quirk_table_size =
- sizeof(sense_quirk_table)/sizeof(sense_quirk_table[0]);
+const u_int sense_quirk_table_size = nitems(sense_quirk_table);
static struct asc_table_entry asc_table[] = {
/*
@@ -972,7 +982,7 @@ static struct asc_table_entry asc_table[] = {
*
* SCSI ASC/ASCQ Assignments
* Numeric Sorted Listing
- * as of 5/20/12
+ * as of 8/12/15
*
* D - DIRECT ACCESS DEVICE (SBC-2) device column key
* .T - SEQUENTIAL ACCESS DEVICE (SSC) -------------------
@@ -1064,6 +1074,9 @@ static struct asc_table_entry asc_table[] = {
/* DT P B */
{ SST(0x00, 0x20, SS_RDEF, /* XXX TBD */
"Extended copy information available") },
+ /* D */
+ { SST(0x00, 0x21, SS_RDEF, /* XXX TBD */
+ "Atomic command aborted due to ACA") },
/* D W O BK */
{ SST(0x01, 0x00, SS_RDEF,
"No index/sector signal") },
@@ -1083,7 +1096,7 @@ static struct asc_table_entry asc_table[] = {
{ SST(0x04, 0x00, SS_RDEF,
"Logical unit not ready, cause not reportable") },
/* DTLPWROMAEBKVF */
- { SST(0x04, 0x01, SS_TUR | SSQ_MANY | SSQ_DECREMENT_COUNT | EBUSY,
+ { SST(0x04, 0x01, SS_WAIT | EBUSY,
"Logical unit is in process of becoming ready") },
/* DTLPWROMAEBKVF */
{ SST(0x04, 0x02, SS_START | SSQ_DECREMENT_COUNT | ENXIO,
@@ -1110,7 +1123,7 @@ static struct asc_table_entry asc_table[] = {
{ SST(0x04, 0x09, SS_RDEF, /* XXX TBD */
"Logical unit not ready, self-test in progress") },
/* DTLPWROMAEBKVF */
- { SST(0x04, 0x0A, SS_TUR | SSQ_MANY | SSQ_DECREMENT_COUNT | ENXIO,
+ { SST(0x04, 0x0A, SS_WAIT | ENXIO,
"Logical unit not accessible, asymmetric access state transition")},
/* DTLPWROMAEBKVF */
{ SST(0x04, 0x0B, SS_FATAL | ENXIO,
@@ -1121,11 +1134,14 @@ static struct asc_table_entry asc_table[] = {
/* F */
{ SST(0x04, 0x0D, SS_RDEF, /* XXX TBD */
"Logical unit not ready, structure check required") },
+ /* DTL WR MAEBKVF */
+ { SST(0x04, 0x0E, SS_RDEF, /* XXX TBD */
+ "Logical unit not ready, security session in progress") },
/* DT WROM B */
{ SST(0x04, 0x10, SS_RDEF, /* XXX TBD */
"Logical unit not ready, auxiliary memory not accessible") },
/* DT WRO AEB VF */
- { SST(0x04, 0x11, SS_TUR | SSQ_MANY | SSQ_DECREMENT_COUNT | EBUSY,
+ { SST(0x04, 0x11, SS_WAIT | EBUSY,
"Logical unit not ready, notify (enable spinup) required") },
/* M V */
{ SST(0x04, 0x12, SS_RDEF, /* XXX TBD */
@@ -1160,6 +1176,24 @@ static struct asc_table_entry asc_table[] = {
/* DT MAEB */
{ SST(0x04, 0x1C, SS_RDEF, /* XXX TBD */
"Logical unit not ready, additional power use not yet granted") },
+ /* D */
+ { SST(0x04, 0x1D, SS_RDEF, /* XXX TBD */
+ "Logical unit not ready, configuration in progress") },
+ /* D */
+ { SST(0x04, 0x1E, SS_FATAL | ENXIO,
+ "Logical unit not ready, microcode activation required") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x04, 0x1F, SS_FATAL | ENXIO,
+ "Logical unit not ready, microcode download required") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x04, 0x20, SS_RDEF, /* XXX TBD */
+ "Logical unit not ready, logical unit reset required") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x04, 0x21, SS_RDEF, /* XXX TBD */
+ "Logical unit not ready, hard reset required") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x04, 0x22, SS_RDEF, /* XXX TBD */
+ "Logical unit not ready, power cycle required") },
/* DTL WROMAEBKVF */
{ SST(0x05, 0x00, SS_RDEF,
"Logical unit does not respond to selection") },
@@ -1199,6 +1233,9 @@ static struct asc_table_entry asc_table[] = {
/* DT WRO B */
{ SST(0x09, 0x04, SS_RDEF,
"Head select fault") },
+ /* DT RO B */
+ { SST(0x09, 0x05, SS_RDEF,
+ "Vibration induced tracking error") },
/* DTLPWROMAEBKVF */
{ SST(0x0A, 0x00, SS_FATAL | ENOSPC,
"Error log overflow") },
@@ -1232,6 +1269,30 @@ static struct asc_table_entry asc_table[] = {
/* D */
{ SST(0x0B, 0x09, SS_RDEF, /* XXX TBD */
"Warning - device statistics notification available") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x0B, 0x0A, SS_RDEF, /* XXX TBD */
+ "Warning - High critical temperature limit exceeded") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x0B, 0x0B, SS_RDEF, /* XXX TBD */
+ "Warning - Low critical temperature limit exceeded") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x0B, 0x0C, SS_RDEF, /* XXX TBD */
+ "Warning - High operating temperature limit exceeded") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x0B, 0x0D, SS_RDEF, /* XXX TBD */
+ "Warning - Low operating temperature limit exceeded") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x0B, 0x0E, SS_RDEF, /* XXX TBD */
+ "Warning - High citical humidity limit exceeded") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x0B, 0x0F, SS_RDEF, /* XXX TBD */
+ "Warning - Low citical humidity limit exceeded") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x0B, 0x10, SS_RDEF, /* XXX TBD */
+ "Warning - High operating humidity limit exceeded") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x0B, 0x11, SS_RDEF, /* XXX TBD */
+ "Warning - Low operating humidity limit exceeded") },
/* T R */
{ SST(0x0C, 0x00, SS_RDEF,
"Write error") },
@@ -1280,6 +1341,15 @@ static struct asc_table_entry asc_table[] = {
/* R */
{ SST(0x0C, 0x0F, SS_RDEF, /* XXX TBD */
"Defects in error window") },
+ /* D */
+ { SST(0x0C, 0x10, SS_RDEF, /* XXX TBD */
+ "Incomplete multiple atomic write operations") },
+ /* D */
+ { SST(0x0C, 0x11, SS_RDEF, /* XXX TBD */
+ "Write error - recovery scan needed") },
+ /* D */
+ { SST(0x0C, 0x12, SS_RDEF, /* XXX TBD */
+ "Write error - insufficient zone resources") },
/* DTLPWRO A K */
{ SST(0x0D, 0x00, SS_RDEF, /* XXX TBD */
"Error detected by third party temporary initiator") },
@@ -1391,6 +1461,9 @@ static struct asc_table_entry asc_table[] = {
/* D */
{ SST(0x11, 0x14, SS_RDEF, /* XXX TBD */
"Read error - LBA marked bad by application client") },
+ /* D */
+ { SST(0x11, 0x15, SS_RDEF, /* XXX TBD */
+ "Write after sanitize required") },
/* D W O BK */
{ SST(0x12, 0x00, SS_RDEF,
"Address mark not found for ID field") },
@@ -1593,40 +1666,52 @@ static struct asc_table_entry asc_table[] = {
{ SST(0x21, 0x03, SS_RDEF, /* XXX TBD */
"Invalid write crossing layer jump") },
/* D */
+ { SST(0x21, 0x04, SS_RDEF, /* XXX TBD */
+ "Unaligned write command") },
+ /* D */
+ { SST(0x21, 0x05, SS_RDEF, /* XXX TBD */
+ "Write boundary violation") },
+ /* D */
+ { SST(0x21, 0x06, SS_RDEF, /* XXX TBD */
+ "Attempt to read invalid data") },
+ /* D */
+ { SST(0x21, 0x07, SS_RDEF, /* XXX TBD */
+ "Read boundary violation") },
+ /* D */
{ SST(0x22, 0x00, SS_FATAL | EINVAL,
"Illegal function (use 20 00, 24 00, or 26 00)") },
/* DT P B */
- { SST(0x23, 0x00, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x00, SS_FATAL | EINVAL,
"Invalid token operation, cause not reportable") },
/* DT P B */
- { SST(0x23, 0x01, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x01, SS_FATAL | EINVAL,
"Invalid token operation, unsupported token type") },
/* DT P B */
- { SST(0x23, 0x02, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x02, SS_FATAL | EINVAL,
"Invalid token operation, remote token usage not supported") },
/* DT P B */
- { SST(0x23, 0x03, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x03, SS_FATAL | EINVAL,
"Invalid token operation, remote ROD token creation not supported") },
/* DT P B */
- { SST(0x23, 0x04, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x04, SS_FATAL | EINVAL,
"Invalid token operation, token unknown") },
/* DT P B */
- { SST(0x23, 0x05, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x05, SS_FATAL | EINVAL,
"Invalid token operation, token corrupt") },
/* DT P B */
- { SST(0x23, 0x06, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x06, SS_FATAL | EINVAL,
"Invalid token operation, token revoked") },
/* DT P B */
- { SST(0x23, 0x07, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x07, SS_FATAL | EINVAL,
"Invalid token operation, token expired") },
/* DT P B */
- { SST(0x23, 0x08, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x08, SS_FATAL | EINVAL,
"Invalid token operation, token cancelled") },
/* DT P B */
- { SST(0x23, 0x09, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x09, SS_FATAL | EINVAL,
"Invalid token operation, token deleted") },
/* DT P B */
- { SST(0x23, 0x0A, SS_RDEF, /* XXX TBD */
+ { SST(0x23, 0x0A, SS_FATAL | EINVAL,
"Invalid token operation, invalid token length") },
/* DTLPWROMAEBKVF */
{ SST(0x24, 0x00, SS_FATAL | EINVAL,
@@ -1677,28 +1762,28 @@ static struct asc_table_entry asc_table[] = {
{ SST(0x26, 0x05, SS_RDEF, /* XXX TBD */
"Data decryption error") },
/* DTLPWRO K */
- { SST(0x26, 0x06, SS_RDEF, /* XXX TBD */
+ { SST(0x26, 0x06, SS_FATAL | EINVAL,
"Too many target descriptors") },
/* DTLPWRO K */
- { SST(0x26, 0x07, SS_RDEF, /* XXX TBD */
+ { SST(0x26, 0x07, SS_FATAL | EINVAL,
"Unsupported target descriptor type code") },
/* DTLPWRO K */
- { SST(0x26, 0x08, SS_RDEF, /* XXX TBD */
+ { SST(0x26, 0x08, SS_FATAL | EINVAL,
"Too many segment descriptors") },
/* DTLPWRO K */
- { SST(0x26, 0x09, SS_RDEF, /* XXX TBD */
+ { SST(0x26, 0x09, SS_FATAL | EINVAL,
"Unsupported segment descriptor type code") },
/* DTLPWRO K */
- { SST(0x26, 0x0A, SS_RDEF, /* XXX TBD */
+ { SST(0x26, 0x0A, SS_FATAL | EINVAL,
"Unexpected inexact segment") },
/* DTLPWRO K */
- { SST(0x26, 0x0B, SS_RDEF, /* XXX TBD */
+ { SST(0x26, 0x0B, SS_FATAL | EINVAL,
"Inline data length exceeded") },
/* DTLPWRO K */
- { SST(0x26, 0x0C, SS_RDEF, /* XXX TBD */
+ { SST(0x26, 0x0C, SS_FATAL | EINVAL,
"Invalid operation for copy source or destination") },
/* DTLPWRO K */
- { SST(0x26, 0x0D, SS_RDEF, /* XXX TBD */
+ { SST(0x26, 0x0D, SS_FATAL | EINVAL,
"Copy segment granularity violation") },
/* DT PWROMAEBK */
{ SST(0x26, 0x0E, SS_RDEF, /* XXX TBD */
@@ -1715,6 +1800,9 @@ static struct asc_table_entry asc_table[] = {
/* T */
{ SST(0x26, 0x12, SS_RDEF, /* XXX TBD */
"Vendor specific key reference not found") },
+ /* D */
+ { SST(0x26, 0x13, SS_RDEF, /* XXX TBD */
+ "Application tag mode page is invalid") },
/* DT WRO BK */
{ SST(0x27, 0x00, SS_FATAL | EACCES,
"Write protected") },
@@ -1737,8 +1825,11 @@ static struct asc_table_entry asc_table[] = {
{ SST(0x27, 0x06, SS_RDEF, /* XXX TBD */
"Conditional write protect") },
/* D B */
- { SST(0x27, 0x07, SS_RDEF, /* XXX TBD */
+ { SST(0x27, 0x07, SS_FATAL | ENOSPC,
"Space allocation failed write protect") },
+ /* D */
+ { SST(0x27, 0x08, SS_FATAL | EACCES,
+ "Zone is read only") },
/* DTLPWROMAEBKVF */
{ SST(0x28, 0x00, SS_FATAL | ENXIO,
"Not ready to ready change, medium may have changed") },
@@ -1882,12 +1973,33 @@ static struct asc_table_entry asc_table[] = {
/* D */
{ SST(0x2C, 0x0C, SS_RDEF, /* XXX TBD */
"ORWRITE generation does not match") },
+ /* D */
+ { SST(0x2C, 0x0D, SS_RDEF, /* XXX TBD */
+ "Reset write pointer not allowed") },
+ /* D */
+ { SST(0x2C, 0x0E, SS_RDEF, /* XXX TBD */
+ "Zone is offline") },
+ /* D */
+ { SST(0x2C, 0x0F, SS_RDEF, /* XXX TBD */
+ "Stream not open") },
+ /* D */
+ { SST(0x2C, 0x10, SS_RDEF, /* XXX TBD */
+ "Unwritten data in zone") },
/* T */
{ SST(0x2D, 0x00, SS_RDEF,
"Overwrite error on update in place") },
/* R */
{ SST(0x2E, 0x00, SS_RDEF, /* XXX TBD */
"Insufficient time for operation") },
+ /* D */
+ { SST(0x2E, 0x01, SS_RDEF, /* XXX TBD */
+ "Command timeout before processing") },
+ /* D */
+ { SST(0x2E, 0x02, SS_RDEF, /* XXX TBD */
+ "Command timeout during processing") },
+ /* D */
+ { SST(0x2E, 0x03, SS_RDEF, /* XXX TBD */
+ "Command timeout during processing due to error recovery") },
/* DTLPWROMAEBKVF */
{ SST(0x2F, 0x00, SS_RDEF,
"Commands cleared by another initiator") },
@@ -1897,6 +2009,9 @@ static struct asc_table_entry asc_table[] = {
/* DTLPWROMAEBKVF */
{ SST(0x2F, 0x02, SS_RDEF, /* XXX TBD */
"Commands cleared by device server") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x2F, 0x03, SS_RDEF, /* XXX TBD */
+ "Some commands cleared by queuing layer event") },
/* DT WROM BK */
{ SST(0x30, 0x00, SS_RDEF,
"Incompatible medium installed") },
@@ -2194,6 +2309,15 @@ static struct asc_table_entry asc_table[] = {
/* DTLPWR MAEBK F */
{ SST(0x3F, 0x14, SS_RDEF, /* XXX TBD */
"iSCSI IP address changed") },
+ /* DTLPWR MAEBK */
+ { SST(0x3F, 0x15, SS_RDEF, /* XXX TBD */
+ "Inspect referrals sense descriptors") },
+ /* DTLPWROMAEBKVF */
+ { SST(0x3F, 0x16, SS_RDEF, /* XXX TBD */
+ "Microcode has been changed without reset") },
+ /* D */
+ { SST(0x3F, 0x17, SS_RDEF, /* XXX TBD */
+ "Zone transition to full") },
/* D */
{ SST(0x40, 0x00, SS_RDEF,
"RAM failure") }, /* deprecated - use 40 NN instead */
@@ -2303,6 +2427,30 @@ static struct asc_table_entry asc_table[] = {
/* DT PWROMAEBK F */
{ SST(0x4B, 0x0D, SS_RDEF, /* XXX TBD */
"Data-out buffer error") },
+ /* DT PWROMAEBK F */
+ { SST(0x4B, 0x0E, SS_RDEF, /* XXX TBD */
+ "PCIe fabric error") },
+ /* DT PWROMAEBK F */
+ { SST(0x4B, 0x0F, SS_RDEF, /* XXX TBD */
+ "PCIe completion timeout") },
+ /* DT PWROMAEBK F */
+ { SST(0x4B, 0x10, SS_RDEF, /* XXX TBD */
+ "PCIe completer abort") },
+ /* DT PWROMAEBK F */
+ { SST(0x4B, 0x11, SS_RDEF, /* XXX TBD */
+ "PCIe poisoned TLP received") },
+ /* DT PWROMAEBK F */
+ { SST(0x4B, 0x12, SS_RDEF, /* XXX TBD */
+ "PCIe ECRC check failed") },
+ /* DT PWROMAEBK F */
+ { SST(0x4B, 0x13, SS_RDEF, /* XXX TBD */
+ "PCIe unsupported request") },
+ /* DT PWROMAEBK F */
+ { SST(0x4B, 0x14, SS_RDEF, /* XXX TBD */
+ "PCIe ACS violation") },
+ /* DT PWROMAEBK F */
+ { SST(0x4B, 0x15, SS_RDEF, /* XXX TBD */
+ "PCIe TLP prefix blocket") },
/* DTLPWROMAEBKVF */
{ SST(0x4C, 0x00, SS_RDEF,
"Logical unit failed self-configuration") },
@@ -2360,6 +2508,21 @@ static struct asc_table_entry asc_table[] = {
/* M */
{ SST(0x53, 0x08, SS_RDEF, /* XXX TBD */
"Element status unknown") },
+ /* M */
+ { SST(0x53, 0x09, SS_RDEF, /* XXX TBD */
+ "Data transfer device error - load failed") },
+ /* M */
+ { SST(0x53, 0x0A, SS_RDEF, /* XXX TBD */
+ "Data transfer device error - unload failed") },
+ /* M */
+ { SST(0x53, 0x0B, SS_RDEF, /* XXX TBD */
+ "Data transfer device error - unload missing") },
+ /* M */
+ { SST(0x53, 0x0C, SS_RDEF, /* XXX TBD */
+ "Data transfer device error - eject failed") },
+ /* M */
+ { SST(0x53, 0x0D, SS_RDEF, /* XXX TBD */
+ "Data transfer device error - library communication failed") },
/* P */
{ SST(0x54, 0x00, SS_RDEF,
"SCSI to host system interface failure") },
@@ -2405,6 +2568,15 @@ static struct asc_table_entry asc_table[] = {
/* DT P B */
{ SST(0x55, 0x0D, SS_RDEF, /* XXX TBD */
"Insufficient resources to create ROD token") },
+ /* D */
+ { SST(0x55, 0x0E, SS_RDEF, /* XXX TBD */
+ "Insufficient zone resources") },
+ /* D */
+ { SST(0x55, 0x0F, SS_RDEF, /* XXX TBD */
+ "Insufficient zone resources to complete write") },
+ /* D */
+ { SST(0x55, 0x10, SS_RDEF, /* XXX TBD */
+ "Maximum number of streams open") },
/* R */
{ SST(0x57, 0x00, SS_RDEF,
"Unable to recover table-of-contents") },
@@ -2825,6 +2997,9 @@ static struct asc_table_entry asc_table[] = {
/* A */
{ SST(0x68, 0x00, SS_RDEF,
"Logical unit not configured") },
+ /* D */
+ { SST(0x68, 0x01, SS_RDEF,
+ "Subsidiary logical unit not configured") },
/* A */
{ SST(0x69, 0x00, SS_RDEF,
"Data loss on logical unit") },
@@ -3031,7 +3206,7 @@ static struct asc_table_entry asc_table[] = {
"Security conflict in translated device") }
};
-const int asc_table_size = sizeof(asc_table)/sizeof(asc_table[0]);
+const u_int asc_table_size = nitems(asc_table);
struct asc_key
{
@@ -3124,14 +3299,14 @@ fetchtableentries(int sense_key, int asc, int ascq,
sense_tables[0] = quirk->sense_key_info;
sense_tables_size[0] = quirk->num_sense_keys;
sense_tables[1] = sense_key_table;
- sense_tables_size[1] = sense_key_table_size;
+ sense_tables_size[1] = nitems(sense_key_table);
num_sense_tables = 2;
} else {
asc_tables[0] = asc_table;
asc_tables_size[0] = asc_table_size;
num_asc_tables = 1;
sense_tables[0] = sense_key_table;
- sense_tables_size[0] = sense_key_table_size;
+ sense_tables_size[0] = nitems(sense_key_table);
num_sense_tables = 1;
}
@@ -3297,14 +3472,32 @@ scsi_error_action(struct ccb_scsiio *csio, struct scsi_inquiry_data *inq_data,
char *
scsi_cdb_string(u_int8_t *cdb_ptr, char *cdb_string, size_t len)
{
+ struct sbuf sb;
+ int error;
+
+ if (len == 0)
+ return ("");
+
+ sbuf_new(&sb, cdb_string, len, SBUF_FIXEDLEN);
+
+ scsi_cdb_sbuf(cdb_ptr, &sb);
+
+ /* ENOMEM just means that the fixed buffer is full, OK to ignore */
+ error = sbuf_finish(&sb);
+ if (error != 0 && error != ENOMEM)
+ return ("");
+
+ return(sbuf_data(&sb));
+}
+
+void
+scsi_cdb_sbuf(u_int8_t *cdb_ptr, struct sbuf *sb)
+{
u_int8_t cdb_len;
int i;
if (cdb_ptr == NULL)
- return("");
-
- /* Silence warnings */
- cdb_len = 0;
+ return;
/*
* This is taken from the SCSI-3 draft spec.
@@ -3341,12 +3534,11 @@ scsi_cdb_string(u_int8_t *cdb_ptr, char *cdb_string, size_t len)
cdb_len = 12;
break;
}
- *cdb_string = '\0';
+
for (i = 0; i < cdb_len; i++)
- snprintf(cdb_string + strlen(cdb_string),
- len - strlen(cdb_string), "%02hhx ", cdb_ptr[i]);
+ sbuf_printf(sb, "%02hhx ", cdb_ptr[i]);
- return(cdb_string);
+ return;
}
const char *
@@ -3395,7 +3587,6 @@ scsi_command_string(struct cam_device *device, struct ccb_scsiio *csio,
#endif /* _KERNEL/!_KERNEL */
{
struct scsi_inquiry_data *inq_data;
- char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1];
#ifdef _KERNEL
struct ccb_getdev *cgd;
#endif /* _KERNEL */
@@ -3428,15 +3619,13 @@ scsi_command_string(struct cam_device *device, struct ccb_scsiio *csio,
#endif /* _KERNEL/!_KERNEL */
if ((csio->ccb_h.flags & CAM_CDB_POINTER) != 0) {
- sbuf_printf(sb, "%s. CDB: %s",
- scsi_op_desc(csio->cdb_io.cdb_ptr[0], inq_data),
- scsi_cdb_string(csio->cdb_io.cdb_ptr, cdb_str,
- sizeof(cdb_str)));
+ sbuf_printf(sb, "%s. CDB: ",
+ scsi_op_desc(csio->cdb_io.cdb_ptr[0], inq_data));
+ scsi_cdb_sbuf(csio->cdb_io.cdb_ptr, sb);
} else {
- sbuf_printf(sb, "%s. CDB: %s",
- scsi_op_desc(csio->cdb_io.cdb_bytes[0], inq_data),
- scsi_cdb_string(csio->cdb_io.cdb_bytes, cdb_str,
- sizeof(cdb_str)));
+ sbuf_printf(sb, "%s. CDB: ",
+ scsi_op_desc(csio->cdb_io.cdb_bytes[0], inq_data));
+ scsi_cdb_sbuf(csio->cdb_io.cdb_bytes, sb);
}
#ifdef _KERNEL
@@ -3468,7 +3657,7 @@ scsi_desc_iterate(struct scsi_sense_data_desc *sense, u_int sense_len,
/*
* The length of data actually returned may be different than the
- * extra_len recorded in the sturcture.
+ * extra_len recorded in the structure.
*/
desc_len = sense_len -offsetof(struct scsi_sense_data_desc, sense_desc);
@@ -3808,8 +3997,6 @@ scsi_set_sense_data_va(struct scsi_sense_data *sense_data,
*/
sense->extra_len = 10;
sense_len = (int)va_arg(ap, int);
- len_to_copy = MIN(sense_len, SSD_EXTRA_MAX -
- sense->extra_len);
data = (uint8_t *)va_arg(ap, uint8_t *);
switch (elem_type) {
@@ -3827,10 +4014,14 @@ scsi_set_sense_data_va(struct scsi_sense_data *sense_data,
uint8_t *data_dest;
int i;
- if (elem_type == SSD_ELEM_COMMAND)
+ if (elem_type == SSD_ELEM_COMMAND) {
data_dest = &sense->cmd_spec_info[0];
- else {
+ len_to_copy = MIN(sense_len,
+ sizeof(sense->cmd_spec_info));
+ } else {
data_dest = &sense->info[0];
+ len_to_copy = MIN(sense_len,
+ sizeof(sense->info));
/*
* We're setting the info field, so
* set the valid bit.
@@ -4083,6 +4274,7 @@ scsi_get_block_info(struct scsi_sense_data *sense_data, u_int sense_len,
switch (SID_TYPE(inq_data)) {
case T_DIRECT:
case T_RBC:
+ case T_ZBC_HM:
break;
default:
goto bailout;
@@ -4524,10 +4716,9 @@ scsi_sense_desc_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
struct scsi_inquiry_data *inq_data,
struct scsi_sense_desc_header *header)
{
- int i;
+ u_int i;
- for (i = 0; i < (sizeof(scsi_sense_printers) /
- sizeof(scsi_sense_printers[0])); i++) {
+ for (i = 0; i < nitems(scsi_sense_printers); i++) {
struct scsi_sense_desc_printer *printer;
printer = &scsi_sense_printers[i];
@@ -5236,6 +5427,9 @@ scsi_print_inquiry(struct scsi_inquiry_data *inq_data)
case T_ADC:
dtype = "Automation/Drive Interface";
break;
+ case T_ZBC_HM:
+ dtype = "Host Managed Zoned Block";
+ break;
case T_NODEVICE:
dtype = "Uninstalled";
break;
@@ -5304,8 +5498,8 @@ static struct {
u_int
scsi_calc_syncsrate(u_int period_factor)
{
- int i;
- int num_syncrates;
+ u_int i;
+ u_int num_syncrates;
/*
* It's a bug if period is zero, but if it is anyway, don't
@@ -5316,7 +5510,7 @@ scsi_calc_syncsrate(u_int period_factor)
return (3300);
}
- num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]);
+ num_syncrates = nitems(scsi_syncrates);
/* See if the period is in the "exception" table */
for (i = 0; i < num_syncrates; i++) {
@@ -5334,21 +5528,21 @@ scsi_calc_syncsrate(u_int period_factor)
}
/*
- * Return the SCSI sync parameter that corresponsd to
+ * Return the SCSI sync parameter that corresponds to
* the passed in period in 10ths of ns.
*/
u_int
scsi_calc_syncparam(u_int period)
{
- int i;
- int num_syncrates;
+ u_int i;
+ u_int num_syncrates;
if (period == 0)
return (~0); /* Async */
/* Adjust for exception table being in 100ths. */
period *= 10;
- num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]);
+ num_syncrates = nitems(scsi_syncrates);
/* See if the period is in the "exception" table */
for (i = 0; i < num_syncrates; i++) {
@@ -5450,33 +5644,1853 @@ scsi_devid_is_lun_name(uint8_t *bufp)
return 1;
}
+int
+scsi_devid_is_port_naa(uint8_t *bufp)
+{
+ struct scsi_vpd_id_descriptor *descr;
+
+ descr = (struct scsi_vpd_id_descriptor *)bufp;
+ if ((descr->id_type & SVPD_ID_ASSOC_MASK) != SVPD_ID_ASSOC_PORT)
+ return 0;
+ if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_NAA)
+ return 0;
+ return 1;
+}
+
struct scsi_vpd_id_descriptor *
-scsi_get_devid(struct scsi_vpd_device_id *id, uint32_t page_len,
+scsi_get_devid_desc(struct scsi_vpd_id_descriptor *desc, uint32_t len,
scsi_devid_checkfn_t ck_fn)
{
- struct scsi_vpd_id_descriptor *desc;
- uint8_t *page_end;
uint8_t *desc_buf_end;
- page_end = (uint8_t *)id + page_len;
- if (page_end < id->desc_list)
- return (NULL);
-
- desc_buf_end = MIN(id->desc_list + scsi_2btoul(id->length), page_end);
+ desc_buf_end = (uint8_t *)desc + len;
- for (desc = (struct scsi_vpd_id_descriptor *)id->desc_list;
- desc->identifier <= desc_buf_end
- && desc->identifier + desc->length <= desc_buf_end;
- desc = (struct scsi_vpd_id_descriptor *)(desc->identifier
+ for (; desc->identifier <= desc_buf_end &&
+ desc->identifier + desc->length <= desc_buf_end;
+ desc = (struct scsi_vpd_id_descriptor *)(desc->identifier
+ desc->length)) {
if (ck_fn == NULL || ck_fn((uint8_t *)desc) != 0)
return (desc);
}
+ return (NULL);
+}
+
+struct scsi_vpd_id_descriptor *
+scsi_get_devid(struct scsi_vpd_device_id *id, uint32_t page_len,
+ scsi_devid_checkfn_t ck_fn)
+{
+ uint32_t len;
+
+ if (page_len < sizeof(*id))
+ return (NULL);
+ len = MIN(scsi_2btoul(id->length), page_len - sizeof(*id));
+ return (scsi_get_devid_desc((struct scsi_vpd_id_descriptor *)
+ id->desc_list, len, ck_fn));
+}
+
+int
+scsi_transportid_sbuf(struct sbuf *sb, struct scsi_transportid_header *hdr,
+ uint32_t valid_len)
+{
+ switch (hdr->format_protocol & SCSI_TRN_PROTO_MASK) {
+ case SCSI_PROTO_FC: {
+ struct scsi_transportid_fcp *fcp;
+ uint64_t n_port_name;
+
+ fcp = (struct scsi_transportid_fcp *)hdr;
+
+ n_port_name = scsi_8btou64(fcp->n_port_name);
+
+ sbuf_printf(sb, "FCP address: 0x%.16jx",(uintmax_t)n_port_name);
+ break;
+ }
+ case SCSI_PROTO_SPI: {
+ struct scsi_transportid_spi *spi;
+
+ spi = (struct scsi_transportid_spi *)hdr;
+
+ sbuf_printf(sb, "SPI address: %u,%u",
+ scsi_2btoul(spi->scsi_addr),
+ scsi_2btoul(spi->rel_trgt_port_id));
+ break;
+ }
+ case SCSI_PROTO_SSA:
+ /*
+ * XXX KDM there is no transport ID defined in SPC-4 for
+ * SSA.
+ */
+ break;
+ case SCSI_PROTO_1394: {
+ struct scsi_transportid_1394 *sbp;
+ uint64_t eui64;
+
+ sbp = (struct scsi_transportid_1394 *)hdr;
+
+ eui64 = scsi_8btou64(sbp->eui64);
+ sbuf_printf(sb, "SBP address: 0x%.16jx", (uintmax_t)eui64);
+ break;
+ }
+ case SCSI_PROTO_RDMA: {
+ struct scsi_transportid_rdma *rdma;
+ unsigned int i;
+
+ rdma = (struct scsi_transportid_rdma *)hdr;
+
+ sbuf_printf(sb, "RDMA address: 0x");
+ for (i = 0; i < sizeof(rdma->initiator_port_id); i++)
+ sbuf_printf(sb, "%02x", rdma->initiator_port_id[i]);
+ break;
+ }
+ case SCSI_PROTO_ISCSI: {
+ uint32_t add_len, i;
+ uint8_t *iscsi_name = NULL;
+ int nul_found = 0;
+
+ sbuf_printf(sb, "iSCSI address: ");
+ if ((hdr->format_protocol & SCSI_TRN_FORMAT_MASK) ==
+ SCSI_TRN_ISCSI_FORMAT_DEVICE) {
+ struct scsi_transportid_iscsi_device *dev;
+
+ dev = (struct scsi_transportid_iscsi_device *)hdr;
+
+ /*
+ * Verify how much additional data we really have.
+ */
+ add_len = scsi_2btoul(dev->additional_length);
+ add_len = MIN(add_len, valid_len -
+ __offsetof(struct scsi_transportid_iscsi_device,
+ iscsi_name));
+ iscsi_name = &dev->iscsi_name[0];
+
+ } else if ((hdr->format_protocol & SCSI_TRN_FORMAT_MASK) ==
+ SCSI_TRN_ISCSI_FORMAT_PORT) {
+ struct scsi_transportid_iscsi_port *port;
+
+ port = (struct scsi_transportid_iscsi_port *)hdr;
+
+ add_len = scsi_2btoul(port->additional_length);
+ add_len = MIN(add_len, valid_len -
+ __offsetof(struct scsi_transportid_iscsi_port,
+ iscsi_name));
+ iscsi_name = &port->iscsi_name[0];
+ } else {
+ sbuf_printf(sb, "unknown format %x",
+ (hdr->format_protocol &
+ SCSI_TRN_FORMAT_MASK) >>
+ SCSI_TRN_FORMAT_SHIFT);
+ break;
+ }
+ if (add_len == 0) {
+ sbuf_printf(sb, "not enough data");
+ break;
+ }
+ /*
+ * This is supposed to be a NUL-terminated ASCII
+ * string, but you never know. So we're going to
+ * check. We need to do this because there is no
+ * sbuf equivalent of strncat().
+ */
+ for (i = 0; i < add_len; i++) {
+ if (iscsi_name[i] == '\0') {
+ nul_found = 1;
+ break;
+ }
+ }
+ /*
+ * If there is a NUL in the name, we can just use
+ * sbuf_cat(). Otherwise we need to use sbuf_bcat().
+ */
+ if (nul_found != 0)
+ sbuf_cat(sb, iscsi_name);
+ else
+ sbuf_bcat(sb, iscsi_name, add_len);
+ break;
+ }
+ case SCSI_PROTO_SAS: {
+ struct scsi_transportid_sas *sas;
+ uint64_t sas_addr;
+
+ sas = (struct scsi_transportid_sas *)hdr;
+
+ sas_addr = scsi_8btou64(sas->sas_address);
+ sbuf_printf(sb, "SAS address: 0x%.16jx", (uintmax_t)sas_addr);
+ break;
+ }
+ case SCSI_PROTO_ADITP:
+ case SCSI_PROTO_ATA:
+ case SCSI_PROTO_UAS:
+ /*
+ * No Transport ID format for ADI, ATA or USB is defined in
+ * SPC-4.
+ */
+ sbuf_printf(sb, "No known Transport ID format for protocol "
+ "%#x", hdr->format_protocol & SCSI_TRN_PROTO_MASK);
+ break;
+ case SCSI_PROTO_SOP: {
+ struct scsi_transportid_sop *sop;
+ struct scsi_sop_routing_id_norm *rid;
+
+ sop = (struct scsi_transportid_sop *)hdr;
+ rid = (struct scsi_sop_routing_id_norm *)sop->routing_id;
+
+ /*
+ * Note that there is no alternate format specified in SPC-4
+ * for the PCIe routing ID, so we don't really have a way
+ * to know whether the second byte of the routing ID is
+ * a device and function or just a function. So we just
+ * assume bus,device,function.
+ */
+ sbuf_printf(sb, "SOP Routing ID: %u,%u,%u",
+ rid->bus, rid->devfunc >> SCSI_TRN_SOP_DEV_SHIFT,
+ rid->devfunc & SCSI_TRN_SOP_FUNC_NORM_MAX);
+ break;
+ }
+ case SCSI_PROTO_NONE:
+ default:
+ sbuf_printf(sb, "Unknown protocol %#x",
+ hdr->format_protocol & SCSI_TRN_PROTO_MASK);
+ break;
+ }
+
+ return (0);
+}
+
+struct scsi_nv scsi_proto_map[] = {
+ { "fcp", SCSI_PROTO_FC },
+ { "spi", SCSI_PROTO_SPI },
+ { "ssa", SCSI_PROTO_SSA },
+ { "sbp", SCSI_PROTO_1394 },
+ { "1394", SCSI_PROTO_1394 },
+ { "srp", SCSI_PROTO_RDMA },
+ { "rdma", SCSI_PROTO_RDMA },
+ { "iscsi", SCSI_PROTO_ISCSI },
+ { "iqn", SCSI_PROTO_ISCSI },
+ { "sas", SCSI_PROTO_SAS },
+ { "aditp", SCSI_PROTO_ADITP },
+ { "ata", SCSI_PROTO_ATA },
+ { "uas", SCSI_PROTO_UAS },
+ { "usb", SCSI_PROTO_UAS },
+ { "sop", SCSI_PROTO_SOP }
+};
+
+const char *
+scsi_nv_to_str(struct scsi_nv *table, int num_table_entries, uint64_t value)
+{
+ int i;
+
+ for (i = 0; i < num_table_entries; i++) {
+ if (table[i].value == value)
+ return (table[i].name);
+ }
return (NULL);
}
+/*
+ * Given a name/value table, find a value matching the given name.
+ * Return values:
+ * SCSI_NV_FOUND - match found
+ * SCSI_NV_AMBIGUOUS - more than one match, none of them exact
+ * SCSI_NV_NOT_FOUND - no match found
+ */
+scsi_nv_status
+scsi_get_nv(struct scsi_nv *table, int num_table_entries,
+ char *name, int *table_entry, scsi_nv_flags flags)
+{
+ int i, num_matches = 0;
+
+ for (i = 0; i < num_table_entries; i++) {
+ size_t table_len, name_len;
+
+ table_len = strlen(table[i].name);
+ name_len = strlen(name);
+
+ if ((((flags & SCSI_NV_FLAG_IG_CASE) != 0)
+ && (strncasecmp(table[i].name, name, name_len) == 0))
+ || (((flags & SCSI_NV_FLAG_IG_CASE) == 0)
+ && (strncmp(table[i].name, name, name_len) == 0))) {
+ *table_entry = i;
+
+ /*
+ * Check for an exact match. If we have the same
+ * number of characters in the table as the argument,
+ * and we already know they're the same, we have
+ * an exact match.
+ */
+ if (table_len == name_len)
+ return (SCSI_NV_FOUND);
+
+ /*
+ * Otherwise, bump up the number of matches. We'll
+ * see later how many we have.
+ */
+ num_matches++;
+ }
+ }
+
+ if (num_matches > 1)
+ return (SCSI_NV_AMBIGUOUS);
+ else if (num_matches == 1)
+ return (SCSI_NV_FOUND);
+ else
+ return (SCSI_NV_NOT_FOUND);
+}
+
+/*
+ * Parse transport IDs for Fibre Channel, 1394 and SAS. Since these are
+ * all 64-bit numbers, the code is similar.
+ */
+int
+scsi_parse_transportid_64bit(int proto_id, char *id_str,
+ struct scsi_transportid_header **hdr,
+ unsigned int *alloc_len,
+#ifdef _KERNEL
+ struct malloc_type *type, int flags,
+#endif
+ char *error_str, int error_str_len)
+{
+ uint64_t value;
+ char *endptr;
+ int retval;
+ size_t alloc_size;
+
+ retval = 0;
+
+ value = strtouq(id_str, &endptr, 0);
+ if (*endptr != '\0') {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: error "
+ "parsing ID %s, 64-bit number required",
+ __func__, id_str);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+ switch (proto_id) {
+ case SCSI_PROTO_FC:
+ alloc_size = sizeof(struct scsi_transportid_fcp);
+ break;
+ case SCSI_PROTO_1394:
+ alloc_size = sizeof(struct scsi_transportid_1394);
+ break;
+ case SCSI_PROTO_SAS:
+ alloc_size = sizeof(struct scsi_transportid_sas);
+ break;
+ default:
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: unsupported "
+ "protocol %d", __func__, proto_id);
+ }
+ retval = 1;
+ goto bailout;
+ break; /* NOTREACHED */
+ }
+#ifdef _KERNEL
+ *hdr = malloc(alloc_size, type, flags);
+#else /* _KERNEL */
+ *hdr = malloc(alloc_size);
+#endif /*_KERNEL */
+ if (*hdr == NULL) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: unable to "
+ "allocate %zu bytes", __func__, alloc_size);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+ *alloc_len = alloc_size;
+
+ bzero(*hdr, alloc_size);
+
+ switch (proto_id) {
+ case SCSI_PROTO_FC: {
+ struct scsi_transportid_fcp *fcp;
+
+ fcp = (struct scsi_transportid_fcp *)(*hdr);
+ fcp->format_protocol = SCSI_PROTO_FC |
+ SCSI_TRN_FCP_FORMAT_DEFAULT;
+ scsi_u64to8b(value, fcp->n_port_name);
+ break;
+ }
+ case SCSI_PROTO_1394: {
+ struct scsi_transportid_1394 *sbp;
+
+ sbp = (struct scsi_transportid_1394 *)(*hdr);
+ sbp->format_protocol = SCSI_PROTO_1394 |
+ SCSI_TRN_1394_FORMAT_DEFAULT;
+ scsi_u64to8b(value, sbp->eui64);
+ break;
+ }
+ case SCSI_PROTO_SAS: {
+ struct scsi_transportid_sas *sas;
+
+ sas = (struct scsi_transportid_sas *)(*hdr);
+ sas->format_protocol = SCSI_PROTO_SAS |
+ SCSI_TRN_SAS_FORMAT_DEFAULT;
+ scsi_u64to8b(value, sas->sas_address);
+ break;
+ }
+ default:
+ break;
+ }
+bailout:
+ return (retval);
+}
+
+/*
+ * Parse a SPI (Parallel SCSI) address of the form: id,rel_tgt_port
+ */
+int
+scsi_parse_transportid_spi(char *id_str, struct scsi_transportid_header **hdr,
+ unsigned int *alloc_len,
+#ifdef _KERNEL
+ struct malloc_type *type, int flags,
+#endif
+ char *error_str, int error_str_len)
+{
+ unsigned long scsi_addr, target_port;
+ struct scsi_transportid_spi *spi;
+ char *tmpstr, *endptr;
+ int retval;
+
+ retval = 0;
+
+ tmpstr = strsep(&id_str, ",");
+ if (tmpstr == NULL) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len,
+ "%s: no ID found", __func__);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ scsi_addr = strtoul(tmpstr, &endptr, 0);
+ if (*endptr != '\0') {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: error "
+ "parsing SCSI ID %s, number required",
+ __func__, tmpstr);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+ if (id_str == NULL) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: no relative "
+ "target port found", __func__);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+ target_port = strtoul(id_str, &endptr, 0);
+ if (*endptr != '\0') {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: error "
+ "parsing relative target port %s, number "
+ "required", __func__, id_str);
+ }
+ retval = 1;
+ goto bailout;
+ }
+#ifdef _KERNEL
+ spi = malloc(sizeof(*spi), type, flags);
+#else
+ spi = malloc(sizeof(*spi));
+#endif
+ if (spi == NULL) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: unable to "
+ "allocate %zu bytes", __func__,
+ sizeof(*spi));
+ }
+ retval = 1;
+ goto bailout;
+ }
+ *alloc_len = sizeof(*spi);
+ bzero(spi, sizeof(*spi));
+
+ spi->format_protocol = SCSI_PROTO_SPI | SCSI_TRN_SPI_FORMAT_DEFAULT;
+ scsi_ulto2b(scsi_addr, spi->scsi_addr);
+ scsi_ulto2b(target_port, spi->rel_trgt_port_id);
+
+ *hdr = (struct scsi_transportid_header *)spi;
+bailout:
+ return (retval);
+}
+
+/*
+ * Parse an RDMA/SRP Initiator Port ID string. This is 32 hexadecimal digits,
+ * optionally prefixed by "0x" or "0X".
+ */
+int
+scsi_parse_transportid_rdma(char *id_str, struct scsi_transportid_header **hdr,
+ unsigned int *alloc_len,
+#ifdef _KERNEL
+ struct malloc_type *type, int flags,
+#endif
+ char *error_str, int error_str_len)
+{
+ struct scsi_transportid_rdma *rdma;
+ int retval;
+ size_t id_len, rdma_id_size;
+ uint8_t rdma_id[SCSI_TRN_RDMA_PORT_LEN];
+ char *tmpstr;
+ unsigned int i, j;
+
+ retval = 0;
+ id_len = strlen(id_str);
+ rdma_id_size = SCSI_TRN_RDMA_PORT_LEN;
+
+ /*
+ * Check the size. It needs to be either 32 or 34 characters long.
+ */
+ if ((id_len != (rdma_id_size * 2))
+ && (id_len != ((rdma_id_size * 2) + 2))) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: RDMA ID "
+ "must be 32 hex digits (0x prefix "
+ "optional), only %zu seen", __func__, id_len);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+ tmpstr = id_str;
+ /*
+ * If the user gave us 34 characters, the string needs to start
+ * with '0x'.
+ */
+ if (id_len == ((rdma_id_size * 2) + 2)) {
+ if ((tmpstr[0] == '0')
+ && ((tmpstr[1] == 'x') || (tmpstr[1] == 'X'))) {
+ tmpstr += 2;
+ } else {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: RDMA "
+ "ID prefix, if used, must be \"0x\", "
+ "got %s", __func__, tmpstr);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ }
+ bzero(rdma_id, sizeof(rdma_id));
+
+ /*
+ * Convert ASCII hex into binary bytes. There is no standard
+ * 128-bit integer type, and so no strtou128t() routine to convert
+ * from hex into a large integer. In the end, we're not going to
+ * an integer, but rather to a byte array, so that and the fact
+ * that we require the user to give us 32 hex digits simplifies the
+ * logic.
+ */
+ for (i = 0; i < (rdma_id_size * 2); i++) {
+ int cur_shift;
+ unsigned char c;
+
+ /* Increment the byte array one for every 2 hex digits */
+ j = i >> 1;
+
+ /*
+ * The first digit in every pair is the most significant
+ * 4 bits. The second is the least significant 4 bits.
+ */
+ if ((i % 2) == 0)
+ cur_shift = 4;
+ else
+ cur_shift = 0;
+
+ c = tmpstr[i];
+ /* Convert the ASCII hex character into a number */
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: "
+ "RDMA ID must be hex digits, got "
+ "invalid character %c", __func__,
+ tmpstr[i]);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ /*
+ * The converted number can't be less than 0; the type is
+ * unsigned, and the subtraction logic will not give us
+ * a negative number. So we only need to make sure that
+ * the value is not greater than 0xf. (i.e. make sure the
+ * user didn't give us a value like "0x12jklmno").
+ */
+ if (c > 0xf) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: "
+ "RDMA ID must be hex digits, got "
+ "invalid character %c", __func__,
+ tmpstr[i]);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+ rdma_id[j] |= c << cur_shift;
+ }
+
+#ifdef _KERNEL
+ rdma = malloc(sizeof(*rdma), type, flags);
+#else
+ rdma = malloc(sizeof(*rdma));
+#endif
+ if (rdma == NULL) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: unable to "
+ "allocate %zu bytes", __func__,
+ sizeof(*rdma));
+ }
+ retval = 1;
+ goto bailout;
+ }
+ *alloc_len = sizeof(*rdma);
+ bzero(rdma, *alloc_len);
+
+ rdma->format_protocol = SCSI_PROTO_RDMA | SCSI_TRN_RDMA_FORMAT_DEFAULT;
+ bcopy(rdma_id, rdma->initiator_port_id, SCSI_TRN_RDMA_PORT_LEN);
+
+ *hdr = (struct scsi_transportid_header *)rdma;
+
+bailout:
+ return (retval);
+}
+
+/*
+ * Parse an iSCSI name. The format is either just the name:
+ *
+ * iqn.2012-06.com.example:target0
+ * or the name, separator and initiator session ID:
+ *
+ * iqn.2012-06.com.example:target0,i,0x123
+ *
+ * The separator format is exact.
+ */
+int
+scsi_parse_transportid_iscsi(char *id_str, struct scsi_transportid_header **hdr,
+ unsigned int *alloc_len,
+#ifdef _KERNEL
+ struct malloc_type *type, int flags,
+#endif
+ char *error_str, int error_str_len)
+{
+ size_t id_len, sep_len, id_size, name_len;
+ int retval;
+ unsigned int i, sep_pos, sep_found;
+ const char *sep_template = ",i,0x";
+ const char *iqn_prefix = "iqn.";
+ struct scsi_transportid_iscsi_device *iscsi;
+
+ retval = 0;
+ sep_found = 0;
+
+ id_len = strlen(id_str);
+ sep_len = strlen(sep_template);
+
+ /*
+ * The separator is defined as exactly ',i,0x'. Any other commas,
+ * or any other form, is an error. So look for a comma, and once
+ * we find that, the next few characters must match the separator
+ * exactly. Once we get through the separator, there should be at
+ * least one character.
+ */
+ for (i = 0, sep_pos = 0; i < id_len; i++) {
+ if (sep_pos == 0) {
+ if (id_str[i] == sep_template[sep_pos])
+ sep_pos++;
+
+ continue;
+ }
+ if (sep_pos < sep_len) {
+ if (id_str[i] == sep_template[sep_pos]) {
+ sep_pos++;
+ continue;
+ }
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: "
+ "invalid separator in iSCSI name "
+ "\"%s\"",
+ __func__, id_str);
+ }
+ retval = 1;
+ goto bailout;
+ } else {
+ sep_found = 1;
+ break;
+ }
+ }
+
+ /*
+ * Check to see whether we have a separator but no digits after it.
+ */
+ if ((sep_pos != 0)
+ && (sep_found == 0)) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: no digits "
+ "found after separator in iSCSI name \"%s\"",
+ __func__, id_str);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+ /*
+ * The incoming ID string has the "iqn." prefix stripped off. We
+ * need enough space for the base structure (the structures are the
+ * same for the two iSCSI forms), the prefix, the ID string and a
+ * terminating NUL.
+ */
+ id_size = sizeof(*iscsi) + strlen(iqn_prefix) + id_len + 1;
+
+#ifdef _KERNEL
+ iscsi = malloc(id_size, type, flags);
+#else
+ iscsi = malloc(id_size);
+#endif
+ if (iscsi == NULL) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: unable to "
+ "allocate %zu bytes", __func__, id_size);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ *alloc_len = id_size;
+ bzero(iscsi, id_size);
+
+ iscsi->format_protocol = SCSI_PROTO_ISCSI;
+ if (sep_found == 0)
+ iscsi->format_protocol |= SCSI_TRN_ISCSI_FORMAT_DEVICE;
+ else
+ iscsi->format_protocol |= SCSI_TRN_ISCSI_FORMAT_PORT;
+ name_len = id_size - sizeof(*iscsi);
+ scsi_ulto2b(name_len, iscsi->additional_length);
+ snprintf(iscsi->iscsi_name, name_len, "%s%s", iqn_prefix, id_str);
+
+ *hdr = (struct scsi_transportid_header *)iscsi;
+
+bailout:
+ return (retval);
+}
+
+/*
+ * Parse a SCSI over PCIe (SOP) identifier. The Routing ID can either be
+ * of the form 'bus,device,function' or 'bus,function'.
+ */
+int
+scsi_parse_transportid_sop(char *id_str, struct scsi_transportid_header **hdr,
+ unsigned int *alloc_len,
+#ifdef _KERNEL
+ struct malloc_type *type, int flags,
+#endif
+ char *error_str, int error_str_len)
+{
+ struct scsi_transportid_sop *sop;
+ unsigned long bus, device, function;
+ char *tmpstr, *endptr;
+ int retval, device_spec;
+
+ retval = 0;
+ device_spec = 0;
+ device = 0;
+
+ tmpstr = strsep(&id_str, ",");
+ if ((tmpstr == NULL)
+ || (*tmpstr == '\0')) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: no ID found",
+ __func__);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ bus = strtoul(tmpstr, &endptr, 0);
+ if (*endptr != '\0') {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: error "
+ "parsing PCIe bus %s, number required",
+ __func__, tmpstr);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ if ((id_str == NULL)
+ || (*id_str == '\0')) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: no PCIe "
+ "device or function found", __func__);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ tmpstr = strsep(&id_str, ",");
+ function = strtoul(tmpstr, &endptr, 0);
+ if (*endptr != '\0') {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: error "
+ "parsing PCIe device/function %s, number "
+ "required", __func__, tmpstr);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ /*
+ * Check to see whether the user specified a third value. If so,
+ * the second is the device.
+ */
+ if (id_str != NULL) {
+ if (*id_str == '\0') {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: "
+ "no PCIe function found", __func__);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ device = function;
+ device_spec = 1;
+ function = strtoul(id_str, &endptr, 0);
+ if (*endptr != '\0') {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: "
+ "error parsing PCIe function %s, "
+ "number required", __func__, id_str);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ }
+ if (bus > SCSI_TRN_SOP_BUS_MAX) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: bus value "
+ "%lu greater than maximum %u", __func__,
+ bus, SCSI_TRN_SOP_BUS_MAX);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+ if ((device_spec != 0)
+ && (device > SCSI_TRN_SOP_DEV_MASK)) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: device value "
+ "%lu greater than maximum %u", __func__,
+ device, SCSI_TRN_SOP_DEV_MAX);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+ if (((device_spec != 0)
+ && (function > SCSI_TRN_SOP_FUNC_NORM_MAX))
+ || ((device_spec == 0)
+ && (function > SCSI_TRN_SOP_FUNC_ALT_MAX))) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: function value "
+ "%lu greater than maximum %u", __func__,
+ function, (device_spec == 0) ?
+ SCSI_TRN_SOP_FUNC_ALT_MAX :
+ SCSI_TRN_SOP_FUNC_NORM_MAX);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+#ifdef _KERNEL
+ sop = malloc(sizeof(*sop), type, flags);
+#else
+ sop = malloc(sizeof(*sop));
+#endif
+ if (sop == NULL) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: unable to "
+ "allocate %zu bytes", __func__, sizeof(*sop));
+ }
+ retval = 1;
+ goto bailout;
+ }
+ *alloc_len = sizeof(*sop);
+ bzero(sop, sizeof(*sop));
+ sop->format_protocol = SCSI_PROTO_SOP | SCSI_TRN_SOP_FORMAT_DEFAULT;
+ if (device_spec != 0) {
+ struct scsi_sop_routing_id_norm rid;
+
+ rid.bus = bus;
+ rid.devfunc = (device << SCSI_TRN_SOP_DEV_SHIFT) | function;
+ bcopy(&rid, sop->routing_id, MIN(sizeof(rid),
+ sizeof(sop->routing_id)));
+ } else {
+ struct scsi_sop_routing_id_alt rid;
+
+ rid.bus = bus;
+ rid.function = function;
+ bcopy(&rid, sop->routing_id, MIN(sizeof(rid),
+ sizeof(sop->routing_id)));
+ }
+
+ *hdr = (struct scsi_transportid_header *)sop;
+bailout:
+ return (retval);
+}
+
+/*
+ * transportid_str: NUL-terminated string with format: protcol,id
+ * The ID is protocol specific.
+ * hdr: Storage will be allocated for the transport ID.
+ * alloc_len: The amount of memory allocated is returned here.
+ * type: Malloc bucket (kernel only).
+ * flags: Malloc flags (kernel only).
+ * error_str: If non-NULL, it will contain error information (without
+ * a terminating newline) if an error is returned.
+ * error_str_len: Allocated length of the error string.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+scsi_parse_transportid(char *transportid_str,
+ struct scsi_transportid_header **hdr,
+ unsigned int *alloc_len,
+#ifdef _KERNEL
+ struct malloc_type *type, int flags,
+#endif
+ char *error_str, int error_str_len)
+{
+ char *tmpstr;
+ scsi_nv_status status;
+ u_int num_proto_entries;
+ int retval, table_entry;
+
+ retval = 0;
+ table_entry = 0;
+
+ /*
+ * We do allow a period as well as a comma to separate the protocol
+ * from the ID string. This is to accommodate iSCSI names, which
+ * start with "iqn.".
+ */
+ tmpstr = strsep(&transportid_str, ",.");
+ if (tmpstr == NULL) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len,
+ "%s: transportid_str is NULL", __func__);
+ }
+ retval = 1;
+ goto bailout;
+ }
+
+ num_proto_entries = nitems(scsi_proto_map);
+ status = scsi_get_nv(scsi_proto_map, num_proto_entries, tmpstr,
+ &table_entry, SCSI_NV_FLAG_IG_CASE);
+ if (status != SCSI_NV_FOUND) {
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: %s protocol "
+ "name %s", __func__,
+ (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" :
+ "invalid", tmpstr);
+ }
+ retval = 1;
+ goto bailout;
+ }
+ switch (scsi_proto_map[table_entry].value) {
+ case SCSI_PROTO_FC:
+ case SCSI_PROTO_1394:
+ case SCSI_PROTO_SAS:
+ retval = scsi_parse_transportid_64bit(
+ scsi_proto_map[table_entry].value, transportid_str, hdr,
+ alloc_len,
+#ifdef _KERNEL
+ type, flags,
+#endif
+ error_str, error_str_len);
+ break;
+ case SCSI_PROTO_SPI:
+ retval = scsi_parse_transportid_spi(transportid_str, hdr,
+ alloc_len,
+#ifdef _KERNEL
+ type, flags,
+#endif
+ error_str, error_str_len);
+ break;
+ case SCSI_PROTO_RDMA:
+ retval = scsi_parse_transportid_rdma(transportid_str, hdr,
+ alloc_len,
+#ifdef _KERNEL
+ type, flags,
+#endif
+ error_str, error_str_len);
+ break;
+ case SCSI_PROTO_ISCSI:
+ retval = scsi_parse_transportid_iscsi(transportid_str, hdr,
+ alloc_len,
+#ifdef _KERNEL
+ type, flags,
+#endif
+ error_str, error_str_len);
+ break;
+ case SCSI_PROTO_SOP:
+ retval = scsi_parse_transportid_sop(transportid_str, hdr,
+ alloc_len,
+#ifdef _KERNEL
+ type, flags,
+#endif
+ error_str, error_str_len);
+ break;
+ case SCSI_PROTO_SSA:
+ case SCSI_PROTO_ADITP:
+ case SCSI_PROTO_ATA:
+ case SCSI_PROTO_UAS:
+ case SCSI_PROTO_NONE:
+ default:
+ /*
+ * There is no format defined for a Transport ID for these
+ * protocols. So even if the user gives us something, we
+ * have no way to turn it into a standard SCSI Transport ID.
+ */
+ retval = 1;
+ if (error_str != NULL) {
+ snprintf(error_str, error_str_len, "%s: no Transport "
+ "ID format exists for protocol %s",
+ __func__, tmpstr);
+ }
+ goto bailout;
+ break; /* NOTREACHED */
+ }
+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,
+ nitems(scsi_mam_attr_table), 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 = nitems(scsi_mam_attr_table);
+ } else {
+ table1 = scsi_mam_attr_table;
+ table1_size = nitems(scsi_mam_attr_table);
+ table2 = user_table;
+ table2_size = num_user_entries;
+ }
+ } else {
+ table1 = scsi_mam_attr_table;
+ table1_size = nitems(scsi_mam_attr_table);
+ }
+
+ 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 *),
@@ -5500,7 +7514,6 @@ scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries,
scsi_cmd->opcode = TEST_UNIT_READY;
}
-#ifndef __rtems__
void
scsi_request_sense(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
@@ -5525,7 +7538,6 @@ scsi_request_sense(struct ccb_scsiio *csio, u_int32_t retries,
scsi_cmd->opcode = REQUEST_SENSE;
scsi_cmd->length = dxfer_len;
}
-#endif /* __rtems__ */
void
scsi_inquiry(struct ccb_scsiio *csio, u_int32_t retries,
@@ -5557,7 +7569,6 @@ scsi_inquiry(struct ccb_scsiio *csio, u_int32_t retries,
scsi_ulto2b(inq_len, scsi_cmd->length);
}
-#ifndef __rtems__
void
scsi_mode_sense(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
@@ -5790,7 +7801,6 @@ scsi_prevent(struct ccb_scsiio *csio, u_int32_t retries,
scsi_cmd->opcode = PREVENT_ALLOW;
scsi_cmd->how = action;
}
-#endif /* __rtems__ */
/* XXX allow specification of address and PMI bit and LBA */
void
@@ -5823,8 +7833,8 @@ void
scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
uint8_t tag_action, uint64_t lba, int reladr, int pmi,
- struct scsi_read_capacity_data_long *rcap_buf,
- uint8_t sense_len, uint32_t timeout)
+ uint8_t *rcap_buf, int rcap_buf_len, uint8_t sense_len,
+ uint32_t timeout)
{
struct scsi_read_capacity_16 *scsi_cmd;
@@ -5835,7 +7845,7 @@ scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
/*flags*/CAM_DIR_IN,
tag_action,
/*data_ptr*/(u_int8_t *)rcap_buf,
- /*dxfer_len*/sizeof(*rcap_buf),
+ /*dxfer_len*/rcap_buf_len,
sense_len,
sizeof(*scsi_cmd),
timeout);
@@ -5844,7 +7854,7 @@ scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
scsi_cmd->opcode = SERVICE_ACTION_IN;
scsi_cmd->service_action = SRC16_SERVICE_ACTION;
scsi_u64to8b(lba, scsi_cmd->addr);
- scsi_ulto4b(sizeof(*rcap_buf), scsi_cmd->alloc_len);
+ scsi_ulto4b(rcap_buf_len, scsi_cmd->alloc_len);
if (pmi)
reladr |= SRC16_PMI;
if (reladr)
@@ -6152,23 +8162,30 @@ scsi_ata_identify(struct ccb_scsiio *csio, u_int32_t retries,
u_int16_t dxfer_len, u_int8_t sense_len,
u_int32_t timeout)
{
- scsi_ata_pass_16(csio,
- retries,
- cbfcnp,
- /*flags*/CAM_DIR_IN,
- tag_action,
- /*protocol*/AP_PROTO_PIO_IN,
- /*ata_flags*/AP_FLAG_TDIR_FROM_DEV|
- AP_FLAG_BYT_BLOK_BYTES|AP_FLAG_TLEN_SECT_CNT,
- /*features*/0,
- /*sector_count*/dxfer_len,
- /*lba*/0,
- /*command*/ATA_ATA_IDENTIFY,
- /*control*/0,
- data_ptr,
- dxfer_len,
- sense_len,
- timeout);
+ scsi_ata_pass(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_IN,
+ tag_action,
+ /*protocol*/AP_PROTO_PIO_IN,
+ /*ata_flags*/AP_FLAG_TDIR_FROM_DEV |
+ AP_FLAG_BYT_BLOK_BYTES |
+ AP_FLAG_TLEN_SECT_CNT,
+ /*features*/0,
+ /*sector_count*/dxfer_len,
+ /*lba*/0,
+ /*command*/ATA_ATA_IDENTIFY,
+ /*device*/ 0,
+ /*icc*/ 0,
+ /*auxiliary*/ 0,
+ /*control*/0,
+ data_ptr,
+ dxfer_len,
+ /*cdb_storage*/ NULL,
+ /*cdb_storage_len*/ 0,
+ /*minimum_cmd_size*/ 0,
+ sense_len,
+ timeout);
}
void
@@ -6196,6 +8213,248 @@ scsi_ata_trim(struct ccb_scsiio *csio, u_int32_t retries,
timeout);
}
+int
+scsi_ata_read_log(struct ccb_scsiio *csio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ uint8_t tag_action, uint32_t log_address,
+ uint32_t page_number, uint16_t block_count,
+ uint8_t protocol, uint8_t *data_ptr, uint32_t dxfer_len,
+ uint8_t sense_len, uint32_t timeout)
+{
+ uint8_t command, protocol_out;
+ uint16_t count_out;
+ uint64_t lba;
+ int retval;
+
+ retval = 0;
+
+ switch (protocol) {
+ case AP_PROTO_DMA:
+ count_out = block_count;
+ command = ATA_READ_LOG_DMA_EXT;
+ protocol_out = AP_PROTO_DMA;
+ break;
+ case AP_PROTO_PIO_IN:
+ default:
+ count_out = block_count;
+ command = ATA_READ_LOG_EXT;
+ protocol_out = AP_PROTO_PIO_IN;
+ break;
+ }
+
+ lba = (((uint64_t)page_number & 0xff00) << 32) |
+ ((page_number & 0x00ff) << 8) |
+ (log_address & 0xff);
+
+ protocol_out |= AP_EXTEND;
+
+ retval = scsi_ata_pass(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_IN,
+ tag_action,
+ /*protocol*/ protocol_out,
+ /*ata_flags*/AP_FLAG_TLEN_SECT_CNT |
+ AP_FLAG_BYT_BLOK_BLOCKS |
+ AP_FLAG_TDIR_FROM_DEV,
+ /*feature*/ 0,
+ /*sector_count*/ count_out,
+ /*lba*/ lba,
+ /*command*/ command,
+ /*device*/ 0,
+ /*icc*/ 0,
+ /*auxiliary*/ 0,
+ /*control*/0,
+ data_ptr,
+ dxfer_len,
+ /*cdb_storage*/ NULL,
+ /*cdb_storage_len*/ 0,
+ /*minimum_cmd_size*/ 0,
+ sense_len,
+ timeout);
+
+ return (retval);
+}
+
+/*
+ * Note! This is an unusual CDB building function because it can return
+ * an error in the event that the command in question requires a variable
+ * length CDB, but the caller has not given storage space for one or has not
+ * given enough storage space. If there is enough space available in the
+ * standard SCSI CCB CDB bytes, we'll prefer that over passed in storage.
+ */
+int
+scsi_ata_pass(struct ccb_scsiio *csio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ uint32_t flags, uint8_t tag_action,
+ uint8_t protocol, uint8_t ata_flags, uint16_t features,
+ uint16_t sector_count, uint64_t lba, uint8_t command,
+ uint8_t device, uint8_t icc, uint32_t auxiliary,
+ uint8_t control, u_int8_t *data_ptr, uint32_t dxfer_len,
+ uint8_t *cdb_storage, size_t cdb_storage_len,
+ int minimum_cmd_size, u_int8_t sense_len, u_int32_t timeout)
+{
+ uint32_t cam_flags;
+ uint8_t *cdb_ptr;
+ int cmd_size;
+ int retval;
+ uint8_t cdb_len;
+
+ retval = 0;
+ cam_flags = flags;
+
+ /*
+ * Round the user's request to the nearest command size that is at
+ * least as big as what he requested.
+ */
+ if (minimum_cmd_size <= 12)
+ cmd_size = 12;
+ else if (minimum_cmd_size > 16)
+ cmd_size = 32;
+ else
+ cmd_size = 16;
+
+ /*
+ * If we have parameters that require a 48-bit ATA command, we have to
+ * use the 16 byte ATA PASS-THROUGH command at least.
+ */
+ if (((lba > ATA_MAX_28BIT_LBA)
+ || (sector_count > 255)
+ || (features > 255)
+ || (protocol & AP_EXTEND))
+ && ((cmd_size < 16)
+ || ((protocol & AP_EXTEND) == 0))) {
+ if (cmd_size < 16)
+ cmd_size = 16;
+ protocol |= AP_EXTEND;
+ }
+
+ /*
+ * The icc and auxiliary ATA registers are only supported in the
+ * 32-byte version of the ATA PASS-THROUGH command.
+ */
+ if ((icc != 0)
+ || (auxiliary != 0)) {
+ cmd_size = 32;
+ protocol |= AP_EXTEND;
+ }
+
+
+ if ((cmd_size > sizeof(csio->cdb_io.cdb_bytes))
+ && ((cdb_storage == NULL)
+ || (cdb_storage_len < cmd_size))) {
+ retval = 1;
+ goto bailout;
+ }
+
+ /*
+ * At this point we know we have enough space to store the command
+ * in one place or another. We prefer the built-in array, but used
+ * the passed in storage if necessary.
+ */
+ if (cmd_size <= sizeof(csio->cdb_io.cdb_bytes))
+ cdb_ptr = csio->cdb_io.cdb_bytes;
+ else {
+ cdb_ptr = cdb_storage;
+ cam_flags |= CAM_CDB_POINTER;
+ }
+
+ if (cmd_size <= 12) {
+ struct ata_pass_12 *cdb;
+
+ cdb = (struct ata_pass_12 *)cdb_ptr;
+ cdb_len = sizeof(*cdb);
+ bzero(cdb, cdb_len);
+
+ cdb->opcode = ATA_PASS_12;
+ cdb->protocol = protocol;
+ cdb->flags = ata_flags;
+ cdb->features = features;
+ cdb->sector_count = sector_count;
+ cdb->lba_low = lba & 0xff;
+ cdb->lba_mid = (lba >> 8) & 0xff;
+ cdb->lba_high = (lba >> 16) & 0xff;
+ cdb->device = ((lba >> 24) & 0xf) | ATA_DEV_LBA;
+ cdb->command = command;
+ cdb->control = control;
+ } else if (cmd_size <= 16) {
+ struct ata_pass_16 *cdb;
+
+ cdb = (struct ata_pass_16 *)cdb_ptr;
+ cdb_len = sizeof(*cdb);
+ bzero(cdb, cdb_len);
+
+ cdb->opcode = ATA_PASS_16;
+ cdb->protocol = protocol;
+ cdb->flags = ata_flags;
+ cdb->features = features & 0xff;
+ cdb->sector_count = sector_count & 0xff;
+ cdb->lba_low = lba & 0xff;
+ cdb->lba_mid = (lba >> 8) & 0xff;
+ cdb->lba_high = (lba >> 16) & 0xff;
+ /*
+ * If AP_EXTEND is set, we're sending a 48-bit command.
+ * Otherwise it's a 28-bit command.
+ */
+ if (protocol & AP_EXTEND) {
+ cdb->lba_low_ext = (lba >> 24) & 0xff;
+ cdb->lba_mid_ext = (lba >> 32) & 0xff;
+ cdb->lba_high_ext = (lba >> 40) & 0xff;
+ cdb->features_ext = (features >> 8) & 0xff;
+ cdb->sector_count_ext = (sector_count >> 8) & 0xff;
+ cdb->device = device | ATA_DEV_LBA;
+ } else {
+ cdb->lba_low_ext = (lba >> 24) & 0xf;
+ cdb->device = ((lba >> 24) & 0xf) | ATA_DEV_LBA;
+ }
+ cdb->command = command;
+ cdb->control = control;
+ } else {
+ struct ata_pass_32 *cdb;
+ uint8_t tmp_lba[8];
+
+ cdb = (struct ata_pass_32 *)cdb_ptr;
+ cdb_len = sizeof(*cdb);
+ bzero(cdb, cdb_len);
+ cdb->opcode = VARIABLE_LEN_CDB;
+ cdb->control = control;
+ cdb->length = sizeof(*cdb) - __offsetof(struct ata_pass_32,
+ service_action);
+ scsi_ulto2b(ATA_PASS_32_SA, cdb->service_action);
+ cdb->protocol = protocol;
+ cdb->flags = ata_flags;
+
+ if ((protocol & AP_EXTEND) == 0) {
+ lba &= 0x0fffffff;
+ cdb->device = ((lba >> 24) & 0xf) | ATA_DEV_LBA;
+ features &= 0xff;
+ sector_count &= 0xff;
+ } else {
+ cdb->device = device | ATA_DEV_LBA;
+ }
+ scsi_u64to8b(lba, tmp_lba);
+ bcopy(&tmp_lba[2], cdb->lba, sizeof(cdb->lba));
+ scsi_ulto2b(features, cdb->features);
+ scsi_ulto2b(sector_count, cdb->count);
+ cdb->command = command;
+ cdb->icc = icc;
+ scsi_ulto4b(auxiliary, cdb->auxiliary);
+ }
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ cam_flags,
+ tag_action,
+ data_ptr,
+ dxfer_len,
+ sense_len,
+ cmd_size,
+ timeout);
+bailout:
+ return (retval);
+}
+
void
scsi_ata_pass_16(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),
@@ -6434,6 +8693,229 @@ 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,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ uint8_t tag_action, int service_action,
+ uint8_t *data_ptr, uint32_t dxfer_len, int sense_len,
+ int timeout)
+{
+ struct scsi_per_res_in *scsi_cmd;
+
+ scsi_cmd = (struct scsi_per_res_in *)&csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = PERSISTENT_RES_IN;
+ scsi_cmd->action = service_action;
+ scsi_ulto2b(dxfer_len, scsi_cmd->length);
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_IN,
+ tag_action,
+ data_ptr,
+ dxfer_len,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
+void
+scsi_persistent_reserve_out(struct ccb_scsiio *csio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ uint8_t tag_action, int service_action,
+ int scope, int res_type, uint8_t *data_ptr,
+ uint32_t dxfer_len, int sense_len, int timeout)
+{
+ struct scsi_per_res_out *scsi_cmd;
+
+ scsi_cmd = (struct scsi_per_res_out *)&csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = PERSISTENT_RES_OUT;
+ scsi_cmd->action = service_action;
+ scsi_cmd->scope_type = scope | res_type;
+ scsi_ulto4b(dxfer_len, scsi_cmd->length);
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_OUT,
+ tag_action,
+ /*data_ptr*/data_ptr,
+ /*dxfer_len*/dxfer_len,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
+void
+scsi_security_protocol_in(struct ccb_scsiio *csio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ uint8_t tag_action, uint32_t security_protocol,
+ uint32_t security_protocol_specific, int byte4,
+ uint8_t *data_ptr, uint32_t dxfer_len, int sense_len,
+ int timeout)
+{
+ struct scsi_security_protocol_in *scsi_cmd;
+
+ scsi_cmd = (struct scsi_security_protocol_in *)&csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = SECURITY_PROTOCOL_IN;
+
+ scsi_cmd->security_protocol = security_protocol;
+ scsi_ulto2b(security_protocol_specific,
+ scsi_cmd->security_protocol_specific);
+ scsi_cmd->byte4 = byte4;
+ scsi_ulto4b(dxfer_len, scsi_cmd->length);
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_IN,
+ tag_action,
+ data_ptr,
+ dxfer_len,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
+void
+scsi_security_protocol_out(struct ccb_scsiio *csio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ uint8_t tag_action, uint32_t security_protocol,
+ uint32_t security_protocol_specific, int byte4,
+ uint8_t *data_ptr, uint32_t dxfer_len, int sense_len,
+ int timeout)
+{
+ struct scsi_security_protocol_out *scsi_cmd;
+
+ scsi_cmd = (struct scsi_security_protocol_out *)&csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = SECURITY_PROTOCOL_OUT;
+
+ scsi_cmd->security_protocol = security_protocol;
+ scsi_ulto2b(security_protocol_specific,
+ scsi_cmd->security_protocol_specific);
+ scsi_cmd->byte4 = byte4;
+ scsi_ulto4b(dxfer_len, scsi_cmd->length);
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_OUT,
+ tag_action,
+ data_ptr,
+ dxfer_len,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
+void
+scsi_report_supported_opcodes(struct ccb_scsiio *csio, uint32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ uint8_t tag_action, int options, int req_opcode,
+ int req_service_action, uint8_t *data_ptr,
+ uint32_t dxfer_len, int sense_len, int timeout)
+{
+ struct scsi_report_supported_opcodes *scsi_cmd;
+
+ scsi_cmd = (struct scsi_report_supported_opcodes *)
+ &csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = MAINTENANCE_IN;
+ scsi_cmd->service_action = REPORT_SUPPORTED_OPERATION_CODES;
+ scsi_cmd->options = options;
+ scsi_cmd->requested_opcode = req_opcode;
+ scsi_ulto2b(req_service_action, scsi_cmd->requested_service_action);
+ scsi_ulto4b(dxfer_len, scsi_cmd->length);
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/CAM_DIR_IN,
+ tag_action,
+ data_ptr,
+ dxfer_len,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
/*
* Try make as good a match as possible with
@@ -6500,7 +8982,7 @@ scsi_static_inquiry_match(caddr_t inqbuffer, caddr_t table_entry)
* \return 0 on a match, -1 otherwise.
*
* Treat rhs and lhs as arrays of vpd device id descriptors. Walk lhs matching
- * agains each element in rhs until all data are exhausted or we have found
+ * against each element in rhs until all data are exhausted or we have found
* a match.
*/
int