summaryrefslogtreecommitdiffstats
path: root/sys/cam/scsi/scsi_all.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cam/scsi/scsi_all.c')
-rw-r--r--sys/cam/scsi/scsi_all.c1667
1 files changed, 1519 insertions, 148 deletions
diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c
index 7361c42..5ec287b 100644
--- a/sys/cam/scsi/scsi_all.c
+++ b/sys/cam/scsi/scsi_all.c
@@ -31,6 +31,8 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stdint.h>
#ifdef _KERNEL
#include <opt_scsi.h>
@@ -54,6 +56,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sbuf.h>
#ifndef _KERNEL
#include <camlib.h>
+#include <stddef.h>
#ifndef FALSE
#define FALSE 0
@@ -608,14 +611,24 @@ scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data)
struct op_table_entry *table[2];
int num_tables;
- pd_type = SID_TYPE(inq_data);
+ /*
+ * If we've got inquiry data, use it to determine what type of
+ * device we're dealing with here. Otherwise, assume direct
+ * access.
+ */
+ if (inq_data == NULL) {
+ pd_type = T_DIRECT;
+ match = NULL;
+ } else {
+ pd_type = SID_TYPE(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),
- sizeof(*scsi_op_quirk_table),
- scsi_inquiry_match);
+ match = cam_quirkmatch((caddr_t)inq_data,
+ (caddr_t)scsi_op_quirk_table,
+ sizeof(scsi_op_quirk_table)/
+ sizeof(*scsi_op_quirk_table),
+ sizeof(*scsi_op_quirk_table),
+ scsi_inquiry_match);
+ }
if (match != NULL) {
table[0] = ((struct scsi_op_quirk_entry *)match)->op_table;
@@ -699,7 +712,7 @@ const struct sense_key_table_entry sense_key_table[] =
{ SSD_KEY_EQUAL, SS_NOP, "EQUAL" },
{ SSD_KEY_VOLUME_OVERFLOW, SS_FATAL|EIO, "VOLUME OVERFLOW" },
{ SSD_KEY_MISCOMPARE, SS_NOP, "MISCOMPARE" },
- { SSD_KEY_RESERVED, SS_FATAL|EIO, "RESERVED" }
+ { SSD_KEY_COMPLETED, SS_NOP, "COMPLETED" }
};
const int sense_key_table_size =
@@ -1062,25 +1075,25 @@ static struct asc_table_entry asc_table[] = {
{ SST(0x10, 0x03, SS_RDEF, /* XXX TBD */
"Logical block reference tag check failed") },
/* DT WRO BK */
- { SST(0x11, 0x00, SS_RDEF,
+ { SST(0x11, 0x00, SS_FATAL|EIO,
"Unrecovered read error") },
/* DT WRO BK */
- { SST(0x11, 0x01, SS_RDEF,
+ { SST(0x11, 0x01, SS_FATAL|EIO,
"Read retries exhausted") },
/* DT WRO BK */
- { SST(0x11, 0x02, SS_RDEF,
+ { SST(0x11, 0x02, SS_FATAL|EIO,
"Error too long to correct") },
/* DT W O BK */
- { SST(0x11, 0x03, SS_RDEF,
+ { SST(0x11, 0x03, SS_FATAL|EIO,
"Multiple read errors") },
/* D W O BK */
- { SST(0x11, 0x04, SS_RDEF,
+ { SST(0x11, 0x04, SS_FATAL|EIO,
"Unrecovered read error - auto reallocate failed") },
/* WRO B */
- { SST(0x11, 0x05, SS_RDEF,
+ { SST(0x11, 0x05, SS_FATAL|EIO,
"L-EC uncorrectable error") },
/* WRO B */
- { SST(0x11, 0x06, SS_RDEF,
+ { SST(0x11, 0x06, SS_FATAL|EIO,
"CIRC unrecovered error") },
/* W O B */
{ SST(0x11, 0x07, SS_RDEF,
@@ -1095,10 +1108,10 @@ static struct asc_table_entry asc_table[] = {
{ SST(0x11, 0x0A, SS_RDEF,
"Miscorrected error") },
/* D W O BK */
- { SST(0x11, 0x0B, SS_RDEF,
+ { SST(0x11, 0x0B, SS_FATAL|EIO,
"Unrecovered read error - recommend reassignment") },
/* D W O BK */
- { SST(0x11, 0x0C, SS_RDEF,
+ { SST(0x11, 0x0C, SS_FATAL|EIO,
"Unrecovered read error - recommend rewrite the data") },
/* DT WRO B */
{ SST(0x11, 0x0D, SS_RDEF,
@@ -2790,7 +2803,10 @@ scsi_sense_desc(int sense_key, int asc, int ascq,
&sense_entry,
&asc_entry);
- *sense_key_desc = sense_entry->desc;
+ if (sense_entry != NULL)
+ *sense_key_desc = sense_entry->desc;
+ else
+ *sense_key_desc = "Invalid Sense Key";
if (asc_entry != NULL)
*asc_desc = asc_entry->desc;
@@ -2816,10 +2832,12 @@ scsi_error_action(struct ccb_scsiio *csio, struct scsi_inquiry_data *inq_data,
int error_code, sense_key, asc, ascq;
scsi_sense_action action;
- scsi_extract_sense(&csio->sense_data, &error_code,
- &sense_key, &asc, &ascq);
+ scsi_extract_sense_len(&csio->sense_data, csio->sense_len -
+ csio->sense_resid, &error_code,
+ &sense_key, &asc, &ascq, /*show_errors*/ 1);
- if (error_code == SSD_DEFERRED_ERROR) {
+ if ((error_code == SSD_DEFERRED_ERROR)
+ || (error_code == SSD_DESC_DEFERRED_ERROR)) {
/*
* XXX dufault@FreeBSD.org
* This error doesn't relate to the command associated
@@ -2857,8 +2875,10 @@ scsi_error_action(struct ccb_scsiio *csio, struct scsi_inquiry_data *inq_data,
if (asc_entry != NULL
&& (asc != 0 || ascq != 0))
action = asc_entry->action;
- else
+ else if (sense_entry != NULL)
action = sense_entry->action;
+ else
+ action = SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE;
if (sense_key == SSD_KEY_RECOVERED_ERROR) {
/*
@@ -3040,6 +3060,1346 @@ scsi_command_string(struct cam_device *device, struct ccb_scsiio *csio,
return(0);
}
+/*
+ * Iterate over sense descriptors. Each descriptor is passed into iter_func().
+ * If iter_func() returns 0, list traversal continues. If iter_func()
+ * returns non-zero, list traversal is stopped.
+ */
+void
+scsi_desc_iterate(struct scsi_sense_data_desc *sense, u_int sense_len,
+ int (*iter_func)(struct scsi_sense_data_desc *sense,
+ u_int, struct scsi_sense_desc_header *,
+ void *), void *arg)
+{
+ int cur_pos;
+ int desc_len;
+
+ /*
+ * First make sure the extra length field is present.
+ */
+ if (SSD_DESC_IS_PRESENT(sense, sense_len, extra_len) == 0)
+ return;
+
+ /*
+ * The length of data actually returned may be different than the
+ * extra_len recorded in the sturcture.
+ */
+ desc_len = sense_len -offsetof(struct scsi_sense_data_desc, sense_desc);
+
+ /*
+ * Limit this further by the extra length reported, and the maximum
+ * allowed extra length.
+ */
+ desc_len = MIN(desc_len, MIN(sense->extra_len, SSD_EXTRA_MAX));
+
+ /*
+ * Subtract the size of the header from the descriptor length.
+ * This is to ensure that we have at least the header left, so we
+ * don't have to check that inside the loop. This can wind up
+ * being a negative value.
+ */
+ desc_len -= sizeof(struct scsi_sense_desc_header);
+
+ for (cur_pos = 0; cur_pos < desc_len;) {
+ struct scsi_sense_desc_header *header;
+
+ header = (struct scsi_sense_desc_header *)
+ &sense->sense_desc[cur_pos];
+
+ /*
+ * Check to make sure we have the entire descriptor. We
+ * don't call iter_func() unless we do.
+ *
+ * Note that although cur_pos is at the beginning of the
+ * descriptor, desc_len already has the header length
+ * subtracted. So the comparison of the length in the
+ * header (which does not include the header itself) to
+ * desc_len - cur_pos is correct.
+ */
+ if (header->length > (desc_len - cur_pos))
+ break;
+
+ if (iter_func(sense, sense_len, header, arg) != 0)
+ break;
+
+ cur_pos += sizeof(*header) + header->length;
+ }
+}
+
+struct scsi_find_desc_info {
+ uint8_t desc_type;
+ struct scsi_sense_desc_header *header;
+};
+
+static int
+scsi_find_desc_func(struct scsi_sense_data_desc *sense, u_int sense_len,
+ struct scsi_sense_desc_header *header, void *arg)
+{
+ struct scsi_find_desc_info *desc_info;
+
+ desc_info = (struct scsi_find_desc_info *)arg;
+
+ if (header->desc_type == desc_info->desc_type) {
+ desc_info->header = header;
+
+ /* We found the descriptor, tell the iterator to stop. */
+ return (1);
+ } else
+ return (0);
+}
+
+/*
+ * Given a descriptor type, return a pointer to it if it is in the sense
+ * data and not truncated. Avoiding truncating sense data will simplify
+ * things significantly for the caller.
+ */
+uint8_t *
+scsi_find_desc(struct scsi_sense_data_desc *sense, u_int sense_len,
+ uint8_t desc_type)
+{
+ struct scsi_find_desc_info desc_info;
+
+ desc_info.desc_type = desc_type;
+ desc_info.header = NULL;
+
+ scsi_desc_iterate(sense, sense_len, scsi_find_desc_func, &desc_info);
+
+ return ((uint8_t *)desc_info.header);
+}
+
+/*
+ * Fill in SCSI sense data with the specified parameters. This routine can
+ * fill in either fixed or descriptor type sense data.
+ */
+void
+scsi_set_sense_data_va(struct scsi_sense_data *sense_data,
+ scsi_sense_data_type sense_format, int current_error,
+ int sense_key, int asc, int ascq, va_list ap)
+{
+ int descriptor_sense;
+ scsi_sense_elem_type elem_type;
+
+ /*
+ * Determine whether to return fixed or descriptor format sense
+ * data. If the user specifies SSD_TYPE_NONE for some reason,
+ * they'll just get fixed sense data.
+ */
+ if (sense_format == SSD_TYPE_DESC)
+ descriptor_sense = 1;
+ else
+ descriptor_sense = 0;
+
+ /*
+ * Zero the sense data, so that we don't pass back any garbage data
+ * to the user.
+ */
+ memset(sense_data, 0, sizeof(*sense_data));
+
+ if (descriptor_sense != 0) {
+ struct scsi_sense_data_desc *sense;
+
+ sense = (struct scsi_sense_data_desc *)sense_data;
+ /*
+ * The descriptor sense format eliminates the use of the
+ * valid bit.
+ */
+ if (current_error != 0)
+ sense->error_code = SSD_DESC_CURRENT_ERROR;
+ else
+ sense->error_code = SSD_DESC_DEFERRED_ERROR;
+ sense->sense_key = sense_key;
+ sense->add_sense_code = asc;
+ sense->add_sense_code_qual = ascq;
+ /*
+ * Start off with no extra length, since the above data
+ * fits in the standard descriptor sense information.
+ */
+ sense->extra_len = 0;
+ while ((elem_type = (scsi_sense_elem_type)va_arg(ap,
+ scsi_sense_elem_type)) != SSD_ELEM_NONE) {
+ int sense_len, len_to_copy;
+ uint8_t *data;
+
+ if (elem_type >= SSD_ELEM_MAX) {
+ printf("%s: invalid sense type %d\n", __func__,
+ elem_type);
+ break;
+ }
+
+ 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 *);
+
+ /*
+ * We've already consumed the arguments for this one.
+ */
+ if (elem_type == SSD_ELEM_SKIP)
+ continue;
+
+ switch (elem_type) {
+ case SSD_ELEM_DESC: {
+
+ /*
+ * This is a straight descriptor. All we
+ * need to do is copy the data in.
+ */
+ bcopy(data, &sense->sense_desc[
+ sense->extra_len], len_to_copy);
+ sense->extra_len += len_to_copy;
+ break;
+ }
+ case SSD_ELEM_SKS: {
+ struct scsi_sense_sks sks;
+
+ bzero(&sks, sizeof(sks));
+
+ /*
+ * This is already-formatted sense key
+ * specific data. We just need to fill out
+ * the header and copy everything in.
+ */
+ bcopy(data, &sks.sense_key_spec,
+ MIN(len_to_copy,
+ sizeof(sks.sense_key_spec)));
+
+ sks.desc_type = SSD_DESC_SKS;
+ sks.length = sizeof(sks) -
+ offsetof(struct scsi_sense_sks, reserved1);
+ bcopy(&sks,&sense->sense_desc[sense->extra_len],
+ sizeof(sks));
+ sense->extra_len += sizeof(sks);
+ break;
+ }
+ case SSD_ELEM_INFO:
+ case SSD_ELEM_COMMAND: {
+ struct scsi_sense_command cmd;
+ struct scsi_sense_info info;
+ uint8_t *data_dest;
+ uint8_t *descriptor;
+ int descriptor_size, i, copy_len;
+
+ bzero(&cmd, sizeof(cmd));
+ bzero(&info, sizeof(info));
+
+ /*
+ * Command or information data. The
+ * operate in pretty much the same way.
+ */
+ if (elem_type == SSD_ELEM_COMMAND) {
+ len_to_copy = MIN(len_to_copy,
+ sizeof(cmd.command_info));
+ descriptor = (uint8_t *)&cmd;
+ descriptor_size = sizeof(cmd);
+ data_dest =(uint8_t *)&cmd.command_info;
+ cmd.desc_type = SSD_DESC_COMMAND;
+ cmd.length = sizeof(cmd) -
+ offsetof(struct scsi_sense_command,
+ reserved);
+ } else {
+ len_to_copy = MIN(len_to_copy,
+ sizeof(info.info));
+ descriptor = (uint8_t *)&info;
+ descriptor_size = sizeof(cmd);
+ data_dest = (uint8_t *)&info.info;
+ info.desc_type = SSD_DESC_INFO;
+ info.byte2 = SSD_INFO_VALID;
+ info.length = sizeof(info) -
+ offsetof(struct scsi_sense_info,
+ byte2);
+ }
+
+ /*
+ * Copy this in reverse because the spec
+ * (SPC-4) says that when 4 byte quantities
+ * are stored in this 8 byte field, the
+ * first four bytes shall be 0.
+ *
+ * So we fill the bytes in from the end, and
+ * if we have less than 8 bytes to copy,
+ * the initial, most significant bytes will
+ * be 0.
+ */
+ for (i = sense_len - 1; i >= 0 &&
+ len_to_copy > 0; i--, len_to_copy--)
+ data_dest[len_to_copy - 1] = data[i];
+
+ /*
+ * This calculation looks much like the
+ * initial len_to_copy calculation, but
+ * we have to do it again here, because
+ * we're looking at a larger amount that
+ * may or may not fit. It's not only the
+ * data the user passed in, but also the
+ * rest of the descriptor.
+ */
+ copy_len = MIN(descriptor_size,
+ SSD_EXTRA_MAX - sense->extra_len);
+ bcopy(descriptor, &sense->sense_desc[
+ sense->extra_len], copy_len);
+ sense->extra_len += copy_len;
+ break;
+ }
+ case SSD_ELEM_FRU: {
+ struct scsi_sense_fru fru;
+ int copy_len;
+
+ bzero(&fru, sizeof(fru));
+
+ fru.desc_type = SSD_DESC_FRU;
+ fru.length = sizeof(fru) -
+ offsetof(struct scsi_sense_fru, reserved);
+ fru.fru = *data;
+
+ copy_len = MIN(sizeof(fru), SSD_EXTRA_MAX -
+ sense->extra_len);
+ bcopy(&fru, &sense->sense_desc[
+ sense->extra_len], copy_len);
+ sense->extra_len += copy_len;
+ break;
+ }
+ case SSD_ELEM_STREAM: {
+ struct scsi_sense_stream stream_sense;
+ int copy_len;
+
+ bzero(&stream_sense, sizeof(stream_sense));
+ stream_sense.desc_type = SSD_DESC_STREAM;
+ stream_sense.length = sizeof(stream_sense) -
+ offsetof(struct scsi_sense_stream, reserved);
+ stream_sense.byte3 = *data;
+
+ copy_len = MIN(sizeof(stream_sense),
+ SSD_EXTRA_MAX - sense->extra_len);
+ bcopy(&stream_sense, &sense->sense_desc[
+ sense->extra_len], copy_len);
+ sense->extra_len += copy_len;
+ break;
+ }
+ default:
+ /*
+ * We shouldn't get here, but if we do, do
+ * nothing. We've already consumed the
+ * arguments above.
+ */
+ break;
+ }
+ }
+ } else {
+ struct scsi_sense_data_fixed *sense;
+
+ sense = (struct scsi_sense_data_fixed *)sense_data;
+
+ if (current_error != 0)
+ sense->error_code = SSD_CURRENT_ERROR;
+ else
+ sense->error_code = SSD_DEFERRED_ERROR;
+
+ sense->flags = sense_key;
+ sense->add_sense_code = asc;
+ sense->add_sense_code_qual = ascq;
+ /*
+ * We've set the ASC and ASCQ, so we have 6 more bytes of
+ * valid data. If we wind up setting any of the other
+ * fields, we'll bump this to 10 extra bytes.
+ */
+ sense->extra_len = 6;
+
+ while ((elem_type = (scsi_sense_elem_type)va_arg(ap,
+ scsi_sense_elem_type)) != SSD_ELEM_NONE) {
+ int sense_len, len_to_copy;
+ uint8_t *data;
+
+ if (elem_type >= SSD_ELEM_MAX) {
+ printf("%s: invalid sense type %d\n", __func__,
+ elem_type);
+ break;
+ }
+ /*
+ * If we get in here, just bump the extra length to
+ * 10 bytes. That will encompass anything we're
+ * going to set here.
+ */
+ 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) {
+ case SSD_ELEM_SKS:
+ /*
+ * The user passed in pre-formatted sense
+ * key specific data.
+ */
+ bcopy(data, &sense->sense_key_spec[0],
+ MIN(sizeof(sense->sense_key_spec),
+ sense_len));
+ break;
+ case SSD_ELEM_INFO:
+ case SSD_ELEM_COMMAND: {
+ uint8_t *data_dest;
+ int i;
+
+ if (elem_type == SSD_ELEM_COMMAND)
+ data_dest = &sense->cmd_spec_info[0];
+ else {
+ data_dest = &sense->info[0];
+ /*
+ * We're setting the info field, so
+ * set the valid bit.
+ */
+ sense->error_code |= SSD_ERRCODE_VALID;
+ }
+
+ /*
+ * Copy this in reverse so that if we have
+ * less than 4 bytes to fill, the least
+ * significant bytes will be at the end.
+ * If we have more than 4 bytes, only the
+ * least significant bytes will be included.
+ */
+ for (i = sense_len - 1; i >= 0 &&
+ len_to_copy > 0; i--, len_to_copy--)
+ data_dest[len_to_copy - 1] = data[i];
+
+ break;
+ }
+ case SSD_ELEM_FRU:
+ sense->fru = *data;
+ break;
+ case SSD_ELEM_STREAM:
+ sense->flags |= *data;
+ break;
+ case SSD_ELEM_DESC:
+ default:
+
+ /*
+ * If the user passes in descriptor sense,
+ * we can't handle that in fixed format.
+ * So just skip it, and any unknown argument
+ * types.
+ */
+ break;
+ }
+ }
+ }
+}
+
+void
+scsi_set_sense_data(struct scsi_sense_data *sense_data,
+ scsi_sense_data_type sense_format, int current_error,
+ int sense_key, int asc, int ascq, ...)
+{
+ va_list ap;
+
+ va_start(ap, ascq);
+ scsi_set_sense_data_va(sense_data, sense_format, current_error,
+ sense_key, asc, ascq, ap);
+ va_end(ap);
+}
+
+/*
+ * Get sense information for three similar sense data types.
+ */
+int
+scsi_get_sense_info(struct scsi_sense_data *sense_data, u_int sense_len,
+ uint8_t info_type, uint64_t *info, int64_t *signed_info)
+{
+ scsi_sense_data_type sense_type;
+
+ if (sense_len == 0)
+ goto bailout;
+
+ sense_type = scsi_sense_type(sense_data);
+
+ switch (sense_type) {
+ case SSD_TYPE_DESC: {
+ struct scsi_sense_data_desc *sense;
+ uint8_t *desc;
+
+ sense = (struct scsi_sense_data_desc *)sense_data;
+
+ desc = scsi_find_desc(sense, sense_len, info_type);
+ if (desc == NULL)
+ goto bailout;
+
+ switch (info_type) {
+ case SSD_DESC_INFO: {
+ struct scsi_sense_info *info_desc;
+
+ info_desc = (struct scsi_sense_info *)desc;
+ *info = scsi_8btou64(info_desc->info);
+ if (signed_info != NULL)
+ *signed_info = *info;
+ break;
+ }
+ case SSD_DESC_COMMAND: {
+ struct scsi_sense_command *cmd_desc;
+
+ cmd_desc = (struct scsi_sense_command *)desc;
+
+ *info = scsi_8btou64(cmd_desc->command_info);
+ if (signed_info != NULL)
+ *signed_info = *info;
+ break;
+ }
+ case SSD_DESC_FRU: {
+ struct scsi_sense_fru *fru_desc;
+
+ fru_desc = (struct scsi_sense_fru *)desc;
+
+ *info = fru_desc->fru;
+ if (signed_info != NULL)
+ *signed_info = (int8_t)fru_desc->fru;
+ break;
+ }
+ default:
+ goto bailout;
+ break;
+ }
+ break;
+ }
+ case SSD_TYPE_FIXED: {
+ struct scsi_sense_data_fixed *sense;
+
+ sense = (struct scsi_sense_data_fixed *)sense_data;
+
+ switch (info_type) {
+ case SSD_DESC_INFO: {
+ uint32_t info_val;
+
+ if ((sense->error_code & SSD_ERRCODE_VALID) == 0)
+ goto bailout;
+
+ if (SSD_FIXED_IS_PRESENT(sense, sense_len, info) == 0)
+ goto bailout;
+
+ info_val = scsi_4btoul(sense->info);
+
+ *info = info_val;
+ if (signed_info != NULL)
+ *signed_info = (int32_t)info_val;
+ break;
+ }
+ case SSD_DESC_COMMAND: {
+ uint32_t cmd_val;
+
+ if ((SSD_FIXED_IS_PRESENT(sense, sense_len,
+ cmd_spec_info) == 0)
+ || (SSD_FIXED_IS_FILLED(sense, cmd_spec_info) == 0))
+ goto bailout;
+
+ cmd_val = scsi_4btoul(sense->cmd_spec_info);
+ if (cmd_val == 0)
+ goto bailout;
+
+ *info = cmd_val;
+ if (signed_info != NULL)
+ *signed_info = (int32_t)cmd_val;
+ break;
+ }
+ case SSD_DESC_FRU:
+ if ((SSD_FIXED_IS_PRESENT(sense, sense_len, fru) == 0)
+ || (SSD_FIXED_IS_FILLED(sense, fru) == 0))
+ goto bailout;
+
+ if (sense->fru == 0)
+ goto bailout;
+
+ *info = sense->fru;
+ if (signed_info != NULL)
+ *signed_info = (int8_t)sense->fru;
+ break;
+ default:
+ goto bailout;
+ break;
+ }
+ break;
+ }
+ default:
+ goto bailout;
+ break;
+ }
+
+ return (0);
+bailout:
+ return (1);
+}
+
+int
+scsi_get_sks(struct scsi_sense_data *sense_data, u_int sense_len, uint8_t *sks)
+{
+ scsi_sense_data_type sense_type;
+
+ if (sense_len == 0)
+ goto bailout;
+
+ sense_type = scsi_sense_type(sense_data);
+
+ switch (sense_type) {
+ case SSD_TYPE_DESC: {
+ struct scsi_sense_data_desc *sense;
+ struct scsi_sense_sks *desc;
+
+ sense = (struct scsi_sense_data_desc *)sense_data;
+
+ desc = (struct scsi_sense_sks *)scsi_find_desc(sense, sense_len,
+ SSD_DESC_SKS);
+ if (desc == NULL)
+ goto bailout;
+
+ /*
+ * No need to check the SKS valid bit for descriptor sense.
+ * If the descriptor is present, it is valid.
+ */
+ bcopy(desc->sense_key_spec, sks, sizeof(desc->sense_key_spec));
+ break;
+ }
+ case SSD_TYPE_FIXED: {
+ struct scsi_sense_data_fixed *sense;
+
+ sense = (struct scsi_sense_data_fixed *)sense_data;
+
+ if ((SSD_FIXED_IS_PRESENT(sense, sense_len, sense_key_spec)== 0)
+ || (SSD_FIXED_IS_FILLED(sense, sense_key_spec) == 0))
+ goto bailout;
+
+ if ((sense->sense_key_spec[0] & SSD_SCS_VALID) == 0)
+ goto bailout;
+
+ bcopy(sense->sense_key_spec, sks,sizeof(sense->sense_key_spec));
+ break;
+ }
+ default:
+ goto bailout;
+ break;
+ }
+ return (0);
+bailout:
+ return (1);
+}
+
+/*
+ * Provide a common interface for fixed and descriptor sense to detect
+ * whether we have block-specific sense information. It is clear by the
+ * presence of the block descriptor in descriptor mode, but we have to
+ * infer from the inquiry data and ILI bit in fixed mode.
+ */
+int
+scsi_get_block_info(struct scsi_sense_data *sense_data, u_int sense_len,
+ struct scsi_inquiry_data *inq_data, uint8_t *block_bits)
+{
+ scsi_sense_data_type sense_type;
+
+ if (inq_data != NULL) {
+ switch (SID_TYPE(inq_data)) {
+ case T_DIRECT:
+ case T_RBC:
+ break;
+ default:
+ goto bailout;
+ break;
+ }
+ }
+
+ sense_type = scsi_sense_type(sense_data);
+
+ switch (sense_type) {
+ case SSD_TYPE_DESC: {
+ struct scsi_sense_data_desc *sense;
+ struct scsi_sense_block *block;
+
+ sense = (struct scsi_sense_data_desc *)sense_data;
+
+ block = (struct scsi_sense_block *)scsi_find_desc(sense,
+ sense_len, SSD_DESC_BLOCK);
+ if (block == NULL)
+ goto bailout;
+
+ *block_bits = block->byte3;
+ break;
+ }
+ case SSD_TYPE_FIXED: {
+ struct scsi_sense_data_fixed *sense;
+
+ sense = (struct scsi_sense_data_fixed *)sense_data;
+
+ if (SSD_FIXED_IS_PRESENT(sense, sense_len, flags) == 0)
+ goto bailout;
+
+ if ((sense->flags & SSD_ILI) == 0)
+ goto bailout;
+
+ *block_bits = sense->flags & SSD_ILI;
+ break;
+ }
+ default:
+ goto bailout;
+ break;
+ }
+ return (0);
+bailout:
+ return (1);
+}
+
+int
+scsi_get_stream_info(struct scsi_sense_data *sense_data, u_int sense_len,
+ struct scsi_inquiry_data *inq_data, uint8_t *stream_bits)
+{
+ scsi_sense_data_type sense_type;
+
+ if (inq_data != NULL) {
+ switch (SID_TYPE(inq_data)) {
+ case T_SEQUENTIAL:
+ break;
+ default:
+ goto bailout;
+ break;
+ }
+ }
+
+ sense_type = scsi_sense_type(sense_data);
+
+ switch (sense_type) {
+ case SSD_TYPE_DESC: {
+ struct scsi_sense_data_desc *sense;
+ struct scsi_sense_stream *stream;
+
+ sense = (struct scsi_sense_data_desc *)sense_data;
+
+ stream = (struct scsi_sense_stream *)scsi_find_desc(sense,
+ sense_len, SSD_DESC_STREAM);
+ if (stream == NULL)
+ goto bailout;
+
+ *stream_bits = stream->byte3;
+ break;
+ }
+ case SSD_TYPE_FIXED: {
+ struct scsi_sense_data_fixed *sense;
+
+ sense = (struct scsi_sense_data_fixed *)sense_data;
+
+ if (SSD_FIXED_IS_PRESENT(sense, sense_len, flags) == 0)
+ goto bailout;
+
+ if ((sense->flags & (SSD_ILI|SSD_EOM|SSD_FILEMARK)) == 0)
+ goto bailout;
+
+ *stream_bits = sense->flags & (SSD_ILI|SSD_EOM|SSD_FILEMARK);
+ break;
+ }
+ default:
+ goto bailout;
+ break;
+ }
+ return (0);
+bailout:
+ return (1);
+}
+
+void
+scsi_info_sbuf(struct sbuf *sb, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data, uint64_t info)
+{
+ sbuf_printf(sb, "Info: %#jx", info);
+}
+
+void
+scsi_command_sbuf(struct sbuf *sb, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data, uint64_t csi)
+{
+ sbuf_printf(sb, "Command Specific Info: %#jx", csi);
+}
+
+
+void
+scsi_progress_sbuf(struct sbuf *sb, uint16_t progress)
+{
+ sbuf_printf(sb, "Progress: %d%% (%d/%d) complete",
+ (progress * 100) / SSD_SKS_PROGRESS_DENOM,
+ progress, SSD_SKS_PROGRESS_DENOM);
+}
+
+/*
+ * Returns 1 for failure (i.e. SKS isn't valid) and 0 for success.
+ */
+int
+scsi_sks_sbuf(struct sbuf *sb, int sense_key, uint8_t *sks)
+{
+ if ((sks[0] & SSD_SKS_VALID) == 0)
+ return (1);
+
+ switch (sense_key) {
+ case SSD_KEY_ILLEGAL_REQUEST: {
+ struct scsi_sense_sks_field *field;
+ int bad_command;
+ char tmpstr[40];
+
+ /*Field Pointer*/
+ field = (struct scsi_sense_sks_field *)sks;
+
+ if (field->byte0 & SSD_SKS_FIELD_CMD)
+ bad_command = 1;
+ else
+ bad_command = 0;
+
+ tmpstr[0] = '\0';
+
+ /* Bit pointer is valid */
+ if (field->byte0 & SSD_SKS_BPV)
+ snprintf(tmpstr, sizeof(tmpstr), "bit %d ",
+ field->byte0 & SSD_SKS_BIT_VALUE);
+
+ sbuf_printf(sb, "%s byte %d %sis invalid",
+ bad_command ? "Command" : "Data",
+ scsi_2btoul(field->field), tmpstr);
+ break;
+ }
+ case SSD_KEY_UNIT_ATTENTION: {
+ struct scsi_sense_sks_overflow *overflow;
+
+ overflow = (struct scsi_sense_sks_overflow *)sks;
+
+ /*UA Condition Queue Overflow*/
+ sbuf_printf(sb, "Unit Attention Condition Queue %s",
+ (overflow->byte0 & SSD_SKS_OVERFLOW_SET) ?
+ "Overflowed" : "Did Not Overflow??");
+ break;
+ }
+ case SSD_KEY_RECOVERED_ERROR:
+ case SSD_KEY_HARDWARE_ERROR:
+ case SSD_KEY_MEDIUM_ERROR: {
+ struct scsi_sense_sks_retry *retry;
+
+ /*Actual Retry Count*/
+ retry = (struct scsi_sense_sks_retry *)sks;
+
+ sbuf_printf(sb, "Actual Retry Count: %d",
+ scsi_2btoul(retry->actual_retry_count));
+ break;
+ }
+ case SSD_KEY_NO_SENSE:
+ case SSD_KEY_NOT_READY: {
+ struct scsi_sense_sks_progress *progress;
+ int progress_val;
+
+ /*Progress Indication*/
+ progress = (struct scsi_sense_sks_progress *)sks;
+ progress_val = scsi_2btoul(progress->progress);
+
+ scsi_progress_sbuf(sb, progress_val);
+ break;
+ }
+ case SSD_KEY_COPY_ABORTED: {
+ struct scsi_sense_sks_segment *segment;
+ char tmpstr[40];
+
+ /*Segment Pointer*/
+ segment = (struct scsi_sense_sks_segment *)sks;
+
+ tmpstr[0] = '\0';
+
+ if (segment->byte0 & SSD_SKS_SEGMENT_BPV)
+ snprintf(tmpstr, sizeof(tmpstr), "bit %d ",
+ segment->byte0 & SSD_SKS_SEGMENT_BITPTR);
+
+ sbuf_printf(sb, "%s byte %d %sis invalid", (segment->byte0 &
+ SSD_SKS_SEGMENT_SD) ? "Segment" : "Data",
+ scsi_2btoul(segment->field), tmpstr);
+ break;
+ }
+ default:
+ sbuf_printf(sb, "Sense Key Specific: %#x,%#x", sks[0],
+ scsi_2btoul(&sks[1]));
+ break;
+ }
+
+ return (0);
+}
+
+void
+scsi_fru_sbuf(struct sbuf *sb, uint64_t fru)
+{
+ sbuf_printf(sb, "Field Replaceable Unit: %d", (int)fru);
+}
+
+void
+scsi_stream_sbuf(struct sbuf *sb, uint8_t stream_bits, uint64_t info)
+{
+ int need_comma;
+
+ need_comma = 0;
+ /*
+ * XXX KDM this needs more descriptive decoding.
+ */
+ if (stream_bits & SSD_DESC_STREAM_FM) {
+ sbuf_printf(sb, "Filemark");
+ need_comma = 1;
+ }
+
+ if (stream_bits & SSD_DESC_STREAM_EOM) {
+ sbuf_printf(sb, "%sEOM", (need_comma) ? "," : "");
+ need_comma = 1;
+ }
+
+ if (stream_bits & SSD_DESC_STREAM_ILI)
+ sbuf_printf(sb, "%sILI", (need_comma) ? "," : "");
+
+ sbuf_printf(sb, ": Info: %#jx", (uintmax_t) info);
+}
+
+void
+scsi_block_sbuf(struct sbuf *sb, uint8_t block_bits, uint64_t info)
+{
+ if (block_bits & SSD_DESC_BLOCK_ILI)
+ sbuf_printf(sb, "ILI: residue %#jx", (uintmax_t) info);
+}
+
+void
+scsi_sense_info_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+ u_int sense_len, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data,
+ struct scsi_sense_desc_header *header)
+{
+ struct scsi_sense_info *info;
+
+ info = (struct scsi_sense_info *)header;
+
+ scsi_info_sbuf(sb, cdb, cdb_len, inq_data, scsi_8btou64(info->info));
+}
+
+void
+scsi_sense_command_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+ u_int sense_len, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data,
+ struct scsi_sense_desc_header *header)
+{
+ struct scsi_sense_command *command;
+
+ command = (struct scsi_sense_command *)header;
+
+ scsi_command_sbuf(sb, cdb, cdb_len, inq_data,
+ scsi_8btou64(command->command_info));
+}
+
+void
+scsi_sense_sks_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+ u_int sense_len, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data,
+ struct scsi_sense_desc_header *header)
+{
+ struct scsi_sense_sks *sks;
+ int error_code, sense_key, asc, ascq;
+
+ sks = (struct scsi_sense_sks *)header;
+
+ scsi_extract_sense_len(sense, sense_len, &error_code, &sense_key,
+ &asc, &ascq, /*show_errors*/ 1);
+
+ scsi_sks_sbuf(sb, sense_key, sks->sense_key_spec);
+}
+
+void
+scsi_sense_fru_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+ u_int sense_len, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data,
+ struct scsi_sense_desc_header *header)
+{
+ struct scsi_sense_fru *fru;
+
+ fru = (struct scsi_sense_fru *)header;
+
+ scsi_fru_sbuf(sb, (uint64_t)fru->fru);
+}
+
+void
+scsi_sense_stream_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+ u_int sense_len, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data,
+ struct scsi_sense_desc_header *header)
+{
+ struct scsi_sense_stream *stream;
+ uint64_t info;
+
+ stream = (struct scsi_sense_stream *)header;
+ info = 0;
+
+ scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &info, NULL);
+
+ scsi_stream_sbuf(sb, stream->byte3, info);
+}
+
+void
+scsi_sense_block_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+ u_int sense_len, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data,
+ struct scsi_sense_desc_header *header)
+{
+ struct scsi_sense_block *block;
+ uint64_t info;
+
+ block = (struct scsi_sense_block *)header;
+ info = 0;
+
+ scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &info, NULL);
+
+ scsi_block_sbuf(sb, block->byte3, info);
+}
+
+void
+scsi_sense_progress_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+ u_int sense_len, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data,
+ struct scsi_sense_desc_header *header)
+{
+ struct scsi_sense_progress *progress;
+ const char *sense_key_desc;
+ const char *asc_desc;
+ int progress_val;
+
+ progress = (struct scsi_sense_progress *)header;
+
+ /*
+ * Get descriptions for the sense key, ASC, and ASCQ in the
+ * progress descriptor. These could be different than the values
+ * in the overall sense data.
+ */
+ scsi_sense_desc(progress->sense_key, progress->add_sense_code,
+ progress->add_sense_code_qual, inq_data,
+ &sense_key_desc, &asc_desc);
+
+ progress_val = scsi_2btoul(progress->progress);
+
+ /*
+ * The progress indicator is for the operation described by the
+ * sense key, ASC, and ASCQ in the descriptor.
+ */
+ sbuf_cat(sb, sense_key_desc);
+ sbuf_printf(sb, " asc:%x,%x (%s): ", progress->add_sense_code,
+ progress->add_sense_code_qual, asc_desc);
+ scsi_progress_sbuf(sb, progress_val);
+}
+
+/*
+ * Generic sense descriptor printing routine. This is used when we have
+ * not yet implemented a specific printing routine for this descriptor.
+ */
+void
+scsi_sense_generic_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+ u_int sense_len, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data,
+ struct scsi_sense_desc_header *header)
+{
+ int i;
+ uint8_t *buf_ptr;
+
+ sbuf_printf(sb, "Descriptor %#x:", header->desc_type);
+
+ buf_ptr = (uint8_t *)&header[1];
+
+ for (i = 0; i < header->length; i++, buf_ptr++)
+ sbuf_printf(sb, " %02x", *buf_ptr);
+}
+
+/*
+ * Keep this list in numeric order. This speeds the array traversal.
+ */
+struct scsi_sense_desc_printer {
+ uint8_t desc_type;
+ /*
+ * The function arguments here are the superset of what is needed
+ * to print out various different descriptors. Command and
+ * information descriptors need inquiry data and command type.
+ * Sense key specific descriptors need the sense key.
+ *
+ * The sense, cdb, and inquiry data arguments may be NULL, but the
+ * information printed may not be fully decoded as a result.
+ */
+ void (*print_func)(struct sbuf *sb, struct scsi_sense_data *sense,
+ u_int sense_len, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data,
+ struct scsi_sense_desc_header *header);
+} scsi_sense_printers[] = {
+ {SSD_DESC_INFO, scsi_sense_info_sbuf},
+ {SSD_DESC_COMMAND, scsi_sense_command_sbuf},
+ {SSD_DESC_SKS, scsi_sense_sks_sbuf},
+ {SSD_DESC_FRU, scsi_sense_fru_sbuf},
+ {SSD_DESC_STREAM, scsi_sense_stream_sbuf},
+ {SSD_DESC_BLOCK, scsi_sense_block_sbuf},
+ {SSD_DESC_PROGRESS, scsi_sense_progress_sbuf}
+};
+
+void
+scsi_sense_desc_sbuf(struct sbuf *sb, struct scsi_sense_data *sense,
+ u_int sense_len, uint8_t *cdb, int cdb_len,
+ struct scsi_inquiry_data *inq_data,
+ struct scsi_sense_desc_header *header)
+{
+ int i, found;
+
+ for (i = 0, found = 0; i < (sizeof(scsi_sense_printers) /
+ sizeof(scsi_sense_printers[0])); i++) {
+ struct scsi_sense_desc_printer *printer;
+
+ printer = &scsi_sense_printers[i];
+
+ /*
+ * The list is sorted, so quit if we've passed our
+ * descriptor number.
+ */
+ if (printer->desc_type > header->desc_type)
+ break;
+
+ if (printer->desc_type != header->desc_type)
+ continue;
+
+ printer->print_func(sb, sense, sense_len, cdb, cdb_len,
+ inq_data, header);
+
+ return;
+ }
+
+ /*
+ * No specific printing routine, so use the generic routine.
+ */
+ scsi_sense_generic_sbuf(sb, sense, sense_len, cdb, cdb_len,
+ inq_data, header);
+}
+
+scsi_sense_data_type
+scsi_sense_type(struct scsi_sense_data *sense_data)
+{
+ switch (sense_data->error_code & SSD_ERRCODE) {
+ case SSD_DESC_CURRENT_ERROR:
+ case SSD_DESC_DEFERRED_ERROR:
+ return (SSD_TYPE_DESC);
+ break;
+ case SSD_CURRENT_ERROR:
+ case SSD_DEFERRED_ERROR:
+ return (SSD_TYPE_FIXED);
+ break;
+ default:
+ break;
+ }
+
+ return (SSD_TYPE_NONE);
+}
+
+struct scsi_print_sense_info {
+ struct sbuf *sb;
+ char *path_str;
+ uint8_t *cdb;
+ int cdb_len;
+ struct scsi_inquiry_data *inq_data;
+};
+
+static int
+scsi_print_desc_func(struct scsi_sense_data_desc *sense, u_int sense_len,
+ struct scsi_sense_desc_header *header, void *arg)
+{
+ struct scsi_print_sense_info *print_info;
+
+ print_info = (struct scsi_print_sense_info *)arg;
+
+ switch (header->desc_type) {
+ case SSD_DESC_INFO:
+ case SSD_DESC_FRU:
+ case SSD_DESC_COMMAND:
+ case SSD_DESC_SKS:
+ case SSD_DESC_BLOCK:
+ case SSD_DESC_STREAM:
+ /*
+ * We have already printed these descriptors, if they are
+ * present.
+ */
+ break;
+ default: {
+ sbuf_printf(print_info->sb, "%s", print_info->path_str);
+ scsi_sense_desc_sbuf(print_info->sb,
+ (struct scsi_sense_data *)sense, sense_len,
+ print_info->cdb, print_info->cdb_len,
+ print_info->inq_data, header);
+ sbuf_printf(print_info->sb, "\n");
+ break;
+ }
+ }
+
+ /*
+ * Tell the iterator that we want to see more descriptors if they
+ * are present.
+ */
+ return (0);
+}
+
+void
+scsi_sense_only_sbuf(struct scsi_sense_data *sense, u_int sense_len,
+ struct sbuf *sb, char *path_str,
+ struct scsi_inquiry_data *inq_data, uint8_t *cdb,
+ int cdb_len)
+{
+ int error_code, sense_key, asc, ascq;
+
+ sbuf_cat(sb, path_str);
+
+ scsi_extract_sense_len(sense, sense_len, &error_code, &sense_key,
+ &asc, &ascq, /*show_errors*/ 1);
+
+ sbuf_printf(sb, "SCSI sense: ");
+ switch (error_code) {
+ case SSD_DEFERRED_ERROR:
+ case SSD_DESC_DEFERRED_ERROR:
+ sbuf_printf(sb, "Deferred error: ");
+
+ /* FALLTHROUGH */
+ case SSD_CURRENT_ERROR:
+ case SSD_DESC_CURRENT_ERROR:
+ {
+ struct scsi_sense_data_desc *desc_sense;
+ struct scsi_print_sense_info print_info;
+ const char *sense_key_desc;
+ const char *asc_desc;
+ uint8_t sks[3];
+ uint64_t val;
+ int info_valid;
+
+ /*
+ * Get descriptions for the sense key, ASC, and ASCQ. If
+ * these aren't present in the sense data (i.e. the sense
+ * data isn't long enough), the -1 values that
+ * scsi_extract_sense_len() returns will yield default
+ * or error descriptions.
+ */
+ scsi_sense_desc(sense_key, asc, ascq, inq_data,
+ &sense_key_desc, &asc_desc);
+
+ /*
+ * We first print the sense key and ASC/ASCQ.
+ */
+ sbuf_cat(sb, sense_key_desc);
+ sbuf_printf(sb, " asc:%x,%x (%s)\n", asc, ascq, asc_desc);
+
+ /*
+ * Get the info field if it is valid.
+ */
+ if (scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO,
+ &val, NULL) == 0)
+ info_valid = 1;
+ else
+ info_valid = 0;
+
+ if (info_valid != 0) {
+ uint8_t bits;
+
+ /*
+ * Determine whether we have any block or stream
+ * device-specific information.
+ */
+ if (scsi_get_block_info(sense, sense_len, inq_data,
+ &bits) == 0) {
+ sbuf_cat(sb, path_str);
+ scsi_block_sbuf(sb, bits, val);
+ sbuf_printf(sb, "\n");
+ } else if (scsi_get_stream_info(sense, sense_len,
+ inq_data, &bits) == 0) {
+ sbuf_cat(sb, path_str);
+ scsi_stream_sbuf(sb, bits, val);
+ sbuf_printf(sb, "\n");
+ } else if (val != 0) {
+ /*
+ * The information field can be valid but 0.
+ * If the block or stream bits aren't set,
+ * and this is 0, it isn't terribly useful
+ * to print it out.
+ */
+ sbuf_cat(sb, path_str);
+ scsi_info_sbuf(sb, cdb, cdb_len, inq_data, val);
+ sbuf_printf(sb, "\n");
+ }
+ }
+
+ /*
+ * Print the FRU.
+ */
+ if (scsi_get_sense_info(sense, sense_len, SSD_DESC_FRU,
+ &val, NULL) == 0) {
+ sbuf_cat(sb, path_str);
+ scsi_fru_sbuf(sb, val);
+ sbuf_printf(sb, "\n");
+ }
+
+ /*
+ * Print any command-specific information.
+ */
+ if (scsi_get_sense_info(sense, sense_len, SSD_DESC_COMMAND,
+ &val, NULL) == 0) {
+ sbuf_cat(sb, path_str);
+ scsi_command_sbuf(sb, cdb, cdb_len, inq_data, val);
+ sbuf_printf(sb, "\n");
+ }
+
+ /*
+ * Print out any sense-key-specific information.
+ */
+ if (scsi_get_sks(sense, sense_len, sks) == 0) {
+ sbuf_cat(sb, path_str);
+ scsi_sks_sbuf(sb, sense_key, sks);
+ sbuf_printf(sb, "\n");
+ }
+
+ /*
+ * If this is fixed sense, we're done. If we have
+ * descriptor sense, we might have more information
+ * available.
+ */
+ if (scsi_sense_type(sense) != SSD_TYPE_DESC)
+ break;
+
+ desc_sense = (struct scsi_sense_data_desc *)sense;
+
+ print_info.sb = sb;
+ print_info.path_str = path_str;
+ print_info.cdb = cdb;
+ print_info.cdb_len = cdb_len;
+ print_info.inq_data = inq_data;
+
+ /*
+ * Print any sense descriptors that we have not already printed.
+ */
+ scsi_desc_iterate(desc_sense, sense_len, scsi_print_desc_func,
+ &print_info);
+ break;
+
+ }
+ case -1:
+ /*
+ * scsi_extract_sense_len() sets values to -1 if the
+ * show_errors flag is set and they aren't present in the
+ * sense data. This means that sense_len is 0.
+ */
+ sbuf_printf(sb, "No sense data present\n");
+ break;
+ default: {
+ sbuf_printf(sb, "Error code 0x%x", error_code);
+ if (sense->error_code & SSD_ERRCODE_VALID) {
+ struct scsi_sense_data_fixed *fixed_sense;
+
+ fixed_sense = (struct scsi_sense_data_fixed *)sense;
+
+ if (SSD_FIXED_IS_PRESENT(fixed_sense, sense_len, info)){
+ uint32_t info;
+
+ info = scsi_4btoul(fixed_sense->info);
+
+ sbuf_printf(sb, " at block no. %d (decimal)",
+ info);
+ }
+ }
+ sbuf_printf(sb, "\n");
+ break;
+ }
+ }
+}
/*
* scsi_sense_sbuf() returns 0 for success and -1 for failure.
@@ -3059,11 +4419,8 @@ scsi_sense_sbuf(struct cam_device *device, struct ccb_scsiio *csio,
#ifdef _KERNEL
struct ccb_getdev *cgd;
#endif /* _KERNEL */
- u_int32_t info;
- int error_code;
- int sense_key;
- int asc, ascq;
char path_str[64];
+ uint8_t *cdb;
#ifndef _KERNEL
if (device == NULL)
@@ -3161,129 +4518,14 @@ scsi_sense_sbuf(struct cam_device *device, struct ccb_scsiio *csio,
sense = &csio->sense_data;
}
+ if (csio->ccb_h.flags & CAM_CDB_POINTER)
+ cdb = csio->cdb_io.cdb_ptr;
+ else
+ cdb = csio->cdb_io.cdb_bytes;
- sbuf_cat(sb, path_str);
-
- error_code = sense->error_code & SSD_ERRCODE;
- sense_key = sense->flags & SSD_KEY;
-
- sbuf_printf(sb, "SCSI sense: ");
- switch (error_code) {
- case SSD_DEFERRED_ERROR:
- sbuf_printf(sb, "Deferred error: ");
-
- /* FALLTHROUGH */
- case SSD_CURRENT_ERROR:
- {
- const char *sense_key_desc;
- const char *asc_desc;
-
- asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0;
- ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0;
- scsi_sense_desc(sense_key, asc, ascq, inq_data,
- &sense_key_desc, &asc_desc);
- sbuf_cat(sb, sense_key_desc);
-
- info = scsi_4btoul(sense->info);
-
- if (sense->error_code & SSD_ERRCODE_VALID) {
-
- switch (sense_key) {
- case SSD_KEY_NOT_READY:
- case SSD_KEY_ILLEGAL_REQUEST:
- case SSD_KEY_UNIT_ATTENTION:
- case SSD_KEY_DATA_PROTECT:
- break;
- case SSD_KEY_BLANK_CHECK:
- sbuf_printf(sb, " req sz: %d (decimal)", info);
- break;
- default:
- if (info) {
- if (sense->flags & SSD_ILI) {
- sbuf_printf(sb, " ILI (length "
- "mismatch): %d", info);
-
- } else {
- sbuf_printf(sb, " info:%x",
- info);
- }
- }
- }
- } else if (info) {
- sbuf_printf(sb, " info?:%x", info);
- }
-
- if (sense->extra_len >= 4) {
- if (bcmp(sense->cmd_spec_info, "\0\0\0\0", 4)) {
- sbuf_printf(sb, " csi:%x,%x,%x,%x",
- sense->cmd_spec_info[0],
- sense->cmd_spec_info[1],
- sense->cmd_spec_info[2],
- sense->cmd_spec_info[3]);
- }
- }
-
- sbuf_printf(sb, " asc:%x,%x (%s)", asc, ascq, asc_desc);
-
- if (sense->extra_len >= 7 && sense->fru) {
- sbuf_printf(sb, " field replaceable unit: %x",
- sense->fru);
- }
-
- if ((sense->extra_len >= 10)
- && (sense->sense_key_spec[0] & SSD_SCS_VALID) != 0) {
- switch(sense_key) {
- case SSD_KEY_ILLEGAL_REQUEST: {
- int bad_command;
- char tmpstr2[40];
-
- if (sense->sense_key_spec[0] & 0x40)
- bad_command = 1;
- else
- bad_command = 0;
-
- tmpstr2[0] = '\0';
-
- /* Bit pointer is valid */
- if (sense->sense_key_spec[0] & 0x08)
- snprintf(tmpstr2, sizeof(tmpstr2),
- "bit %d ",
- sense->sense_key_spec[0] & 0x7);
- sbuf_printf(sb, ": %s byte %d %sis invalid",
- bad_command ? "Command" : "Data",
- scsi_2btoul(
- &sense->sense_key_spec[1]),
- tmpstr2);
- break;
- }
- case SSD_KEY_RECOVERED_ERROR:
- case SSD_KEY_HARDWARE_ERROR:
- case SSD_KEY_MEDIUM_ERROR:
- sbuf_printf(sb, " actual retry count: %d",
- scsi_2btoul(
- &sense->sense_key_spec[1]));
- break;
- default:
- sbuf_printf(sb, " sks:%#x,%#x",
- sense->sense_key_spec[0],
- scsi_2btoul(
- &sense->sense_key_spec[1]));
- break;
- }
- }
- break;
-
- }
- default:
- sbuf_printf(sb, "Error code 0x%x", sense->error_code);
- if (sense->error_code & SSD_ERRCODE_VALID) {
- sbuf_printf(sb, " at block no. %d (decimal)",
- info = scsi_4btoul(sense->info));
- }
- }
-
- sbuf_printf(sb, "\n");
-
+ scsi_sense_only_sbuf(sense, csio->sense_len - csio->sense_resid, sb,
+ path_str, inq_data, cdb, csio->cdb_len);
+
#ifdef _KERNEL
xpt_free_ccb((union ccb*)cgd);
#endif /* _KERNEL/!_KERNEL */
@@ -3355,6 +4597,135 @@ scsi_sense_print(struct cam_device *device, struct ccb_scsiio *csio,
#endif /* _KERNEL/!_KERNEL */
/*
+ * Extract basic sense information. This is backward-compatible with the
+ * previous implementation. For new implementations,
+ * scsi_extract_sense_len() is recommended.
+ */
+void
+scsi_extract_sense(struct scsi_sense_data *sense_data, int *error_code,
+ int *sense_key, int *asc, int *ascq)
+{
+ scsi_extract_sense_len(sense_data, sizeof(*sense_data), error_code,
+ sense_key, asc, ascq, /*show_errors*/ 0);
+}
+
+/*
+ * Extract basic sense information. If show_errors is set, sense values
+ * will be set to -1 if they are not present.
+ */
+void
+scsi_extract_sense_len(struct scsi_sense_data *sense_data, u_int sense_len,
+ int *error_code, int *sense_key, int *asc, int *ascq,
+ int show_errors)
+{
+ /*
+ * If we have no length, we have no sense.
+ */
+ if (sense_len == 0) {
+ if (show_errors == 0) {
+ *error_code = 0;
+ *sense_key = 0;
+ *asc = 0;
+ *ascq = 0;
+ } else {
+ *error_code = -1;
+ *sense_key = -1;
+ *asc = -1;
+ *ascq = -1;
+ }
+ return;
+ }
+
+ *error_code = sense_data->error_code & SSD_ERRCODE;
+
+ switch (*error_code) {
+ case SSD_DESC_CURRENT_ERROR:
+ case SSD_DESC_DEFERRED_ERROR: {
+ struct scsi_sense_data_desc *sense;
+
+ sense = (struct scsi_sense_data_desc *)sense_data;
+
+ if (SSD_DESC_IS_PRESENT(sense, sense_len, sense_key))
+ *sense_key = sense->sense_key & SSD_KEY;
+ else
+ *sense_key = (show_errors) ? -1 : 0;
+
+ if (SSD_DESC_IS_PRESENT(sense, sense_len, add_sense_code))
+ *asc = sense->add_sense_code;
+ else
+ *asc = (show_errors) ? -1 : 0;
+
+ if (SSD_DESC_IS_PRESENT(sense, sense_len, add_sense_code_qual))
+ *ascq = sense->add_sense_code_qual;
+ else
+ *ascq = (show_errors) ? -1 : 0;
+ break;
+ }
+ case SSD_CURRENT_ERROR:
+ case SSD_DEFERRED_ERROR:
+ default: {
+ struct scsi_sense_data_fixed *sense;
+
+ sense = (struct scsi_sense_data_fixed *)sense_data;
+
+ if (SSD_FIXED_IS_PRESENT(sense, sense_len, flags))
+ *sense_key = sense->flags & SSD_KEY;
+ else
+ *sense_key = (show_errors) ? -1 : 0;
+
+ if ((SSD_FIXED_IS_PRESENT(sense, sense_len, add_sense_code))
+ && (SSD_FIXED_IS_FILLED(sense, add_sense_code)))
+ *asc = sense->add_sense_code;
+ else
+ *asc = (show_errors) ? -1 : 0;
+
+ if ((SSD_FIXED_IS_PRESENT(sense, sense_len,add_sense_code_qual))
+ && (SSD_FIXED_IS_FILLED(sense, add_sense_code_qual)))
+ *ascq = sense->add_sense_code_qual;
+ else
+ *ascq = (show_errors) ? -1 : 0;
+ break;
+ }
+ }
+}
+
+int
+scsi_get_sense_key(struct scsi_sense_data *sense_data, u_int sense_len,
+ int show_errors)
+{
+ int error_code, sense_key, asc, ascq;
+
+ scsi_extract_sense_len(sense_data, sense_len, &error_code,
+ &sense_key, &asc, &ascq, show_errors);
+
+ return (sense_key);
+}
+
+int
+scsi_get_asc(struct scsi_sense_data *sense_data, u_int sense_len,
+ int show_errors)
+{
+ int error_code, sense_key, asc, ascq;
+
+ scsi_extract_sense_len(sense_data, sense_len, &error_code,
+ &sense_key, &asc, &ascq, show_errors);
+
+ return (asc);
+}
+
+int
+scsi_get_ascq(struct scsi_sense_data *sense_data, u_int sense_len,
+ int show_errors)
+{
+ int error_code, sense_key, asc, ascq;
+
+ scsi_extract_sense_len(sense_data, sense_len, &error_code,
+ &sense_key, &asc, &ascq, show_errors);
+
+ return (ascq);
+}
+
+/*
* This function currently requires at least 36 bytes, or
* SHORT_INQUIRY_LENGTH, worth of data to function properly. If this
* function needs more or less data in the future, another length should be
OpenPOWER on IntegriCloud