diff options
Diffstat (limited to 'lib/sg_io_linux.c')
-rw-r--r-- | lib/sg_io_linux.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/lib/sg_io_linux.c b/lib/sg_io_linux.c new file mode 100644 index 00000000..968f5e7a --- /dev/null +++ b/lib/sg_io_linux.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 1999-2021 Douglas Gilbert. + * All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the BSD_LICENSE file. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef SG_LIB_LINUX + +#include "sg_io_linux.h" +#include "sg_pr2serr.h" + + +/* Version 1.13 20210831 */ + + +void +sg_print_masked_status(int masked_status) +{ + int scsi_status = (masked_status << 1) & 0x7e; + + sg_print_scsi_status(scsi_status); +} + +/* host_bytes: DID_* are Linux SCSI result (a 32 bit variable) bits 16:23 */ + +static const char * linux_host_bytes[] = { + "DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", + "DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR", + "DID_RESET", "DID_BAD_INTR", "DID_PASSTHROUGH", "DID_SOFT_ERROR", + "DID_IMM_RETRY", "DID_REQUEUE", "DID_TRANSPORT_DISRUPTED", + "DID_TRANSPORT_FAILFAST", "DID_TARGET_FAILURE", "DID_NEXUS_FAILURE", + "DID_ALLOC_FAILURE", "DID_MEDIUM_ERROR", "DID_TRANSPORT_MARGINAL", +}; + +void +sg_print_host_status(int host_status) +{ + pr2ws("Host_status=0x%02x ", host_status); + if ((host_status < 0) || + (host_status >= (int)SG_ARRAY_SIZE(linux_host_bytes))) + pr2ws("is invalid "); + else + pr2ws("[%s] ", linux_host_bytes[host_status]); +} + +/* DRIVER_* are Linux SCSI result (a 32 bit variable) bits 24:27 . + * These where made obsolete around lk 5.12.0 . Only DRIVER_SENSE [0x8] is + * defined in scsi/sg.h for backward comaptibility */ +static const char * linux_driver_bytes[] = { + "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", + "DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", + "DRIVER_SENSE", +}; + +#if 0 + +/* SUGGEST_* are Linux SCSI result (a 32 bit variable) bits 28:31 */ + +static const char * linux_driver_suggests[] = { + "SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", + "SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN", + "SUGGEST_SENSE", +}; +#endif + + +void +sg_print_driver_status(int driver_status) +{ + int driv; + const char * driv_cp = "invalid"; + + driv = driver_status & SG_LIB_DRIVER_MASK; + if (driv < (int)SG_ARRAY_SIZE(linux_driver_bytes)) + driv_cp = linux_driver_bytes[driv]; + pr2ws("Driver_status=0x%02x", driver_status); + pr2ws(" [%s] ", driv_cp); +} + +/* Returns 1 if no errors found and thus nothing printed; otherwise + * prints error/warning (prefix by 'leadin') to stderr (pr2ws) and + * returns 0. */ +int +sg_linux_sense_print(const char * leadin, int scsi_status, int host_status, + int driver_status, const uint8_t * sense_buffer, + int sb_len, bool raw_sinfo) +{ + bool done_leadin = false; + bool done_sense = false; + + scsi_status &= 0x7e; /*sanity */ + if ((0 == scsi_status) && (0 == host_status) && (0 == driver_status)) + return 1; /* No problems */ + if (0 != scsi_status) { + if (leadin) + pr2ws("%s: ", leadin); + done_leadin = true; + pr2ws("SCSI status: "); + sg_print_scsi_status(scsi_status); + pr2ws("\n"); + if (sense_buffer && ((scsi_status == SAM_STAT_CHECK_CONDITION) || + (scsi_status == SAM_STAT_COMMAND_TERMINATED))) { + /* SAM_STAT_COMMAND_TERMINATED is obsolete */ + sg_print_sense(0, sense_buffer, sb_len, raw_sinfo); + done_sense = true; + } + } + if (0 != host_status) { + if (leadin && (! done_leadin)) + pr2ws("%s: ", leadin); + if (done_leadin) + pr2ws("plus...: "); + else + done_leadin = true; + sg_print_host_status(host_status); + pr2ws("\n"); + } + if (0 != driver_status) { + if (done_sense && + (SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status))) + return 0; + if (leadin && (! done_leadin)) + pr2ws("%s: ", leadin); + if (done_leadin) + pr2ws("plus...: "); + sg_print_driver_status(driver_status); + pr2ws("\n"); + if (sense_buffer && (! done_sense) && + (SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status))) + sg_print_sense(0, sense_buffer, sb_len, raw_sinfo); + } + return 0; +} + +#ifdef SG_IO + +bool +sg_normalize_sense(const struct sg_io_hdr * hp, + struct sg_scsi_sense_hdr * sshp) +{ + if ((NULL == hp) || (0 == hp->sb_len_wr)) { + if (sshp) + memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr)); + return 0; + } + return sg_scsi_normalize_sense(hp->sbp, hp->sb_len_wr, sshp); +} + +/* Returns 1 if no errors found and thus nothing printed; otherwise + returns 0. */ +int +sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp, + bool raw_sinfo) +{ + return sg_linux_sense_print(leadin, hp->status, hp->host_status, + hp->driver_status, hp->sbp, hp->sb_len_wr, + raw_sinfo); +} +#endif + +/* Returns 1 if no errors found and thus nothing printed; otherwise + returns 0. */ +int +sg_chk_n_print(const char * leadin, int masked_status, int host_status, + int driver_status, const uint8_t * sense_buffer, + int sb_len, bool raw_sinfo) +{ + int scsi_status = (masked_status << 1) & 0x7e; + + return sg_linux_sense_print(leadin, scsi_status, host_status, + driver_status, sense_buffer, sb_len, + raw_sinfo); +} + +#ifdef SG_IO +int +sg_err_category3(struct sg_io_hdr * hp) +{ + return sg_err_category_new(hp->status, hp->host_status, + hp->driver_status, hp->sbp, hp->sb_len_wr); +} +#endif + +int +sg_err_category(int masked_status, int host_status, int driver_status, + const uint8_t * sense_buffer, int sb_len) +{ + int scsi_status = (masked_status << 1) & 0x7e; + + return sg_err_category_new(scsi_status, host_status, driver_status, + sense_buffer, sb_len); +} + +int +sg_err_category_new(int scsi_status, int host_status, int driver_status, + const uint8_t * sense_buffer, int sb_len) +{ + int masked_driver_status = (SG_LIB_DRIVER_MASK & driver_status); + + scsi_status &= 0x7e; + if ((0 == scsi_status) && (0 == host_status) && + (0 == masked_driver_status)) + return SG_LIB_CAT_CLEAN; + if ((SAM_STAT_CHECK_CONDITION == scsi_status) || + (SAM_STAT_COMMAND_TERMINATED == scsi_status) || + (SG_LIB_DRIVER_SENSE == masked_driver_status)) + return sg_err_category_sense(sense_buffer, sb_len); + if (0 != host_status) { + if ((SG_LIB_DID_NO_CONNECT == host_status) || + (SG_LIB_DID_BUS_BUSY == host_status) || + (SG_LIB_DID_TIME_OUT == host_status)) + return SG_LIB_CAT_TIMEOUT; + if (SG_LIB_DID_NEXUS_FAILURE == host_status) + return SG_LIB_CAT_RES_CONFLICT; + } + if (SG_LIB_DRIVER_TIMEOUT == masked_driver_status) + return SG_LIB_CAT_TIMEOUT; + return SG_LIB_CAT_OTHER; +} + +#endif /* if SG_LIB_LINUX defined */ |