diff options
Diffstat (limited to 'freebsd/sys/cam/scsi/scsi_all.c')
-rw-r--r-- | freebsd/sys/cam/scsi/scsi_all.c | 2466 |
1 files changed, 2292 insertions, 174 deletions
diff --git a/freebsd/sys/cam/scsi/scsi_all.c b/freebsd/sys/cam/scsi/scsi_all.c index 151ebb10..5b504010 100644 --- a/freebsd/sys/cam/scsi/scsi_all.c +++ b/freebsd/sys/cam/scsi/scsi_all.c @@ -33,6 +33,8 @@ __FBSDID("$FreeBSD$"); #include <rtems/bsd/sys/param.h> +#include <rtems/bsd/sys/types.h> +#include <sys/stdint.h> #ifdef _KERNEL #ifndef __rtems__ @@ -44,6 +46,9 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/libkern.h> #include <sys/kernel.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> #include <sys/sysctl.h> #else #include <errno.h> @@ -57,9 +62,17 @@ __FBSDID("$FreeBSD$"); #include <cam/cam_queue.h> #include <cam/cam_xpt.h> #include <cam/scsi/scsi_all.h> +#include <sys/ata.h> #include <sys/sbuf.h> -#ifndef _KERNEL + +#ifdef _KERNEL +#include <cam/cam_periph.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_xpt_periph.h> +#include <cam/cam_xpt_internal.h> +#else #include <camlib.h> +#include <stddef.h> #ifndef FALSE #define FALSE 0 @@ -368,6 +381,8 @@ static struct op_table_entry scsi_op_codes[] = { { 0x40, D | T | L | P | W | R | O | M | S | C, "CHANGE DEFINITION" }, /* 41 O WRITE SAME(10) */ { 0x41, D, "WRITE SAME(10)" }, + /* 42 O UNMAP */ + { 0x42, D, "UNMAP" }, /* 42 O READ SUB-CHANNEL */ { 0x42, R, "READ SUB-CHANNEL" }, /* 43 O READ TOC/PMA/ATIP */ @@ -615,14 +630,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; @@ -690,10 +715,7 @@ const struct sense_key_table_entry sense_key_table[] = { { SSD_KEY_NO_SENSE, SS_NOP, "NO SENSE" }, { SSD_KEY_RECOVERED_ERROR, SS_NOP|SSQ_PRINT_SENSE, "RECOVERED ERROR" }, - { - SSD_KEY_NOT_READY, SS_TUR|SSQ_MANY|SSQ_DECREMENT_COUNT|EBUSY, - "NOT READY" - }, + { SSD_KEY_NOT_READY, SS_RDEF, "NOT READY" }, { SSD_KEY_MEDIUM_ERROR, SS_RDEF, "MEDIUM ERROR" }, { SSD_KEY_HARDWARE_ERROR, SS_RDEF, "HARDWARE FAILURE" }, { SSD_KEY_ILLEGAL_REQUEST, SS_FATAL|EINVAL, "ILLEGAL REQUEST" }, @@ -706,7 +728,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 = @@ -722,6 +744,172 @@ static struct asc_table_entry sony_mo_entries[] = { "Logical unit not ready, cause not reportable") } }; +static struct asc_table_entry hgst_entries[] = { + { SST(0x04, 0xF0, SS_RDEF, + "Vendor Unique - Logical Unit Not Ready") }, + { SST(0x0A, 0x01, SS_RDEF, + "Unrecovered Super Certification Log Write Error") }, + { SST(0x0A, 0x02, SS_RDEF, + "Unrecovered Super Certification Log Read Error") }, + { SST(0x15, 0x03, SS_RDEF, + "Unrecovered Sector Error") }, + { SST(0x3E, 0x04, SS_RDEF, + "Unrecovered Self-Test Hard-Cache Test Fail") }, + { SST(0x3E, 0x05, SS_RDEF, + "Unrecovered Self-Test OTF-Cache Fail") }, + { SST(0x40, 0x00, SS_RDEF, + "Unrecovered SAT No Buffer Overflow Error") }, + { SST(0x40, 0x01, SS_RDEF, + "Unrecovered SAT Buffer Overflow Error") }, + { SST(0x40, 0x02, SS_RDEF, + "Unrecovered SAT No Buffer Overflow With ECS Fault") }, + { SST(0x40, 0x03, SS_RDEF, + "Unrecovered SAT Buffer Overflow With ECS Fault") }, + { SST(0x40, 0x81, SS_RDEF, + "DRAM Failure") }, + { SST(0x44, 0x0B, SS_RDEF, + "Vendor Unique - Internal Target Failure") }, + { SST(0x44, 0xF2, SS_RDEF, + "Vendor Unique - Internal Target Failure") }, + { SST(0x44, 0xF6, SS_RDEF, + "Vendor Unique - Internal Target Failure") }, + { SST(0x44, 0xF9, SS_RDEF, + "Vendor Unique - Internal Target Failure") }, + { SST(0x44, 0xFA, SS_RDEF, + "Vendor Unique - Internal Target Failure") }, + { SST(0x5D, 0x22, SS_RDEF, + "Extreme Over-Temperature Warning") }, + { SST(0x5D, 0x50, SS_RDEF, + "Load/Unload cycle Count Warning") }, + { SST(0x81, 0x00, SS_RDEF, + "Vendor Unique - Internal Logic Error") }, + { SST(0x85, 0x00, SS_RDEF, + "Vendor Unique - Internal Key Seed Error") }, +}; + +static struct asc_table_entry seagate_entries[] = { + { SST(0x04, 0xF0, SS_RDEF, + "Logical Unit Not Ready, super certify in Progress") }, + { SST(0x08, 0x86, SS_RDEF, + "Write Fault Data Corruption") }, + { SST(0x09, 0x0D, SS_RDEF, + "Tracking Failure") }, + { SST(0x09, 0x0E, SS_RDEF, + "ETF Failure") }, + { SST(0x0B, 0x5D, SS_RDEF, + "Pre-SMART Warning") }, + { SST(0x0B, 0x85, SS_RDEF, + "5V Voltage Warning") }, + { SST(0x0B, 0x8C, SS_RDEF, + "12V Voltage Warning") }, + { SST(0x0C, 0xFF, SS_RDEF, + "Write Error - Too many error recovery revs") }, + { SST(0x11, 0xFF, SS_RDEF, + "Unrecovered Read Error - Too many error recovery revs") }, + { SST(0x19, 0x0E, SS_RDEF, + "Fewer than 1/2 defect list copies") }, + { SST(0x20, 0xF3, SS_RDEF, + "Illegal CDB linked to skip mask cmd") }, + { SST(0x24, 0xF0, SS_RDEF, + "Illegal byte in CDB, LBA not matching") }, + { SST(0x24, 0xF1, SS_RDEF, + "Illegal byte in CDB, LEN not matching") }, + { SST(0x24, 0xF2, SS_RDEF, + "Mask not matching transfer length") }, + { SST(0x24, 0xF3, SS_RDEF, + "Drive formatted without plist") }, + { SST(0x26, 0x95, SS_RDEF, + "Invalid Field Parameter - CAP File") }, + { SST(0x26, 0x96, SS_RDEF, + "Invalid Field Parameter - RAP File") }, + { SST(0x26, 0x97, SS_RDEF, + "Invalid Field Parameter - TMS Firmware Tag") }, + { SST(0x26, 0x98, SS_RDEF, + "Invalid Field Parameter - Check Sum") }, + { SST(0x26, 0x99, SS_RDEF, + "Invalid Field Parameter - Firmware Tag") }, + { SST(0x29, 0x08, SS_RDEF, + "Write Log Dump data") }, + { SST(0x29, 0x09, SS_RDEF, + "Write Log Dump data") }, + { SST(0x29, 0x0A, SS_RDEF, + "Reserved disk space") }, + { SST(0x29, 0x0B, SS_RDEF, + "SDBP") }, + { SST(0x29, 0x0C, SS_RDEF, + "SDBP") }, + { SST(0x31, 0x91, SS_RDEF, + "Format Corrupted World Wide Name (WWN) is Invalid") }, + { SST(0x32, 0x03, SS_RDEF, + "Defect List - Length exceeds Command Allocated Length") }, + { SST(0x33, 0x00, SS_RDEF, + "Flash not ready for access") }, + { SST(0x3F, 0x70, SS_RDEF, + "Invalid RAP block") }, + { SST(0x3F, 0x71, SS_RDEF, + "RAP/ETF mismatch") }, + { SST(0x3F, 0x90, SS_RDEF, + "Invalid CAP block") }, + { SST(0x3F, 0x91, SS_RDEF, + "World Wide Name (WWN) Mismatch") }, + { SST(0x40, 0x01, SS_RDEF, + "DRAM Parity Error") }, + { SST(0x40, 0x02, SS_RDEF, + "DRAM Parity Error") }, + { SST(0x42, 0x0A, SS_RDEF, + "Loopback Test") }, + { SST(0x42, 0x0B, SS_RDEF, + "Loopback Test") }, + { SST(0x44, 0xF2, SS_RDEF, + "Compare error during data integrity check") }, + { SST(0x44, 0xF6, SS_RDEF, + "Unrecoverable error during data integrity check") }, + { SST(0x47, 0x80, SS_RDEF, + "Fibre Channel Sequence Error") }, + { SST(0x4E, 0x01, SS_RDEF, + "Information Unit Too Short") }, + { SST(0x80, 0x00, SS_RDEF, + "General Firmware Error / Command Timeout") }, + { SST(0x80, 0x01, SS_RDEF, + "Command Timeout") }, + { SST(0x80, 0x02, SS_RDEF, + "Command Timeout") }, + { SST(0x80, 0x80, SS_RDEF, + "FC FIFO Error During Read Transfer") }, + { SST(0x80, 0x81, SS_RDEF, + "FC FIFO Error During Write Transfer") }, + { SST(0x80, 0x82, SS_RDEF, + "DISC FIFO Error During Read Transfer") }, + { SST(0x80, 0x83, SS_RDEF, + "DISC FIFO Error During Write Transfer") }, + { SST(0x80, 0x84, SS_RDEF, + "LBA Seeded LRC Error on Read") }, + { SST(0x80, 0x85, SS_RDEF, + "LBA Seeded LRC Error on Write") }, + { SST(0x80, 0x86, SS_RDEF, + "IOEDC Error on Read") }, + { SST(0x80, 0x87, SS_RDEF, + "IOEDC Error on Write") }, + { SST(0x80, 0x88, SS_RDEF, + "Host Parity Check Failed") }, + { SST(0x80, 0x89, SS_RDEF, + "IOEDC error on read detected by formatter") }, + { SST(0x80, 0x8A, SS_RDEF, + "Host Parity Errors / Host FIFO Initialization Failed") }, + { SST(0x80, 0x8B, SS_RDEF, + "Host Parity Errors") }, + { SST(0x80, 0x8C, SS_RDEF, + "Host Parity Errors") }, + { SST(0x80, 0x8D, SS_RDEF, + "Host Parity Errors") }, + { SST(0x81, 0x00, SS_RDEF, + "LA Check Failed") }, + { SST(0x82, 0x00, SS_RDEF, + "Internal client detected insufficient buffer") }, + { SST(0x84, 0x00, SS_RDEF, + "Scheduled Diagnostic And Repair") }, +}; + static struct scsi_sense_quirk_entry sense_quirk_table[] = { { /* @@ -744,6 +932,26 @@ static struct scsi_sense_quirk_entry sense_quirk_table[] = { sizeof(sony_mo_entries)/sizeof(struct asc_table_entry), /*sense key entries*/NULL, sony_mo_entries + }, + { + /* + * HGST vendor-specific error codes + */ + {T_DIRECT, SIP_MEDIA_FIXED, "HGST", "*", "*"}, + /*num_sense_keys*/0, + sizeof(hgst_entries)/sizeof(struct asc_table_entry), + /*sense key entries*/NULL, + hgst_entries + }, + { + /* + * SEAGATE vendor-specific error codes + */ + {T_DIRECT, SIP_MEDIA_FIXED, "SEAGATE", "*", "*"}, + /*num_sense_keys*/0, + sizeof(seagate_entries)/sizeof(struct asc_table_entry), + /*sense key entries*/NULL, + seagate_entries } }; @@ -868,7 +1076,7 @@ static struct asc_table_entry asc_table[] = { { SST(0x03, 0x02, SS_RDEF, "Excessive write errors") }, /* DTLPWROMAEBKVF */ - { SST(0x04, 0x00, SS_TUR | SSQ_MANY | SSQ_DECREMENT_COUNT | EIO, + { SST(0x04, 0x00, SS_RDEF, "Logical unit not ready, cause not reportable") }, /* DTLPWROMAEBKVF */ { SST(0x04, 0x01, SS_TUR | SSQ_MANY | SSQ_DECREMENT_COUNT | EBUSY, @@ -1117,25 +1325,25 @@ static struct asc_table_entry asc_table[] = { { SST(0x10, 0x05, SS_RDEF, /* XXX TBD */ "Logical block protection method error") }, /* 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, @@ -1150,10 +1358,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, @@ -2968,7 +3176,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; @@ -2994,10 +3205,11 @@ 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); - - if (error_code == SSD_DEFERRED_ERROR) { + if (!scsi_extract_sense_ccb((union ccb *)csio, + &error_code, &sense_key, &asc, &ascq)) { + action = SS_RETRY | SSQ_DECREMENT_COUNT | SSQ_PRINT_SENSE | EIO; + } else 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 @@ -3035,8 +3247,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) { /* @@ -3058,10 +3272,15 @@ scsi_error_action(struct ccb_scsiio *csio, struct scsi_inquiry_data *inq_data, } } } -#ifdef _KERNEL - if (bootverbose) - sense_flags |= SF_PRINT_ALWAYS; -#endif + if ((action & SS_MASK) >= SS_START && + (sense_flags & SF_NO_RECOVERY)) { + action &= ~SS_MASK; + action |= SS_FAIL; + } else if ((action & SS_MASK) == SS_RETRY && + (sense_flags & SF_NO_RETRY)) { + action &= ~SS_MASK; + action |= SS_FAIL; + } if ((sense_flags & SF_PRINT_ALWAYS) != 0) action |= SSQ_PRINT_SENSE; else if ((sense_flags & SF_NO_PRINT) != 0) @@ -3120,7 +3339,7 @@ scsi_cdb_string(u_int8_t *cdb_ptr, char *cdb_string, size_t len) *cdb_string = '\0'; for (i = 0; i < cdb_len; i++) snprintf(cdb_string + strlen(cdb_string), - len - strlen(cdb_string), "%x ", cdb_ptr[i]); + len - strlen(cdb_string), "%02hhx ", cdb_ptr[i]); return(cdb_string); } @@ -3222,6 +3441,1348 @@ 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); +} +#endif /* __rtems__ */ + +/* + * 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); +} + +#ifndef __rtems__ +/* + * 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; + + for (i = 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. @@ -3241,11 +4802,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) @@ -3343,129 +4901,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 */ @@ -3535,6 +4978,167 @@ 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 from SCSI I/O CCB structure. + */ +int +scsi_extract_sense_ccb(union ccb *ccb, + int *error_code, int *sense_key, int *asc, int *ascq) +{ + struct scsi_sense_data *sense_data; + + /* Make sure there are some sense data we can access. */ + if (ccb->ccb_h.func_code != XPT_SCSI_IO || + (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_SCSI_STATUS_ERROR || + (ccb->csio.scsi_status != SCSI_STATUS_CHECK_COND) || + (ccb->ccb_h.status & CAM_AUTOSNS_VALID) == 0 || + (ccb->ccb_h.flags & CAM_SENSE_PHYS)) + return (0); + + if (ccb->ccb_h.flags & CAM_SENSE_PTR) + bcopy(&ccb->csio.sense_data, &sense_data, + sizeof(struct scsi_sense_data *)); + else + sense_data = &ccb->csio.sense_data; + scsi_extract_sense_len(sense_data, + ccb->csio.sense_len - ccb->csio.sense_resid, + error_code, sense_key, asc, ascq, 1); + if (*error_code == -1) + return (0); + return (1); +} +#endif /* __rtems__ */ + +/* + * 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); +} + +#ifndef __rtems__ +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); +} #endif /* __rtems__ */ /* @@ -3737,6 +5341,117 @@ scsi_calc_syncparam(u_int period) } #endif /* __rtems__ */ +int +scsi_devid_is_naa_ieee_reg(uint8_t *bufp) +{ + struct scsi_vpd_id_descriptor *descr; + struct scsi_vpd_id_naa_basic *naa; + + descr = (struct scsi_vpd_id_descriptor *)bufp; + naa = (struct scsi_vpd_id_naa_basic *)descr->identifier; + if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_NAA) + return 0; + if (descr->length < sizeof(struct scsi_vpd_id_naa_ieee_reg)) + return 0; + if ((naa->naa >> SVPD_ID_NAA_NAA_SHIFT) != SVPD_ID_NAA_IEEE_REG) + return 0; + return 1; +} + +int +scsi_devid_is_sas_target(uint8_t *bufp) +{ + struct scsi_vpd_id_descriptor *descr; + + descr = (struct scsi_vpd_id_descriptor *)bufp; + if (!scsi_devid_is_naa_ieee_reg(bufp)) + return 0; + if ((descr->id_type & SVPD_ID_PIV) == 0) /* proto field reserved */ + return 0; + if ((descr->proto_codeset >> SVPD_ID_PROTO_SHIFT) != SCSI_PROTO_SAS) + return 0; + return 1; +} + +int +scsi_devid_is_lun_eui64(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_LUN) + return 0; + if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_EUI64) + return 0; + return 1; +} + +int +scsi_devid_is_lun_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_LUN) + return 0; + if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_NAA) + return 0; + return 1; +} + +int +scsi_devid_is_lun_t10(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_LUN) + return 0; + if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_T10) + return 0; + return 1; +} + +int +scsi_devid_is_lun_name(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_LUN) + return 0; + if ((descr->id_type & SVPD_ID_TYPE_MASK) != SVPD_ID_TYPE_SCSI_NAME) + return 0; + return 1; +} + +struct scsi_vpd_id_descriptor * +scsi_get_devid(struct scsi_vpd_device_id *id, uint32_t page_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); + + 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 + + desc->length)) { + + if (ck_fn == NULL || ck_fn((uint8_t *)desc) != 0) + return (desc); + } + + return (NULL); +} + void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), @@ -3814,14 +5529,7 @@ scsi_inquiry(struct ccb_scsiio *csio, u_int32_t retries, scsi_cmd->byte2 |= SI_EVPD; scsi_cmd->page_code = page_code; } - /* - * A 'transfer units' count of 256 is coded as - * zero for all commands with a single byte count - * field. - */ - if (inq_len == 256) - inq_len = 0; - scsi_cmd->length = inq_len; + scsi_ulto2b(inq_len, scsi_cmd->length); } #ifndef __rtems__ @@ -4236,7 +5944,11 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { + int read; u_int8_t cdb_len; + + read = (readop & SCSI_RW_DIRMASK) == SCSI_RW_READ; + /* * Use the smallest possible command to perform the operation * as some legacy hardware does not support the 10 byte commands. @@ -4253,7 +5965,7 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, struct scsi_rw_6 *scsi_cmd; scsi_cmd = (struct scsi_rw_6 *)&csio->cdb_io.cdb_bytes; - scsi_cmd->opcode = readop ? READ_6 : WRITE_6; + scsi_cmd->opcode = read ? READ_6 : WRITE_6; scsi_ulto3b(lba, scsi_cmd->addr); scsi_cmd->length = block_count & 0xff; scsi_cmd->control = 0; @@ -4272,7 +5984,7 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, struct scsi_rw_10 *scsi_cmd; scsi_cmd = (struct scsi_rw_10 *)&csio->cdb_io.cdb_bytes; - scsi_cmd->opcode = readop ? READ_10 : WRITE_10; + scsi_cmd->opcode = read ? READ_10 : WRITE_10; scsi_cmd->byte2 = byte2; scsi_ulto4b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; @@ -4295,7 +6007,7 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, struct scsi_rw_12 *scsi_cmd; scsi_cmd = (struct scsi_rw_12 *)&csio->cdb_io.cdb_bytes; - scsi_cmd->opcode = readop ? READ_12 : WRITE_12; + scsi_cmd->opcode = read ? READ_12 : WRITE_12; scsi_cmd->byte2 = byte2; scsi_ulto4b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; @@ -4317,7 +6029,7 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, struct scsi_rw_16 *scsi_cmd; scsi_cmd = (struct scsi_rw_16 *)&csio->cdb_io.cdb_bytes; - scsi_cmd->opcode = readop ? READ_16 : WRITE_16; + scsi_cmd->opcode = read ? READ_16 : WRITE_16; scsi_cmd->byte2 = byte2; scsi_u64to8b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; @@ -4328,7 +6040,77 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, cam_fill_csio(csio, retries, cbfcnp, - /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT, + (read ? CAM_DIR_IN : CAM_DIR_OUT) | + ((readop & SCSI_RW_BIO) != 0 ? CAM_DATA_BIO : 0), + tag_action, + data_ptr, + dxfer_len, + sense_len, + cdb_len, + timeout); +} + +void +scsi_write_same(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, u_int8_t byte2, + int minimum_cmd_size, u_int64_t lba, u_int32_t block_count, + u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, + u_int32_t timeout) +{ + u_int8_t cdb_len; + if ((minimum_cmd_size < 16) && + ((block_count & 0xffff) == block_count) && + ((lba & 0xffffffff) == lba)) { + /* + * Need a 10 byte cdb. + */ + struct scsi_write_same_10 *scsi_cmd; + + scsi_cmd = (struct scsi_write_same_10 *)&csio->cdb_io.cdb_bytes; + scsi_cmd->opcode = WRITE_SAME_10; + scsi_cmd->byte2 = byte2; + scsi_ulto4b(lba, scsi_cmd->addr); + scsi_cmd->group = 0; + scsi_ulto2b(block_count, scsi_cmd->length); + scsi_cmd->control = 0; + cdb_len = sizeof(*scsi_cmd); + + CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, + ("10byte: %x%x%x%x:%x%x: %d\n", scsi_cmd->addr[0], + scsi_cmd->addr[1], scsi_cmd->addr[2], + scsi_cmd->addr[3], scsi_cmd->length[0], + scsi_cmd->length[1], dxfer_len)); + } else { + /* + * 16 byte CDB. We'll only get here if the LBA is larger + * than 2^32, or if the user asks for a 16 byte command. + */ + struct scsi_write_same_16 *scsi_cmd; + + scsi_cmd = (struct scsi_write_same_16 *)&csio->cdb_io.cdb_bytes; + scsi_cmd->opcode = WRITE_SAME_16; + scsi_cmd->byte2 = byte2; + scsi_u64to8b(lba, scsi_cmd->addr); + scsi_ulto4b(block_count, scsi_cmd->length); + scsi_cmd->group = 0; + scsi_cmd->control = 0; + cdb_len = sizeof(*scsi_cmd); + + CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, + ("16byte: %x%x%x%x%x%x%x%x:%x%x%x%x: %d\n", + scsi_cmd->addr[0], scsi_cmd->addr[1], + scsi_cmd->addr[2], scsi_cmd->addr[3], + scsi_cmd->addr[4], scsi_cmd->addr[5], + scsi_cmd->addr[6], scsi_cmd->addr[7], + scsi_cmd->length[0], scsi_cmd->length[1], + scsi_cmd->length[2], scsi_cmd->length[3], + dxfer_len)); + } + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_OUT, tag_action, data_ptr, dxfer_len, @@ -4338,6 +6120,261 @@ scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, } #ifndef __rtems__ +void +scsi_ata_identify(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, u_int8_t *data_ptr, + 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); +} + +void +scsi_ata_trim(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, u_int16_t block_count, + u_int8_t *data_ptr, u_int16_t dxfer_len, u_int8_t sense_len, + u_int32_t timeout) +{ + scsi_ata_pass_16(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_OUT, + tag_action, + /*protocol*/AP_EXTEND|AP_PROTO_DMA, + /*ata_flags*/AP_FLAG_TLEN_SECT_CNT|AP_FLAG_BYT_BLOK_BLOCKS, + /*features*/ATA_DSM_TRIM, + /*sector_count*/block_count, + /*lba*/0, + /*command*/ATA_DATA_SET_MANAGEMENT, + /*control*/0, + data_ptr, + dxfer_len, + sense_len, + timeout); +} + +void +scsi_ata_pass_16(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int32_t flags, u_int8_t tag_action, + u_int8_t protocol, u_int8_t ata_flags, u_int16_t features, + u_int16_t sector_count, uint64_t lba, u_int8_t command, + u_int8_t control, u_int8_t *data_ptr, u_int16_t dxfer_len, + u_int8_t sense_len, u_int32_t timeout) +{ + struct ata_pass_16 *ata_cmd; + + ata_cmd = (struct ata_pass_16 *)&csio->cdb_io.cdb_bytes; + ata_cmd->opcode = ATA_PASS_16; + ata_cmd->protocol = protocol; + ata_cmd->flags = ata_flags; + ata_cmd->features_ext = features >> 8; + ata_cmd->features = features; + ata_cmd->sector_count_ext = sector_count >> 8; + ata_cmd->sector_count = sector_count; + ata_cmd->lba_low = lba; + ata_cmd->lba_mid = lba >> 8; + ata_cmd->lba_high = lba >> 16; + ata_cmd->device = ATA_DEV_LBA; + if (protocol & AP_EXTEND) { + ata_cmd->lba_low_ext = lba >> 24; + ata_cmd->lba_mid_ext = lba >> 32; + ata_cmd->lba_high_ext = lba >> 40; + } else + ata_cmd->device |= (lba >> 24) & 0x0f; + ata_cmd->command = command; + ata_cmd->control = control; + + cam_fill_csio(csio, + retries, + cbfcnp, + flags, + tag_action, + data_ptr, + dxfer_len, + sense_len, + sizeof(*ata_cmd), + timeout); +} + +void +scsi_unmap(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, u_int8_t byte2, + u_int8_t *data_ptr, u_int16_t dxfer_len, u_int8_t sense_len, + u_int32_t timeout) +{ + struct scsi_unmap *scsi_cmd; + + scsi_cmd = (struct scsi_unmap *)&csio->cdb_io.cdb_bytes; + scsi_cmd->opcode = UNMAP; + scsi_cmd->byte2 = byte2; + scsi_ulto4b(0, scsi_cmd->reserved); + scsi_cmd->group = 0; + scsi_ulto2b(dxfer_len, scsi_cmd->length); + scsi_cmd->control = 0; + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_OUT, + tag_action, + data_ptr, + dxfer_len, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_receive_diagnostic_results(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb*), + uint8_t tag_action, int pcv, uint8_t page_code, + uint8_t *data_ptr, uint16_t allocation_length, + uint8_t sense_len, uint32_t timeout) +{ + struct scsi_receive_diag *scsi_cmd; + + scsi_cmd = (struct scsi_receive_diag *)&csio->cdb_io.cdb_bytes; + memset(scsi_cmd, 0, sizeof(*scsi_cmd)); + scsi_cmd->opcode = RECEIVE_DIAGNOSTIC; + if (pcv) { + scsi_cmd->byte2 |= SRD_PCV; + scsi_cmd->page_code = page_code; + } + scsi_ulto2b(allocation_length, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + data_ptr, + allocation_length, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_send_diagnostic(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, int unit_offline, int device_offline, + int self_test, int page_format, int self_test_code, + uint8_t *data_ptr, uint16_t param_list_length, + uint8_t sense_len, uint32_t timeout) +{ + struct scsi_send_diag *scsi_cmd; + + scsi_cmd = (struct scsi_send_diag *)&csio->cdb_io.cdb_bytes; + memset(scsi_cmd, 0, sizeof(*scsi_cmd)); + scsi_cmd->opcode = SEND_DIAGNOSTIC; + + /* + * The default self-test mode control and specific test + * control are mutually exclusive. + */ + if (self_test) + self_test_code = SSD_SELF_TEST_CODE_NONE; + + scsi_cmd->byte2 = ((self_test_code << SSD_SELF_TEST_CODE_SHIFT) + & SSD_SELF_TEST_CODE_MASK) + | (unit_offline ? SSD_UNITOFFL : 0) + | (device_offline ? SSD_DEVOFFL : 0) + | (self_test ? SSD_SELFTEST : 0) + | (page_format ? SSD_PF : 0); + scsi_ulto2b(param_list_length, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/param_list_length ? CAM_DIR_OUT : CAM_DIR_NONE, + tag_action, + data_ptr, + param_list_length, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_read_buffer(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb*), + uint8_t tag_action, int mode, + uint8_t buffer_id, u_int32_t offset, + uint8_t *data_ptr, uint32_t allocation_length, + uint8_t sense_len, uint32_t timeout) +{ + struct scsi_read_buffer *scsi_cmd; + + scsi_cmd = (struct scsi_read_buffer *)&csio->cdb_io.cdb_bytes; + memset(scsi_cmd, 0, sizeof(*scsi_cmd)); + scsi_cmd->opcode = READ_BUFFER; + scsi_cmd->byte2 = mode; + scsi_cmd->buffer_id = buffer_id; + scsi_ulto3b(offset, scsi_cmd->offset); + scsi_ulto3b(allocation_length, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + data_ptr, + allocation_length, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + +void +scsi_write_buffer(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, int mode, + uint8_t buffer_id, u_int32_t offset, + uint8_t *data_ptr, uint32_t param_list_length, + uint8_t sense_len, uint32_t timeout) +{ + struct scsi_write_buffer *scsi_cmd; + + scsi_cmd = (struct scsi_write_buffer *)&csio->cdb_io.cdb_bytes; + memset(scsi_cmd, 0, sizeof(*scsi_cmd)); + scsi_cmd->opcode = WRITE_BUFFER; + scsi_cmd->byte2 = mode; + scsi_cmd->buffer_id = buffer_id; + scsi_ulto3b(offset, scsi_cmd->offset); + scsi_ulto3b(param_list_length, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/param_list_length ? CAM_DIR_OUT : CAM_DIR_NONE, + tag_action, + data_ptr, + param_list_length, + sense_len, + sizeof(*scsi_cmd), + timeout); +} + void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), @@ -4370,7 +6407,6 @@ scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, sense_len, sizeof(*scsi_cmd), timeout); - } @@ -4428,7 +6464,89 @@ scsi_static_inquiry_match(caddr_t inqbuffer, caddr_t table_entry) return (-1); } +/** + * Compare two buffers of vpd device descriptors for a match. + * + * \param lhs Pointer to first buffer of descriptors to compare. + * \param lhs_len The length of the first buffer. + * \param rhs Pointer to second buffer of descriptors to compare. + * \param rhs_len The length of the second buffer. + * + * \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 + * a match. + */ +int +scsi_devid_match(uint8_t *lhs, size_t lhs_len, uint8_t *rhs, size_t rhs_len) +{ + struct scsi_vpd_id_descriptor *lhs_id; + struct scsi_vpd_id_descriptor *lhs_last; + struct scsi_vpd_id_descriptor *rhs_last; + uint8_t *lhs_end; + uint8_t *rhs_end; + + lhs_end = lhs + lhs_len; + rhs_end = rhs + rhs_len; + + /* + * rhs_last and lhs_last are the last posible position of a valid + * descriptor assuming it had a zero length identifier. We use + * these variables to insure we can safely dereference the length + * field in our loop termination tests. + */ + lhs_last = (struct scsi_vpd_id_descriptor *) + (lhs_end - __offsetof(struct scsi_vpd_id_descriptor, identifier)); + rhs_last = (struct scsi_vpd_id_descriptor *) + (rhs_end - __offsetof(struct scsi_vpd_id_descriptor, identifier)); + + lhs_id = (struct scsi_vpd_id_descriptor *)lhs; + while (lhs_id <= lhs_last + && (lhs_id->identifier + lhs_id->length) <= lhs_end) { + struct scsi_vpd_id_descriptor *rhs_id; + + rhs_id = (struct scsi_vpd_id_descriptor *)rhs; + while (rhs_id <= rhs_last + && (rhs_id->identifier + rhs_id->length) <= rhs_end) { + + if (rhs_id->length == lhs_id->length + && memcmp(rhs_id->identifier, lhs_id->identifier, + rhs_id->length) == 0) + return (0); + + rhs_id = (struct scsi_vpd_id_descriptor *) + (rhs_id->identifier + rhs_id->length); + } + lhs_id = (struct scsi_vpd_id_descriptor *) + (lhs_id->identifier + lhs_id->length); + } + return (-1); +} + #ifdef _KERNEL +int +scsi_vpd_supported_page(struct cam_periph *periph, uint8_t page_id) +{ + struct cam_ed *device; + struct scsi_vpd_supported_pages *vpds; + int i, num_pages; + + device = periph->path->device; + vpds = (struct scsi_vpd_supported_pages *)device->supported_vpds; + + if (vpds != NULL) { + num_pages = device->supported_vpds_len - + SVPD_SUPPORTED_PAGES_HDR_LEN; + for (i = 0; i < num_pages; i++) { + if (vpds->page_list[i] == page_id) + return (1); + } + } + + return (0); +} + static void init_scsi_delay(void) { |